Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Chapter 4: Inheritance
Chapter 5: Polymorphism
1
Chapter 4: Inheritance (default constructor)
{
4.1 Introduction protected: // No longer private if members need
// to be accessed by derived class
Inheritance is one of three main strengths of OOP. As the name double real;
suggests, inheritance is when you get something for free! This is double imag;
exactly what inheritance in OOP is all about. public:
// Follows Listing 3.3
};
4.2 Simple Inheritance
4.2.2 Inheriting A Base Class
Lets inherit something. But first, you may need to know who are
giving you their inheritance. Now you are in a position to inherit the Complex class and
derive a new class called Cname (which is a Complex class
Let me introduce to you: with a name). You may either write the following straight after the
Complex class definition or in a new file.
Base class A class who is willing to share with you its
inheritance. class Cname : public Complex
Derived class A class that inherits from a base class. {
protected: Protected data behave as private in its own class. protected:
char* name;
data Nobody from the main program can access
public:
them directly. However, these data can be char* getName();
accessed by its derived classes. Cname(char* n, double r, double i);
: When used in the class declaration, the };
operator : makes the contents of the base class to
be within the derived class. char* getName()
{
4.2.1 Protecting Your Data Members return name;
}
If you want certain data members to be used by a derived class, Cname(char* n, double r, double i):name(n)
then you must declare them as protected instead of private. They {
are still non-accessible from the main program. real = r; // Cant put them in the
imag = i; // init-list because they
class Complex // belong to the base class
2
}
Now you have it. By appending : public Complex behind the An overloaded constructor is written for the derived Cname class
class name, you will inherit all the data members and member which accepts three parameters: a name, a real and an imaginary
functions from Complex. The only thing members added are value. While the name is assigned to name of Cname, the real
name, its corresponding accessor function and a constructor. and imaginary values are assigned to the real and imag of the
Dont believe your luck?! Try out the following code and see for base Complex class which Cname now inherits. Instead of the
yourself that you have inherited all the visible data members in long-winded way of initialization via the constructors body:
Complex.
Cname::Cname(char* n, double r, double i)
main() {
{ name = n;
Cname c(Test complex number, 3, 4); real = r;
// A Cname object by the name of Test imag = i;
// complex number and the value of 3+4i is }
// created.
initialization can be done via the init-list method by calling the
cout << c.magnitude(); constructor of the base class. Note it is more complicated if you
// Screen displays 3+4i, even though you want to call the constructor of the base class from within the body:
// have not written a magnitude function
// in Cname thanks to inheritance
Cname::Cname(char* n, double r, double i)
return 0;
{
} Wont work!
name = n;
Complex(r,i);
// This wont perform the desired
4.2.3 Constructor to Call a Constructor // initialization
3
Not all inherited members in the derived class maintain their
access level in the base class. Their new access level in the derived
class is controlled by the keyword public, protected or
private that follows the derived class name and preceeds the base
class name: 4.3 Using Header Files (*.h)
class Cname : public Complex
An amazingly simple way of storing the base class and all its
{
... definitions is to create a header file a file saved with a .h
}; extension, e.g., iostream.h, string.h are C++ header files. This
header file can then be included in any program that wants to use
or inherit the base class. Let me show you how to write and use
public: header files correctly:
Inherited members maintain their access level:- public
members in the base class remain as public members in the Write your base class and all its definitions (function bodies). Do
derived class, protected members remain protected in the derived not include main() because it does not belong a class. Save the
class. Private members cannot be accessed (they are inherited file as Complex.h in your disk. DO NOT COMPILE
though just not visible to its derived class)! Complex.h !!!! When you want to inherit the Complex class
from a derived class (Cname), simply include the Complex.h
protected: header file but with double quotes:
Inherited public and protected members of base class
become protected members of the derived class. Only classes that Cname.cpp
are derived from the base class can access such members, but not
the world. #include <iostream.h>
#include Complex.h
private:
class Cname : public Complex
Inherited public and protected members become private {
members of the derived class, so neither the derived classes of the ...
derived class nor the world has accessed to them. };
4
Lastly, let me remind you NOT to compile Complex.h. Simply
Base class
save it in the same directory as your Cname.cpp and include it
in the header of your Cname class. Anim al
void Animal::eat()
{
cout << "Yummm!! I am eating ... food.\n";
}
void Animal::talk()
5
{ Animal class. The key to inheritance is via : public
cout << "Don't mock me! You know animals Animal. Try this:
can't talk!\n";
} main()
{
Herbivore h; (B)
The Animal base class has been written. The only thing new is /* This calls the default constructor which
the use of protected access in place of private. Now write a calls the Animal constructor, passing
derived Herbivore class: herbie and 5 as input arguments
*/
// ==== Derived class ==== cout << My name is << h.name << .\n;
class Herbivore : public Animal (A) // Screen displays My name is herbie.
{ (C)
public:
char* fav_veg; h.talk(); (D)
void eat(); // Don't mock me! ...
Herbivore(char* veg):fav_veg(veg) {} h.eat(); (E)
Herbivore():Animal(herbie, 5) {} /* Since there is an eat function in the
}; derived class Herbivore as well as one
with the same name in the base class
void Herbivore::eat() Animal, the function that belongs to the
{ host object, i.e., h, will be called.
cout << Yummm!! I am eating << fav_veg; */
} }
Explanation: 3. A name used in a derived class hides any member with the
Herbivore is short and sweet. It has more members than same name in the base class. The members in the base
meet the eye. Its data members include name and age class can be accessed using the scope resolution operator.
because they have been declared protected in the base
Explanation:
6
(E) hides the eat function in the base Animal class. Since class Herbivore : public Animal (A)
there is an identical function in the derived class Herbivore, {
the function that belongs to the host object, i.e., h, will be public:
char* fav_veg;
called.
void eat();
};
4. Constructors with parameters can use base class
constructors with parameters (using an initialization list) Then instantiation of a Herbivore object:
Explanation: Herbivore h;
See (A) of the derived Herbivore class. Explanation in will result in object construction via the constructor of the
Chapter 4.2.3. Animal class, not from the Herbivore class.
A derived class can control access to public and protected 6. If it is anticipated that a derived class will want to override
members inherited from a base class (by the keyword following any functions with its own versions then the functions
the colon in the class definition). They may be inherited as should be declared virtual in the base class. (Details in
public, protected or private (Chapter 4.2.4): later chapters)
Base public protected private
public public protected no access 7. Multiple inheritance is allowed. (See figure below)
protecte
Derived d
protected protected no access Explanation:
private private private no access C++ supports multiple inheritance. Say Dog is both an
Omnivore as well as a Vertebrate. Then write:
5. If a derived class has no constructors and/or destructors, class Dog : public Omnivore, public Vertebrate { ... }
then those of the base class will be used.
If only one function is needed from the base class, avoid
Explanation: inheriting the whole class. Instead declare function public and
Say Herbivore does not have a constructor: access it via scope resolution operator (::) or use the friend
keyword.
7
Base class
Anim al
8
Chapter 5: Polymorphism
5.1 Introduction a herbivore or a carnivore, you can still write your program now
and let C++ deal with your animals appropriate diet later! Before I
proceed, let me introduce to you:
OOP is not complete without polymorphism, which is (arguably)
the most important facet of OOP. Some people may feel that if they
virtual Used on a common function name. When declared
have an idea of what classes are they have stepped in the object-
virtual, a function will be overridden by another
oriented world. But this is not true. Polymorphism IS the core of
function of the same name in its derived class.
OOP.
E.g. virtual void Eat();
Anim al
Polymorphism is only achieved via several classes that are related
through inheritance. It is the ability for the C++ compiler to call
different functions belonging to different classes by just using one
type of function call! This is possible when the base class and its
derived classes (and their derived classes) have common function
H erbivore Carnivore O m nivore
that interfaces with the world / main program. The key to this
unbelievable feature is to use either pointers to base object or
references of derived objects instead of dealing with the physical Derived classes
object itself. In other words, polymorphism is only achieved
through ADDRESSES of objects, and not the objects concerned. Figure 5.2a
5.2.1 The Animal Example Again Alright, now let me show you the codes for your classes.
Afterwards, I will show you the main program.
Lets use the familiar Animal example in Chapter 4 whose family
tree is represented by Figure 5.2a.
Listing 5.1 Animal class and its derived classes
Recall that both the base class and its derived classes have a
common function called eat. Say you are going to rear an animal class Animal
and at 12 noon everyday, youll feed your animal. However, if {
during compile time, you have not decided whether your animal is protected:
9
char* name; Carnivore(char* meat):fav_meat(meat) {}
int age; Carnivore():Animal(connie, 5) {}
public: };
virtual void eat(); void Carnivore::eat()
void talk(); {
Animal() {} cout << I like my << fav_meat << rare!;
Animal(char* n, int a):name(n),age(a) {} }
};
void Animal::eat() Notice that Listing 5.1 is no different from the codes written for
{ usual inheritance EXCEPT for the inclusion of the virtual
cout << "Yummm!! I am eating ... food.\n";
keyword, which is vital for polymorphism. Declaring a function
}
virtual in the base and derived classes allows the function of the
void Animal::talk() derived class to be called through a public base pointer or
{ reference. Without virtual, it is always the function of the base
cout << "Don't mock me! You know animals class that is called!
can't talk!\n";
} A main program example can be used to illustrate this:
// === Derived class Herbivore === main()
class Herbivore : public Animal {
{ Animal* a_ptr; // Note : pointer used! (1)
public: int choice;
char* fav_veg; cout << 1) Herbie, 2) Carnie\n?;
virtual void eat(); cin >> choice;
Herbivore(char* veg):fav_veg(veg) {} if (choice == 1)
Herbivore():Animal(herbie, 5) {} {
}; Herbivore h(grass); (2)
a_ptr = &h; // a_ptr points to h (3)
void Herbivore::eat() }
{ else if (choice == 2)
cout << Yummm!! I am eating << fav_veg; {
} Carnivore c(steak); (4)
a_ptr = &c; // a_ptr points to c (5)
// === Derived class Carnivore === }
class Carnivore : public Animal
{ // Polymorphism demonstrated here
public: // At 12.00pm everyday,
char* fav_meat; a_ptr->eat(); (6)
virtual void eat();
10
// Alternative: (*a_ptr).eat(); Animal* a_ptr;
return 0;
} // Decide at run-time who a_ptr should point to
a_ptr->eat();
Check out the two different screen outputs which depend on the /*
value of choice. The animal pointer, a_ptr could be the address
of a herbivore:
a_ptr = &h;
Or it could be the address of a carnivore:
a_ptr = &c;
*/
5.2.2 In Depth Explanation Of The Animal Example 5.2.3 Try It For Yourself : talk function in Animal
Recall that polymorphism only works if a pointer or a reference to
the base object is used. This is because pointers and references Now that you have seen how it works for the eat function, you
store the address of an object. C++ allows the pointer of a base should try polymorphism by modifying codes for the talk
class type to store the address of an object of its derived class. It is function in a similar fashion to the eat function. Go on, modify
this feature that makes polymorphism possible. the talk function for a Herbivore to display
11
Listing 5.2 Pet class and its derived classes
and the talk function for a Carnivore to display
#include <iostream.h>
You look like my dinner... #include <string.h>
class Pet
{
5.3 Pointers and References public:
Pet() {}
You have just seen an example of polymorphism through a base virtual ~Pet() {}
class pointer. It also works for references too (as they are virtual void speak();
essentially addresses too). };
Check out this example which I found from the internet1 (shhh!) void Pet::speak()
{
which I believe explains polymorphism (at your level) rather
cout << Growl << endl;
completely. First of all, let me introduce to you: }
new The new operator is used to instantiate an object // Derived class : Rat
dynamically from a pool of reserved memory space class Rat : public Pet
called the virtual memory. This is done during run-time. {
delete The opposite of new, delete destroys the memory public:
allocated for the object instantiated using new. Rat() {}
~Rat() {} // Virtual
Lets check out the codes that involve both pointers and references: void speak(); // Virtual
};
Base class
void Rat::speak()
{
Pet cout << Rat noise << endl;
}
12
void Cat::speak()
{
cout << Meow << endl;
}
13
void chorus(Pet pet, Pet *petPtr, Pet &petRef)
{ In main, Pet *ptr; declares a pointer of the base class, Pet. Of
pet.speak(); course, it can store the address of a Pet or any of its derived
petPtr->speak(); classes, i.e., Rat and Cat. (This is the basis of polymorphism
petRef.speak();
}
where the type of the actual object that the pointer points to needs
not be decided until the very last minute!).
main()
{ Check out the screen output:
Pet *ptr; // Pointer to base class
14
In chorus, the first parameter is called pet. The variable pet is The first parameter is a Pet object. Recall that this parameter is
a duplicate copy of the actual Pet object *ptr. The speak passed-by-value, where a Pet object is expected and a local Pet
function of pet is called via the . operator as you would objects. object is duplicated. However, since a Rat object is being passed
and it is bigger than a Pet object (because Rat has more codes
The second parameter is called petPtr, which is a pointer that after it inherits from Pet), the Rat object has to be sliced up and
stores the address of the actual Pet object *ptr. The speak only the Pet portion of the Rat object is duplicated. Thus, the
function of petPtr is called via the -> operator as you would speak function of pet (which is the sliced, duplicate version of
pointers. *ptr, a Rat object) refers to the Pet class, not the Rat.
The third parameter is called petRef, which is the reference of the The second parameter is a Pet pointer, which essentially is the
object *ptr, which happens to be its address. The speak function address of the Rat object. Since only the true address of the Rat
of petRef is called via the . operator as you would references. object is sent in, the speak function refers to that of the Rat
object. That is why screen displays Rat noise.
Section (B) The third parameter is a Pet reference. It means a Pet object
passed-by-reference. Thus PetRef stores the address of the Pet
The line ptr = new Rat; declares a Rat object and its address object. C++ supports a derived Rat object being referenced. Thus,
is stored in ptr. This gets a bit trickier because Rat is a derived calling the speak function of PetRef will result in Rat
class that inherits from Pet. The chorus function is the same. noise, which comes from the Rat, not Pet.
However, the output is drastically different now. Thanks to
polymorphism!
Section (C)
Remember that there is no Pet object, only a Rat object.
This section is similar to Section (B), only that the Rat is replaced
The line by the Cat.
passes to the function the Rat object (*ptr) twice and a pointer 5.4 Array of Pointers
which stores the address of this Rat object (ptr). The parameter
list over at the chorus function includes a Pet object, a Pet If you declare an array of base class pointers, its elements can
pointer and a Pet reference. Note that while the variables being point to any of its derived objects. Note that you must use the new
passed are physically Rat objects (and pointers), the input keyword. Let me show you an example using the same Pet, Rat,
parameters of the chorus function are of the base class type Pet. and Cat classes.
main()
15
{
Pet* arr[5]; I will show you an example of how the program works;
int choice, i=0; emphasizing to you that the pointer array does not know which
derived objects will be used until the program is run. This is also
called late binding.
while(i<5)
{
cout << 1) Rat or 2) Cat\n?;
cin >> choice;
if (choice == 1)
{
arr[i++] = new Rat;
}
else if (choice == 2)
{
arr[i++] = new Cat;
}
}
for (i=0; i<5; i++)
{
arr[i]->speak();
In this example, I have chosen 2 cats, 1 rat, then another cat, and
}
return 0; lastly, a rat. Using a for loop, the appropriate speak functions
} are called accordingly.
16
Part III Miscellaneous
6.1 Inline function of the function will substitute the function call. So in the
above example, the actual codes compiled by C++ looks like:
The inline keyword tells the compiler to substitute the code within main()
the function definition for every instance of a function call for {
increased efficiency. However, substitution occurs only at the noisyClass x;
compilers discretion. For example, the compiler does not inline a cout << A noisy object has been created!\n;
function if its address is taken or if it is too large to inline. cout << endl << Ching Chang! << endl;
/*
What you need to know about inline functions: During compilation, inline functions are
1. The code within the function definition is substituted for every substituted with the codes in their
function bodies.
function call.
*/
cout << And he is making himself known!\n;
class noisyClass
cout << endl << Ching Chang! << endl;
{
}
public:
void makeNoise();
}; 2. Inline functions are usually short about 3 lines or less.
Otherwise it does not make sense to have them substituted.
inline void noisyClass::makeNoise() 3. You cannot have loops in an inline function.
{ 4. A function defined in the body of a class declaration is
cout << endl << Ching Chang! << endl; automatically an inline function.
}
class Account
main() {
{ public:
noisyClass x; Account(double init_bal) { bal = init_bal; }
cout << A noisy object has been created!\n; double GetBalance();
x.makeNoise(); double Deposit( double Amount );
cout << And he is making himself known!\n; double Withdraw( double Amount );
x.makeNoise(); private:
} double balance;
};
What happens after you declared a member function inline is
that at every occurrence of the function call, the actual codes The Account constructor is an inline function!
17
5. You may declare a function inline by adding the keyword private:
inline a) outside the body of a class or b) inside the body. double balance;
};
a) Outside:
18
// acct has opening balance of RM500 balance is a private member of the Account object, acct.
PrintBalance(acct); As a friend function, PrintBalance has access to it.
return 0; The const keyword is used to ensure that the original
}
Account object is not accidentally (or deliberately) altered
because it has been passed-by-reference (&).
Note:
Since a friend function is NOT a member function, you shall not
1. The friend function PrintBalance has no class scope
call the PrintBalance function via an Account object as the
operator Account:: at function declaration which would
following example of a main program shows:
otherwise look like
main()
// Example of a member function :
{
void Account::PrintBalance(void)
Account acct(500);
{
// acct has opening balance of RM500
cout << The balance of the account is RM; Wrong!
cout << balance; // or cout << this->balance;
acct.PrintBalance(acct); // Illegal
}
// PrintBalance is not a member of Account
return 0;
The above example is a member function. A friend function }
does not have the class scope operator and is therefore NOT a
member function. The difference is that a member function Instead, simply call the friend function in your main program as
can access its data members directly using this-> or by you would a global function:
simply calling the name of the member. Therefore, a friend
function disallows access to the members of the class via the main()
member-access operators (. and ->). {
Account acct(500);
2. Although PrintBalance is not a member function, it can still // acct has opening balance of RM500
access the private members (e.g., balance) of an Account Correct!
object when the object concerned is passed into the function as PrintBalance(acct); // Yeah! This is OK.
return 0;
an input parameter.
}
// Example of a friend function :
void PrintBalance(const Account& acct)
{
cout << The balance of the account is RM; 6.3 Operator overloading
cout << acct.balance;
} Explaining operators
19
Operators in C++ help us perform specific tasks. They include +, operator. It also involves two operands, and hence it is a binary
-, =, >>, <<, ++, just to mention a few. I believe you have operator. The idea is to assign the RHS operand to the LHS
seen various usages of these operators. They can be further divided operand. Here, the RHS operand is the summed integer value of x
into unary operators (with one argument e.g., number++) and and y. The value is to be copied over to the LHS operand, which is
binary operators (with two arguments e.g., num1 + num2). total, also of type int.
Lets look at the following example:
Next let us focus on the code cout << total;. Can you guess
Listing 6.2 Basic operators whether the operator << is a unary operator or a binary
operator? The answer is, << is a binary operator because it takes
#include <iostream.h> in two arguments the first is its LHS operand which is an output
stream called ostream, while the second is its RHS operand
main() which is, in our example, an integer value. ostream is nothing
{
but a class that controls the sequential output of a stream of
int x = 2, y = 5, total;
total = x + y;
characters.
cout << total;
return 0; In cout << total;, the integer value of total is attached to the
} output stream object, ostream, and the resultant stream is
returned and displayed on screen.
Lets focus on the line of codes total = x + y;. Two sets of ostream& operator << ( ostream& out,
instructions are executed, the first being x + y. C++ associates the const int &right )
{
operator + with a function which operates on two operands
return ( out << right );
one from the left hand side (LHS) of + and the other from its right }
hand side (RHS). In fact the function for the operator + which
is pre-written in C++ looks something like this: Here the integer is appended to the existing output stream object.
The result is then returned.
int operator+ (const int &left, const int &right)
{
return left + right; Why learn how to overload operators?
}
C++ can handle addition and display of other (standard) data types
Note that the left and right (integer) operands are passed into the like double and char. However, it capability stops here because
above function as its first and second arguments and subsequently it cannot perform the addition of user defined objects like complex
the summed result (of type int) is returned. Thus after x + y is numbers:
executed, you are left with total = <int>; where <int> is the
summed value of x and y. Here the second set of instruction will main()
now be executed. The operator = is called the assignment {
20
Complex c1(2,5); However, if you choose to use a friend function (or if you have
cout << c1; // Illegal! no choice but to choose a friend function!), then ...
Complex c2(7,3);
cout << c1 + c2; // Illegal!
You will have TWO arguments if the operator is binary
return 0;
} You will have ONE argument if the operator is unary
Ideally, you would like it if your codes above would compile and Let me demonstrate to you by giving you examples of commonly
display the summed value of c1 and c2, i.e., 9 + 8i. overloaded operators:
Unfortunately, this is not readily executable. Fortunately, you can
OVERLOAD the OPERATORS + and << to achieve the desires 6.3.1 operator +
of your heart! In our context, we overload the operators so to have
input parameters of non-standard, user-defined class types (like Using member functions:
Complex).
// Operator overloaded using a member function
class Complex{
How to overload operators?
public:
In general, there are two ways to overload an operator using a Complex operator+( Complex &);
...
member function, and using a friend function. In most cases, you private:
can choose between these two, even though I personally think ...
using a friend function is always better. However, assignment };
operators such as =, +=, *=, <<=, &= can only be
overloaded using member functions (because the left hand side Complex Complex::operator+( Complex &other )
operand of the operator must be a l-value). On the other hand, {
overloading the insertion and extraction operators >> and << return Complex( real + other.real,
would require a friend function. This is because the first imag + other.imag );
}
argument is an ostream and not an object of the user-defined
class. Remember that the ostream class is not ours to touch, but
you may append some data to it. // === Example of usage ===
main()
{
NOTE: ...
If you choose to use a member function (or if you have no choice // Using member function
but to choose a member function!), then ... // member-access operator . required
cnum_total = cnum1.operator+(cnum2);
You will only have ONE argument if the operator is binary }
You will have NO arguments if the operator is unary
NOTE:
21
1. A host object (cnum1) is needed to access the operator+. 3. A Complex object (which is the summed value of cnum1
2. Then an argument (cnum2) is passed into the function. and cnum2) is returned. This value is assigned to
3. A Complex object (which is the summed value of cnum1 cnum_total.
and cnum2) is returned. This value is equated to 4. A friend function is placed outside all scopes. I like to place it
cnum_total. before public: scope.
4. A member function is placed in the public: scope. 5. The differences in syntax in using member and friend
functions are highlighted in bold letters and circles.
Using friend functions: (I personally prefer this)
6.3.2 operator <<
// Operator overloaded using a friend function
class Complex{
friend Complex operator+( Complex &, Complex &);
Overloading the insertion operator << involves the passing and
// friend functions declared before public: returning of basic I/O (input/output) streams.
public:
... Let me introduce to you:
private:
...
}; istream Short for input stream. The cin object belongs to
the istream class. It handles a stream of input.
Complex operator+( Complex &left, Complex &right) ostream Short for output stream. The cout and cerr belong
{ to the ostream class
return Complex( left.real + right.real,
left.imag + right.imag); Both istream and ostream derive the iostream class.
} Including <iostream.h> at the beginning of your program will
ensure the use operators >> and <<.
main()
{
// Using friend function Overloading insertion operator << can only be achieved
cnum_total = cnum1 + cnum2; through a friend function where to its left must lie an ostream
... object (e.g., cout) and on its right the object of your own class:
}
class Complex
NOTE: {
1. A host object (cnum1) is no longer needed to access the friend ostream& operator <<
( ostream& out, Complex& c )
operator +.
{
2. The operator takes in two parameters, the left hand side and return (out << c.real << +i << c.imag);
the right hand side of the operator +. }
public:
...
22
private:
... Complex & Complex::operator= ( const Complex &c )
}; {
this->real = c.real;
The above overloaded friend operator is an inline function. this->imag = c.imag;
return *this;
You pass two parameters in the ostream object (by- }
reference) and the complex number object, c (also by-
reference). Note:
The return object is the same ostream object (by-reference) c is the input parameter that is passed-by-reference (&). To
which is now appended with the real and imaginary parts of prevent the original variable from being accidentally modified,
the complex number. c is declared as a read-only value (const).
Remember that the new ostream object is returned so that After the real and imaginary values of c are copied to the host
the appropriate text can be displayed, i.e., <real>+i<imag>. object, the complex number c is returned (by-reference).
23
## (preprocessor symbol). Operators obey the precedence, grouping, and number of operands
dictated by their typical use with built-in types. Therefore, there is
no way to express the concept add 2 and 3 to an object of type
Point, expecting 2 to be added to the x coordinate and 3 to be
added to the y coordinate.
6.4 General Rules for Operator Overloading
Unary operators declared as member functions take no arguments;
if declared as global function, they take one argument.
The following rules constrain how overloaded operators are Binary operators declared as member functions take one argument
implemented. However, they do not apply to the new and delete if declared as global function, they take two arguments.
operators. All overloaded operators except assignment (operator =) are
inherited by derived classes.
Operators must either be class member functions or take an The first argument for member-function overloaded operator is
argument that is of class or enumerated type or arguments that are always of the class type of the object for which the operator is
references to class or enumerated types. For example: invoked (the class in which the operator is declared, or a class
derived from that class). No conversions are supplied for the first
class Point argument.
{
// Declare addition operators.
friend Point operator+ ( Point &, int ); Note that the meaning of any of the operators can be changed
friend Point operator+ ( int, Point & ); completely. That includes the meaning of the address-of (&),
assignment(=), and function-call operators. Also, identities that can
public: be relied upon for built-in types can be changed using operator
// Declare a member operator overload. overloading. For example, the following four statements are
Point operator< ( Point & ); usually equivalent when completely evaluated:
...
};
var = var + 1;
var += 1;
The preceding code sample declares the less-than operator < as a var++;
member function; however, the addition operators + are declared ++var;
as global functions that have friend access. Note that more than
one implementation can be provided for a given as in the case of This identity cannot be relied upon for class types that overload
the addition operator. In the case of the preceding addition operators. Moreover, some of the requirements implicit in the use
operator, the two implementations are provided to facilitate of these operators for basic types are relaxed for overloaded
commutativity. It is just as likely that operators that add a Point to operators. For example, the addition/assignment operator, +=,
a Point, int to a Point, and so on, might be implemented. requires the left operand to be an l-value when applied to basic
24
types; there is no such requirement when the operator is negative integers. By eliminating the possibility of having negative
overloaded. integers, the storage is doubled instantly.
Note For consistency, it is often best to follow the model of the unsigned int y = 12345U;
unsigned y = 12345U; // assumed int
built-in types when defining overloaded operators. If the semantics
// Note trailing U. Small letter u is OK
of an overloaded operator differ significantly from its meaning in
other contexts, it can be more confusing than useful. unsigned long y = 12345UL;
25
#include <string>
There are 3 scopes in the above example. The outer scope is called // < > (without .h extension)
the global scope, and variables declared there are visible
throughout the entire program. You may declare and initialize them #include Complex.h
// (double quotes)
right before your main() entry point.
The first two statements specify system-supplied header files. The
In inner scopes such as the one defined by function, declaration of
one with double quotes specifies a header file that is written by a
variables using the same name is allowed. The variables in the
user, probably your programmer friend or yourself! This header
inner scope will overwrite those on the outside. Example, char x
file (Complex.h) is stored in the directory as your current
declared within the function overwrites the global int x.
program.
extern, :: 6.7.2 #define
When you are in an inner scope, in order for you to use the same You may want to standardize the values of certain variables. For
variable name as the global variable, you will need to declare the example, you know that = 3.14159265. and that is only an
variable once more AND prefix the declaration with the keyword approximate. Thus, to standardize the value of that you will be
extern. It means that the variable in the inner scope is indeed the using throughout your entire program, you can place you desired
global variable. Example, extern int x; value in the header behind the keyword #define.
To access the global variable directly without re-declaring using #define pi 3.142
extern, simply prefix the variable name with ::, the scope // same value for pi will be used throughout
// entire program
resolution operator. Example, ::x.
#define root_2 1.414
// Similarly for square-root of 2
6.7 Header What You Can Put In It
6.7.3 typedef
The header of a .cpp file may contain statements from below:
The typedef declaration is used to create new names for basic as
6.7.1 #include well as user-defined types, including classes. The new user-defined
type name should be more descriptive or more compact to use.
A C++ file would almost certainly contain one or more #include Once a new type name has been established, it can be used just like
statements. Typically, you will get see the following three any other type name.
variations:
typedef unsigned short Age;
#include <iostream.h> // Age is a new data type!
// < .h > // You can type : Age x = 21;
// Equivalent to : unsigned short x = 21;
26
At other times, you need to / may request the compiler to perform
typedef Complex* cnum_ptr; the type conversion explicitly. You have seen one way of casting in
// You can even typedef a pointer to a class! Chapter 1.2.4, which is given again below:
Type casting is often used when you want to process two data of y = static_cast< int >(x);
different types. Often these processes are arithmetic operations. z = static_cast< float >(x);
6.8.1 Implicit Type Casting Other types of casting include reinterpret_cast, const_cast,
and dynamic_cast. I will briefly show you the usage of the
Sometimes, the compiler performs the conversion automatically dynamic_cast:
and implicitly.
Using the example of Pet and its derived classes in Listing 5.2:
float f = 3.14159f; Pet* p;
int g = f; // g = 3 Cat* c = dynamic_cast<Cat*>(p);
// Automatic type conversion implicit cast c->speak();
A function performing rounding with implicit cast: In the above example, the Pet object, p, is downcasted to a Cat
object. Downcast means it is being cast from an object of a higher
int round(float f) position in the family tree to an object of a lower position.
{
int x = f;
// Conversion from float to int is automatic Pet
return x;
}
27
Dynamic casting always casts to a pointer or a reference to a
previously defined class (<Cat*> is a pointer to a previously You can also assign two similar words with the same integer value.
defined class). Lets say, you want to allow small letters mon, tue, wed, etc. Both
MON and mon should equal 1. Thus:
6.9 enum (enumeration) enum Days {MON=1,TUE,WED,THU,FRI,SAT,SUN,
mon=1,tue,wed,thu,fri,sat,sun};
You use enum when you have a sequence of countable elements
which you would rather describe using (meaningful) words rather enum needs not be in a sequence. You can jump the sequence:
than integers. The enumeration type is a user-defined integer type.
Like typedef, you introduce a new name but it can only be of the enum Square { SQ1=1, SQ2=4, SQ3=9, SQ4=16 };
integer type. For example, there are 7 days in a week, i.e., Monday,
Tuesday, etc. You would like to associate each day with an integer, It is not compulsory to associate every enum with a name. Such
say Monday = 1, Tuesday = 2, etc. Using enum: anonymous enumerations are alternatives to #define constants:
enum Days {MON=1, TUE=2, WED=3, THU, FRI,
SAT, SUN};
enum { MAX_LENGTH = 256, BYTE_SIZE = 8 };
Now you can declare a variable as type Days: You can treat Days as a new data type. You can even pass
arguments such as Mon or Fri into functions:
Days today = MON;
// Equivalent to: int today = 1;
void tomorrow(enum Days today) {
cout << static_cast<Days>(today+1);
Notice THU, FRI, etc do not have an integer value attached. An }
element will automatically get an integer value that is one higher
than its previous element. Since WED=3, therefore, THU=4, and Screen display will be an integer representing the days rather than
FRI=5, and so on. the words (MON, TUE, etc) you were hoping for.
28