This is a discussion on Object-orientated programming with C++ within the Programming forums, part of the Tutorials category; Object-orientated programming Object-orientated languages support to a greater or lesser extent the following concepts Encapsulation (including in an object everything ...
Object-orientated programming
Object-orientated languages support to a greater or lesser extent the following concepts
Encapsulation (including in an object everything it needs, hiding elements
that other objects needn't know about). This keeps data and related routines
together and unclutters the large-scale organisation of the program. Each
object has a 'public' set of routines that can be called, and these routines are
all that other objects need to know.
Inheritance (creating new types of objects from existing ones). Rather than
having many seemingly unrelated objects, objects can be organised hierarchically,
inheriting behaviour. Again, this simpli_es the large-scale organisation.
Polymorphism (di_erent objects responding to the same message in di_erent
ways). Rather than having a di_erent routine to do the same thing to each of
many di_erent types of objects, a single routine does the job. An example of
this is how the + operator can be overloaded in C++ so that it can be used
with new classes.
Using an Object-orientated language often means that program entities can more closely model real-world entities. As Stroustrup wrote, \For small to medium projects there often is no distinction made between analysis and design: These two phases have been merged into one.
Similarly, in small projects there often is no distinction made between design
and programming."
complexity is more localised
code re-use is easier
Templates
A class template is a \type generator": a way to fabricate many similar classes or
functions from one piece of code. This is how the Standard Library can support
many types of vector, etc. C++'s Templates are parameterized types. They
support generic programming by obviating the need for explicit switch statements
to deal with various data types. Let's look at an example. The following code
(which doesn't use templates) swaps 2 integers in the usual C++ way
void swap (int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
If you wanted to swap other types of variables (including those you've de_ned)
you could copy this code, replacing int by (say) float. But in situations like this,
where the code is potentially generic, templates can be used.
template <class T>
void swap (T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
Here the \T" name is arbitrary (like a formal parameter in a function de_nition)
and the class keyword can be replaced by the newer typename keyword. Note that
the function is written much as before. When the compiler is now given the following code
int a = 3, b = 5;
float f=7.5, g=9.5;
swap (a, b);
swap (f, g);
it will create a version (\instantiation") of swap for each type required. These
versions have nothing in common except that they were created from the same
template - they won't share members, not even static ones. Templates are an
example of source code re-use not object code re-use.
You can have template functions, but also template classes and derived classes.
Pointers
Even if you don't use pointers yourself, the code you'll learn from is likely to have
them. Suppose i is an integer. To _nd the address of i the & operator is used
(&i). Setting a pointer to this value lets you refer indirectly to the variable i. If
you have the address of a variable and want to _nd the variable's value, then the
dereferencing operator * is used.
...
int i=0;
cout << i << " is at memory location " << &i << endl;
// The next statement declares i_ptr to be a pointer pointing
// to an integer. The declaration says that if i_ptr is
// dereferenced, one gets an int.
int *i_ptr;
i_ptr = &i; // initialise i_ptr to point to i
// The following 2 lines each set i to 5
i = 5;
*iptr = 5; // i.e. set to 5 the int that iptr points to
Pointers aren't just memory addresses; they have types. A pointer-to-an-int is
of type int*, a di_erent type to a pointer-to-a-char (which is char*). The di_erence
matters especially when the pointer is being incremented; the value of the pointer
is increased by the size of the object it points to. So if we added
iptr=iptr+1;
in the above example, then iptr wouldn't be incremented by 1 (which would
make it point somewhere in the middle of i) but by the length of an int, so that
it would point to the memory location just beyond i. This is useful if i is part of
an array. In the following fragment, the pointer steps through an array.
...
int numbers[10];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
int *iptr = &numbers[0]; // Point iptr to the first element in numbers[].
// A more common shorthand is iptr = numbers;
// now increment iptr to point to successive elements
for (int i=0; i<3; i++){
cout << "*iptr is " << *iptr << endl;
iptr= iptr+1;
}
...
Pointers are especially useful when functions operate on structures. Using a
pointer avoids copies of potentially big structures being made.
class {
public:
int age;
int height;
string surname;
} person;
person fred, jane;
int sum_of_ages(person *person1, person *person2)
{
int sum; // a variable local to this function
// Dereference the pointers, then use the `.' operator to get the
// fields
sum = (*person1).age + (*person2).age;
return sum;
}
Operations like (*person1).age are so common that there's a special, more
natural notation for it: person1->age.
Functions
The form of a function de_nition is
<function return type> <function name> ( <formal argument list> )
f
<local variables>
<body>
g
E.g.
int mean(int x, int y)
{
Int tmp;
tmp = (x + y)/2;
return tmp;
}
or, if the function is a member of class stats, ::, the scope resolution operator
can be used to specify which particular function of that name to access.
int stats::mean(int x, int y)
{
int tmp;
tmp = (x + y)/2;
return tmp;
}
You can provide default values for the trailing arguments. E.g.
int mean(int x, int y=5)
{
int tmp;
tmp = (x + y)/2;
return tmp;
}
...
int foo=mean(7)
is legal, setting foo to 6.
You can have more than function of a particular name provided that the compiler
knows unambiguously which one is required for a function call, so the functions with
identical names have to take di_erent arguments or must only be visible one at a
time. It's not enough for functions to di_er only in the return type.
By default, the variables given to a function won't have their values changed
when the function returns. In the following fragment number won't be increased to
6.
void add (int x)
{
x = x+1;
}
int number = 5;
add(number);
When add(number) is called, the x variable is set to the current value of number,
then incremented, but number is unchanged. The following slight variation that uses
\call by reference" will increase number
void add(int& x)
{
x = x+1;
}
int number = 5;
add(number);
Bookmarks