LevSelector.com |
C++ Tutorial #1 | Other pages | ||
01 • Introduction
02 • Data Types 03 • Enumerations 04 • Type Conversions 05 • Structures 06 • Operators 07 • Control Structures 08 • Class Declarations 09 • Function Declarations 10 • Class Constructors |
11 • Function
Overloading
12 • Default Arguments 13 • Inline Functions 14 • Friend Functions 15 • Arrays 16 • Pointers 17 • Strings 18 • Reference Types 19 • Memory Management 20 • Operator Overloading |
21 • Inheritance
22 • Multiple Inheritance 23 • Virtual Base Classes 24 • Virtual Functions 25 • Constants 26 • Static Keyword 27 • Input/Output 28 • Built in Functions |
• C
• C++ • C++ tutorial #2 |
01.Introduction | home - top of the page - |
C++ is probably the most widely used language of them all, and still probably offers more job opportunities than any other language. All sorts of things are programmed in C++ because it is highly adaptable, allowing the programmer greater low level access to the computer. It is also Object Orientated which makes it good for writing large scale, dependable programs. Visual Basic is probably the biggest contender to C++, although some people are predicting Java will become the most dominant Object Orientated language. Even if that happened though, there would still be thousands of C++ programs out there that need maintaining.
Anyone who can program in C will have no trouble learning C++. In fact
most C++ compilers will compile C code. Even if you come from something
like Pascal you won't have too much trouble. C++ has the same basic structures
and concepts. If you come from VB you will probably become annoyed with
C++ fairly quickly. C++ allows the programmer far greater low level access
to the computer than VB, but this causes as many problems as it solves.
02.Data types | home - top of the page - |
char : char c ='A'; (some implementations allow this to be unsigned)
short: [unsigned] short s = 100; (seldom used - often the same
as Integer)
integer: [unsigned] int i = 100; or i = 100U to signify
unsigned (roughly -32000 >> 32000)
long: [unsigned] long l = 2000L or l = 2000UL to signify
unsigned long
There are three floating point types - these are all unsigned:
float: float f = 3.168F (avoid using float unless memory conservation
is necessary)
double : double d = 5.7423 (use this floating type in most instances)
long double: long double ld = 2.6534L (use this when extremely
high accuracy is needed)
All these types can also be made constants.
You can declare variables anywhere in a program, not just at the start
of a block like in C.
All these types can also be made static so they
have a lifetime as long as the program runs. If they are not static they
evaporate when their block goes out of scope.
You can also make your own data types with typedef:
typedef char * string;
string s = "Dane";
03.Enumerations | home - top of the page - |
Enumerations are also possible whereby integer values can be exchanges
for more meaningful values:
enum colour {orange, gray, red};
in this example orange would take the value 0, gray 1 etc. It is also
possible to assign these integer values:
enum colour {orange = 5, gray = 2, red = 9};
These can then be used as follows:
colour c = red;
04.Type Conversions | home - top of the page - |
There are two types of conversion - the type you
program, and the type the computer does automatically.
Computers doing things automatically is good, but it is useful to know
what these conversions are:
When a value of type char or short appears in an expression
such as addition they are converted to type int.
The other main time the computer converts values is when you use expressions of two or more different types, such as adding an int to a long. Basically, if a smaller type arrears in an expression with a bigger type, the smaller type is converted to the bigger type. So, an int would be converted to a long.
Some conversions need to be done explicitly. This is quite easy, for
example:
(long) i -
given
an int i, we change it to a long
Be careful though, the compiler will let you cast a number into a value
it is too big for (which will corrupt the value):
(int) 80994388;
05.Structures | home - top of the page - |
Structures are used to group together several variables which can be of different datatypes, here is an example:
struct date {
int day;
int month;
int year;
}; // remember the brackets and
final semicolon.
To use this structure you declare variables of date:
date today, yesterday;
And then use it like this:
today.day = 22;
I have always preferred to use pointers with
structures:
date *d = new date;
date->day = 22;
There is also a Union type in C++, but you shouldn't use it
because it does bad things, and only super geeks care about saving a few
bytes.
06.Operators | home - top of the page - |
The following is a list of some of the most common operators in C++:
!= == < > >= <= : not equal to equal to lesser than etc.
+ - / % * :addition subtraction division remainder (for integer division) multiplication
&& ! || : and not or
make a special point not to confuse = and ==. The later is a relational
operator, as in something is equal to something else.
07.Control Structures | home - top of the page - |
The following are the main control structures in C++:
while(condition){
statements;
}
do{
statements;
} while (condition)
Unlike the while statement, this do..while loop will always execute at least once. It is seldom used in programming.
for(int i = 0; i < 10; i++) {
statements;
}
The i = 0 represents the starting value of i, the i < 10 is the ending value of i, and the i++ is the amount i is incremented with each run through the loop. Remember not to change the value of i in the statements section.
You can use the break keyword to get out of any of these loops prematurely.
if (expression) { statements; }
if (condition) { statements; } else { statements; }
if (expression1)
statement1;
else if (expression2)
statement2;
else
statement3;
switch(input) {
case value1: statements;
break;
case value2: statements;
break;
default: statements;
break;
}
Remember to include the break after each section or you are screwed.
08.Class Declarations | home - top of the page - |
Classes can either be declared with class or struct, but there is no real point of not using the class structure. The first line of a class structure for the hypothetical class person is as follows:
class person {
char * name;
int age;
public:
char* getname();
void setage(int num);
};
Note: instance variables are declared - they are private by default, although they can be declared by public (though any non-lazy programmer will advise against this). Next comes the function declarations. More is said on these here, but it is important to note that they are proceeded by the keyword public:, by default they would be private. This allows them to be accessed from outside the class.
The member functions can then be constructed along the following lines:
void person : : setage(int num) { age = num;
}
More is said on declaring and using functions
in later sections.
Remember to use this class from another class you will need to include
"person.h" at the top of that other class.
We declare instances of this class as follows:
person p1;
and then use call its functions as follows:
p1.setage(18);
09.Functions Declarations | home - top of the page - |
In C++ functions can either be member functions of a class, or, as in C, on there own. I will first explain functions on their own, though most of the same rules apply when it comes to using functions declared in a class.
The following is a basic function that accepts an integer (num), squares
it, and returns the result as an integer:
integer square(int num) {
num
= num * num;
return
num;
}
On the first line, the first int signals the return type. If there is no return value use the void type.
The word square is the function name. The brackets indicate the argument list, in this case there is one argument, num, which is an int. All arguments (except arrays) are passed by value, so the arguments passed are changed in the called function, but retain their original values in the calling function. To find out how to pass variables by reference (so the original variable is changed by the called function) see the pointers section.
The keyword return is used to to return the value.
Calling this function is easy. Presuming we have the integer variable
'answer' that we want to store the result in, and the integer 'x' that
you want to square, we call it as follows:
answer = square(x);
if the function didn't return a value we would call it like this:
square(x);
Declaring classes in functions is just as easy. Remember that first
you must declare them in the header section of the class definition. See
here
to learn how to do that. Given a class called person with
the following public function declarations in the header:
char* getname();
void setage(int num);
we can write the functions as follows:
char* person : : getname() { return name; }
where name is a private instance variable.
void person : : setage(int num) { age = age +
num };
once again, age is a private variable, so we access it
through this public function.
Given an instance or person called p1, we access these
functions as follows:
p1.setage(18);
char* s = p1.getname;
There isn't much to using basic functions like these. Many of the next
few chapters also deal with member functions, and are useful for demonstrating
what functions can do.
10.Class Constructors | home - top of the page - |
One of the most important types of function a class will have is a constructor. These are used primarily to initialize the instance variables of a class, but they can basically do anything you want them to do. They are called once, whenever a new instance of a class is created.
The name of the function constructor is always the same as the name
of the class. Like regular member functions they
must be included in the header section. Given the person class used above,
we could declare the constructor as follows:
person(int a, char * s);
The argument list contain the variables we will use to initialize the
class instance variables.
We then make the function as follows:
person : : person(int a, char* s) { age
= a; name = s: }
A function can have several constructors, this is called
function overloading. It can also have default functions,
this is also covered in its own section.
Just as constructors are called when an instance of a class is created, a destructor is called just before it is destroyed (either by going out of scope, or with the delete keyword).
A destructor for person would be declared as follows:
~person();
it is then created as follows:
person : : ~person() { statements; }
Destructors are primarily used to destroy variables created with the
new
keyword.
11.Function Overloading | home - top of the page - |
Function overloading is the situation whereby the same function name
has several definitions. Any function can be overloaded except destructors
which don't accept arguments, but I will demonstrate on the person constructor
defined above. It is currently defined as this:
person(int a, char * s);
so when we create an instance we pass two values:
person p1(18, "Dane");
But we may not know both the values when we create the class, we may
just know the age value. We can therefore declare another constructor like
this:
person(int a);
and define it like this:
person : : person(int a) { age = a; }
We now have two constructors for person. The program decides which
one to call by looking at what arguments we pass, for instance if we declared
an instance of the object with:
person p2(20);
the second constructor would be called, because it matches the argument
list of the second constructor.
We can overload the function as much as we want, we might declare another
constructor that accepts no arguments:
person();
and define it:
person : : person() { }
It is important to remember that return values play no role here, only
the
argument list does. Two functions with the same name cannot differ
only in their return value, because if they did, the program wouldn't not
know which function to call.
Many of the benefits of function overloading can be obtained with default
arguments.
Operators can also be overloaded in C++.
12.Default Arguments | home - top of the page - |
Default arguments are good because they save time. They give the benefits
of overloading without the trouble of creating separate functions for each
possible argument list. For instance, for the person class, we could
declare
the constructor like this:
person(int num = 20, char * s = "Dane");
We can then define this function as below (notice the default
arguments aren't included in the definition):
person : : person(int num,
char * s) {
age
= num;
name
= s;
}
The defaults are used when no corresponding parameters are passed. If
a new class was created like:
person p1(25);
age would take the value 25, but name would get the default
value.
Default values do not have to be supplied for all the arguments:
person(int num, char * s = "Dane");
It is important to remember though, that you can't omit an argument
in the argument list without also omitting all those to the right of it,
otherwise the meaning would become ambiguous. This argument list is invalid:
void triple(int a = 4, int b, int c = 9);
13.Inline Functions | home - top of the page - |
Inline Functions are used for simple functions to increase the speed programs run at. There is a very good explanation of why inline functions increase execution speed, but it is boring, and you don't care anyway.
There is two ways to 'request' that a function be made inline (there is no guarantee the compiler will grant the request).
One way is to precede the function definition (not declaration) with
the word inline.
inline void person : : setage(int num) {age +=
num; }
The other way involves not giving the class a separate definition, but
rather including that in the declaration:
void setage(int num) {age += num;};
This makes things much easier, the setage function is declared
and defined in one go.
14.Friend Functions | home - top of the page - |
A function can be declared a friend of several classes. This allows
us to use the same single function to access and modify the private variables
of several classes. A function cannot be a member of more than one
class, but it can be friends.
15.Arrays | home - top of the page - |
Arrays are easy to declare and use in C++. This declares an array of
5 integers called arr:
int arr[5];
Remember that arrays start at 0, so to give the first element the value
10 we write:
arr[0] = 10;
You can also give an array values when you declare it:
int arr[5] = {10, 20, 30, 40, 50};
You don't even need to supply all the values:
int arr[5] = {10, 20};
static arrays are initialized to 0, but arrays
like those above contain garbage until initialized.
In C++ the assignment of a whole array to another is illegal, ie. given
two arrays with 10 integers each, we can't use:
arr1 = arr2;
Instead you have to copy from arr1 to arr2 one element at a time:
for(int i = 0, i < 10, i++) { arr1[i] = arr2[i];
}
We can have an array of class instances:
person parray[3] = {person(10, "Jerry"), person(30,
"George"), person(24, "Elaine") };
If you have multiple constructors, naturally each element can call
a different constructor.
Remember, when you pass an array to a function it is passed by reference - that is, a copy is not passed, a pointer to the first element in the array is passed.
Arrays are closely connected to pointers. Anytime
you need to use the name of an array to do something, you could usually
also use the name of a pointer. When you declare an array like:
int a [100];
a is really a pointer to the first element in the array a[0],
that is, a = &a[0]
If we have another declaration like:
int * p;
we can declare:
p = a;
This allows for pointer arithmetic. If *p points to a[0]
then *(p + 2) makes p point to a[2]. You can also use subtraction
to move in the other direction, providing you are subtracting to a position
that actually exists. With pointer arithmetic C++ takes into account the
size of the data type.
You can also use the ++ and -- operators:
*p++
moves forward one element in the array.
If p and q are both pointed to the first element in their respective
arrays, and both contain 10 elements, this will copy the elements of p
to q:
for(int i = 0; i < 10, i++) *q++ = *p++;
16.Pointers | home - top of the page - |
Pointers are one of the most useful features of the C++ language. For more information on them look in arrays, memory management, reference types, constants, structures, virtual functions and strings in particular. This list alone tells what an important part pointers play in the language.
We declare a pointer like this:
int * p;
P can then hold an address of an area in memory:
int i = 10;
p = &i; // p
equals the address of i.
We can then printout the value like this:
cout << *p;
Or change the value of the integer p points to (ie. int i):
*p = 30; : the thing p points to equals
30.
Of course, you can create an area of memory for the value you want
p to point to:
p = new int;
*p = 100;
One of the most common things pointers are used for is sending arguments
to functions by reference rather than value. Except
for
arrays, when we pass a variable to a function
we pass a copy of the variable, not the actual variable itself. This means
that the variable outside the function isn't effected by any new values
given to the variable inside the function. This is what we want a lot of
the time, but sometimes we want a variable to be actually changed by a
function.
Pointers allow us to do this because instead of sending a copy of the variable, we can instead send the memory address of the variable. The function then uses this memory address, and can alter the value stored there. This is how it can be implemented:
Suppose we have a function called change, this function accepts
two integers and adds a number to them. Not only are the numbers changed
inside the function, they are changed outside it as well. This is the function:
void change( int * num1, int * num2) {
*num1 += 10;
*num2 += 10; };
And this is how we call it:
int a = 20;
int b = 20;
change(&a, &b);
Note that when we call the function we send over two memory addresses
(symbolized by &). In the function these then become pointers
to the area in memory. Not only are num1 and num2 changed to 30, a and
b are also changed to 30.
17.Strings | home - top of the page - |
I have always found working with strings to be frustrating in C and
C++. Of course there is no string data type, strings are just arrays
of characters. They often seem to leave hard to find bugs in your programs.
A string is declared and initialized like this:
char s[5] = "Dane";
Remember, this is a five character string, because s[4] contains
the '\0' character which signified the end of a string. Don't
forget this or you are in big trouble C++ will willingly let you forget
the '\0' character and make you pay for it at a later date.
You can just leave it to the computer to work out how long the array
is:
char s[] = Dane";
This can then be printed out like this:
cout << s;
or we could have just used:
cout << "Dane";
We could also use pointers:
char * s = "Dane";
cout << s;
A string is always a pointer anyway.
It is an
array, so its name is a pointer to the
first element in the array.
There are quite a few functions that are used on strings. Most are in
string.h,
so remember to declare this at the top of classes that use these functions.
strcpy is used to copy one string into another:
char * s[5];
strcpy(s, "Dane");
You find the length of a string with strlen:
int l = strlen(s);
You compare two strings with strcmp:
int cmp = strcmp(str1, str2);
It returns 0 if they are equal, a positive number if the first string
is before the second in the alphabet, or a negative number if the second
is first.
strcat joins two strings together and returns
a string:
char * ret = strcat(str1, str2);
18.Reference Types | home - top of the page - |
Reference types - work like aliases (shortcuts in Windows terminology).
They aren't really a necessary part of the language, they are used to make
it easier to send elements to a function by reference rather than value.
This is how they work.
Suppose we have an integer variable:
int k = 20;
we can declare a reference type that references it:
int& r = k;
r is now an alias for k. If we change the value of the one, we change
the value of the other because they both reference the same area of memory.
We can also make a reference type that doesn't refer to another variable:
int&w = 20;
This creates an integer object and gives it the value of 20.
References are then usually used to pass arguments by reference. If
there is a function that takes two references:
void two(int& a, int&b);
we can call it with:
two(w, r);
because w and r are reference types. This just makes
things a bit simpler when sending values by
reference.
19.Memory Management | home - top of the page - |
Memory management is very easy is C++, the only keywords you need to
remember are new and delete.
The new operator is used to create objects of any data type.
These objects then stay in existence until they are explicitly destroyed
with the delete operator.
Suppose we had declared a pointer to an integer:
int * i;
This can either point to an existing integer, or we can create an area
of memory for it to store its own integer:
i = new int;
We can then give this area a value:
*i = 200;
We can also create instances of classes like this:
person * p1;
p1 = new person(20, "Michael");
We can then use the -> operator to use this object:
p1->setage(33);
We can also create an array this way:
int * a = new int[100];
this creates an array of 150 integers with a pointing to the
first element, and a[2] pointing to the third element.
When you don't need the data object anymore it is easy to destroy by
using the delete operator.
delete i;
if we are destroying a class object:
delete p1;
the destructor is called before it is destroyed.
The one main complication arises when a memory area to be destroyed
contains an array of classes which have destructors. Other arrays can be
deleted simply with:
delete a;
but if it is an array of objects with constructors the delete operator
needs to know how many destructors need to be called. There are a couple
of main ways of doing this, and some compilers will not support the second
method below. Supposing ao points to an array of 100 objects, the first
method is:
delete [100] ao;
On most modern compilers you do not need to specify the number of elements
the array holds:
delete [] ao;
but the empty brackets are still needed.
20.Operator Overloading | home - top of the page - |
int a,b,c;
Mytype A,B,C;
....
c = a+b; c = 1+a; cout << a;
// valid statements
C = A + B; C = 1 + A; cout <<
A; // compiler will complain.
As we see, those operations (addition and
ostream)
are not defined for those objects.
So we can define them.
We can do this by defining so called "operator functions"
( in this case call it operator+()
and operator<<() ) which will
tell the compiler what to do (how to add objects or nubmer and an objects).
For example:
Mytype operator+ (Mytype N1, Mytype N2) {
Mytype T; T.n = N1.n + N2.n; return T; } |
This is called operator overloading.
Similarly we can define operator functions to overload most of other
operations ( operator/ opeartor%
operator* operator- S, even
operator= ).
Here is an example for the case of the above 2 operators: operator+
and operator<< .
Note that we define operator function here with '&' - so that they
return a reference to an object. even
Also note, that we make them friends so that they have access to private
numbers.
#include <iostream.h>
class number {
number T; // Temporary object (global) number& operator+ (number& N1, number&
N2) {
ostream& operator<< (ostream&
Cout, number& N) {
void main() {
|
21.Inheritance | home - top of the page - |
Inheritance allows a new class to be created by extending a class you have already created. This can save a lot of time and effort. Any class you derive from a base class can itself then become a base class from which you can derive more classes.
This section deals with single inheritance (each derived class inherits
the properties of one base class). Multiple inheritance is dealt
with in the next section.
Suppose we had created a class to hold the details of a bank account.
This account is called account and defined as follows :
class account {
double balance; //the balance of the account double rate; //the interest rate public: account(double b, double r); void deposit(double amt); double withdraw(double amt); double getbalance(); }; |
It is fairly obvious how these would be implemented :
account::account(double b, rouble r) {
balance = b; rate = r; } double account::getbalance() {
etc. etc. |
If we were writing a program dealing with bank accounts we might decide no one just has an account. They have a savings account or a check account or a time deposit account. All these accounts will have things in common with each other, but they will also have things which are different from each other.
To deal with this we could make one generic class called account that implemented the things all the classes have in common. Then we could extend this implementation for each type of account by creating a new class that inherited all these features, but also added some features unique to it.
In the case of accounts we might decide that the things all accounts have in common is that they have a balance, that you can deposit money, and that you can check your balance : we can define and implement it like this:
class account {
protected: double balance; //the balance of the account public: account(double b) {balance = b;} void deposit(double amt) {balance += amt;} double getbalance() {return balance;} }; |
The important thing to notice is that the instance variable balance is protected. In previous examples the instance variables were defined as private. The reason we need to make it protected is so that the functions in classes that inherit account can access it. In all other respects it is still private.
Of course you may not want the functions in a derived class to have access to the instance variables of their base class, in which case you can still declare them as private.
Now we can create a class called savings_account that inherits all the instance variables and functions of account, but adds some of its own.
class savings_account : public account {
protected: double rate; //the rate of interest in the savings account public: savings_account(double b, double r); double compound(); //computes the rate of interest double withdraw(double amt); }; |
We have made rate a protected variable in case we derive classes from savings_account.
Also note that account has been inherited as public account. Use public to ensure that member functions of the base class are available to users of the derived class. The alternative is private but you will seldom want to do this, so don't worry too much about it.
You may have noticed that savings_account will now have two instance variables - balance which is inherited from account, and rate which is declared in savings_account. Because of this we will want to call the constructors of both classes to initialize these two variables. This can easily be done like this :
savings_account::savings_account(double b, double
r) : account(b) {
rate = r; } |
The constructor of savings_account accepts both the variables needed to initialize an instance of the class, but it sends b on to the account constructor, and only deals directly with the r variable. This is signaled by the :account(b) on the first line. You can send away multiple arguments, just as long as the constructor in the base class will know what to do with them. If the account constructor took no arguments there would be no need to call it directly, although the program will still call it with no arguments. The constructor for the base class is actually called before the constructor of the derived class.
The other functions of savings_account have straightforward implementations.
Now if we create an instance of savings_account it can call not only the member functions of savings_account but also all those in account.
It is also possible to redefine functions in a derived class. Lets presume that the bank decides to charge people a fee for depositing money into their savings account (my bank is about at this stage now). The implementation of deposit in account is not sufficient because it only deposits the amount the user requests.
We could redefine the function in savings_account like this:
void deposit(double amt);
and then implement it like this:
void deposit(double amt) {
balance += amt; balance -= 1.00; } |
Now when an instance of savings_account calls deposit, it calls the function as defined in savings_account rather than the one defined in account.
We can also derive new classes from savings_account. Suppose we decide that a time deposit account has all the features that a savings account has, but also a few more. We might define it like this :
class time_account : public savings_account {
protected: double funds_avaliable; public: time_account(double b, double r, double f); double compound(); double get_avaliable_funds() {return avaliable_funds;} }; |
This new class has its own constructor, a function to show the available funds, and also a function that redefines the compound function defined in savings_account.
The most interesting thing to consider is the constructor. It might be declared like this:
time_account::time_account(double b, double r,
double f) : savings_account(b, r)
{ funds_avaliable = f; } |
The constructor uses one of the variables sent to it, but the other two are sent away to savings_account to initialize that class. Of course we also know that one of those will in turn be sent to account but we don't need to know this here - just as long as savings_account knows it.
Other issues relating to inheritance are dealt with in other sections,
but this introduction covers 90% of the things you will need to know for
most situations.
22.Multiple Inheritance | home - top of the page - |
Multiple inheritance may not be such a good idea. If you can avoid it
you probably should, but it is good to know how to do it.
Example: a class clock_calendar derived from 2 classes: clock
& calendar:
class clock {
protected: int hr; int min; public: clock(int h, int m); void advance(); void set_clock(int h, int m); void read_clock(int& h, int&m); }; class calendar {
// We can then create the clock_calendar class: class clock_calendar : public clock, public calendar
{
|
The constructor will then look like this - note that it calls the two existing constructors:
clock_calendar : : clock_calendar
( int mt, int day, int yr,
int h, int m)
: calendar(mt, day, yr), clock(h,
m) { }
All the member functions except advance are inherited, and all work exactly the same, although any could be overwritten if clock_calendar chose to define them. Advance must be overwritten, because if it was called from class_calendar C++ wouldn't know whether to call it from clock or calendar.
There would be a way around it if you insisted on not redefining it:
clock_calendar cc;
cc.clock : : advance();
cc.calendar : : advance();
Both would work. Unfortunately if you try to program in this way you
will forget the full phrase from time to time, it's inevitable.
23.Virtual Base Classes | home - top of the page - |
You may never need to use Virtual Base Classes - but there is no harm
in knowing what they are for, just in case the need ever arises.
Consider the following scenario:
The base class for a program is Grandparent. This has two Classes
derived from it:
class Parent_husband : public Grandparent
class Parent_wife : public Grandparent
We then have another class, Child which has multiple inheritance
from both Parent_husband and Parent_wife:
class Child : public Parent_husband,
public Parent_wife
A problem arises here. Child not only inherits the characteristics
of the two Parent classes, but also, from these, the characteristic of
Grandparent. The problem is, it is inheriting Grandparent twice,
once from Parent_husband, and once from Parent_wife.
To avoid this problem the intermediate base classes (the two parent
classes) must specify Grandparent to be a virtual base class.
Only one set of members will be inherited from a virtual base class, no
matter how many times it occurs in the inheritance chain. This is very
easy to do, simply change the declaration to this:
class Parent_husband : virtual public
Grandparent
class Parent_wife : virtual public
Grandparent
Seeing the problem is the difficult part, solving it is very easy.
24.Virtual Functions | home - top of the page - |
Virtual function allow for
polymorphism, one of the great
buzz words in Object Orientated programming. What this means is that objects
in different classes can respond to the exact same message in different
ways. Suppose a class child is derived from a class parent
.
A pointer to a parent class:
parent * p;
can be also used to point to an instance of child:
p = &c1;
presuming c1 was an instance of
the child class.
Lets suppose that class parent had two functions, one virtual,
the other not:
virtual void vf() {cout <<
"This is a virtual function";}
void nvf() {cout << "This
is not a virtual function";}
Class child redefines these two functions - but note the virtual keyword is NOT used:
class child : public parent {
...
public:
void vf() {cout << "This is
a virtual function of child";}
void nvf() {cout << "This
is not a virtual function of child";}
};
When p is pointing to an instance of parent, then
p->vf();
p->nvf();
are calls to parent's functions (as you would expect).
But if we point this (parent type) pointer it to a child:
p = &c1;
Now
p->vf();
// will point to a function in a child, because it is a virtual function
p->nvf();
// still points to the function in a parent.
With a virtual function, the class of the object pointed to determined
the function definition used. If p points to an instance of parent the
parent functions are used, but if p is changed to point to an instance
of child, the child functions are used.
25.Constants | home - top of the page - |
Constants can also be declared in C++. The basic form is this:
const int year = 2000;
If no type is included int is presumed, so we could actually declare
this:
const year = 2000;
Objects and functions can also be constants. An object is declared constant
like this:
const person p1(22, "Dane");
On the whole this means nothing about p1 can be changed, so statements
like:
p1.setage(20);
would be illegal, and rightly so. But statements like:
cout << p1.getage();
is also illegal, which is stupid, because it isn't changing p1 in any
way.
The way around this is to declare getage as a const in both
its declaration and definition:
int getage() const;
int person : : getage() const { return
age; }
If getage was an inline function it would
be changed like this:
int getage() const {return age;}
Pointers can also be made constants. This can mean two different things,
the first is this:
int k = 25;
int * const p = &k;
// const pointer
This means p must always point to k, but the value of k can be changed:
*p = 40;
The other way is this:
int k = 25;
const int * p = &k;
// const int value
This means that p can point to a different location, but we can change
the value of k.
So we cannot use expressions like
*p = 40;
but we can change what p points to:
p = &newint;
In effect this is a read only pointer. It can be set to point to any
area of memory, but it can't change the value stored there.
26.Static Keyword | home - top of the page - |
The static keyword is used in many
different situations, but it always is used to affect scope.
A static data object stays in existence
until the end of the program. This is opposed to normal automatic
objects are destroyed when they go out of scope. They are declared like
this:
static int i = 100;
Note: if a static variable isn't initialized, the compiler initializes
it to 0.
Note: if you declare it inside a block it can still only be referred
to inside that block, although it isn't destroyed when the block goes out
of scope.
A class can also create class variables with the static keyword. These data objects are associated with the class itself rather than any instance of the class. No matter how many instances of the class are created, there is only one of these data objects. Every instance of the class accesses the same variable. They can be declared like this:
class example {
int k;
static int m;
static int n;
public:
int getk() {return k;}
static void setm(int num) {m = num;}
};
Both m and n are static variables, only one set of them can ever exist in the program. They have not been created yet though. They both must be defined in the source file like this:
int example : : m;
int example : : n = 10;
Any function can manipulate m and n, but you can make special static functions that can only access static members: setm is an example. The great thing about these functions is that they can be called even when no instance of the class exists. We call them like this:
27.Input/Output | home - top of the page - |
User input/output:
cin>> n;
cout << somthing << somthing <<endl;
// file p2.cpp
#include <iostream.h> const int ii = 5; // const will not allow to change it int main() {
char string[256]; //A nice long string
return 0;
|
File read/write:
ifstream a_file("test.txt");
// read
ofstream a_file("test.txt");
// write
Those functions can accept optional 2nd argument,
for example:
ifstream a_file("test.txt",
ios::app); // open for appending
Possible values:
ios::app -- Opens the file,
and allows additions at the end
ios::ate -- Opens the file,
but allows additions anywhere
ios::trunc -- Deletes everything
in the file
ios::nocreate -- Does not
open if the file must be created
ios::noreplace -- Does not
open if the file already exists
Example: read text file and output it on the screen:
ifstream my_in("example.txt");
my_in >> str; cout<<str; my_in.close(); |
28.Built in Functions | home - top of the page - |
For the string functions, go to the strings section.
A lot of the other most useful built in functions are in the math.h
class:
float f = sqrt(9);
float f = sin(90) // same applied for tan() and
cos().
pow(x, z) // returns x to the power of y.
log(x) and log10(x)
abs(x) //: returns absolute of x