Sei sulla pagina 1di 28

Part II Britneys Favourite Programming Language

(OOPs, Ive done it again!)

Encapsulation (Implicit in Chapter 3)

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

Title simply means: a constructor of the derived class is called by this->Complex::Complex(r,i);


the main program. This constructor then calls the constructor of the // OK. Initialization works.
(*this).Complex::Complex(r,i);
base class.
// Identical to using this->
} It works, but more complicated
Derived class constructor than using the init-list

Cname::Cname(char* n, double r, double i)


:name(n),Complex(r,i)
{ 4.2.4 Access Restrictions of Inherited Members
... Base class constructor (in
} the init-list)

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. };

Base public protected private // Function definition here


public public protected no access // You can write your main() program here
protecte
Derived d
protected protected no access
Have I mentioned to you that you SHOULD NOT COMPILE
private private private no access Complex.h?

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

4.4 An Animal Example To Illustrate Some


Rules of Inheritance
H erbivore Carnivore O m nivore
For the sake of simplicity, I will show you an easier example using
an Animal class and several classes derived from it. Consider the
Derived classes
hierarchy:

Animal well, it is a beast.


Data members: name, age. // ==== Base class ====
Member functions: eat(), talk(). class Animal
{
protected:
Herbivore is an animal which eats only grass / vegetables. char* name;
Data members: name, age, fav_veg. int age;
Member functions: eat(), talk().
public:
Carnivore is an animal which eats only meat. void eat();
Data members: name, age, fav_meat. void talk();
Member functions: eat(), talk().
Animal():name("an animal"),age(1) {}
Animal(char* n, int a):name(n),age(a) {}
Omnivore is an animal which eats both meat and grass / veg.
// You can write the constructor body here
Data members: name, age, fav_food. // in the class definition instead of
Member functions: eat(), talk(). // writing its prototype in here first and
// then the function body later.
// Note the semicolon ; is omitted!
};

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; */
} }

2. A derived class has full access to the protected and public


RULES OF INHERITANCE: members of the base class but not to private members
unless declared a friend of the base class. (friend
There are many rules regarding inheritance. Here are several explained in later chapters).
selected rules that I think would be suitable for you at this level:
Explanation:
1. A derived class inherits all functionalities of the base class (C) and (D) access members of the base class which have
(its data members, member functions, operators, etc) been declared public or protected.

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

Derived classes 1st level

H erbivore Carnivore O m nivore Vertebrate Invertebrate

Derived classes 2nd level


D og Snake Multiple inheritance

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();

5.2 Polymorphism Explained Base class

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;
*/

On the other hand, if an object of the base class is declared instead


of a pointer, you will never be able to demonstrate polymorphism
because the type of the object has been fixed during compile time.
Polymorphism is demonstrated here. As you can see, the same line
It is the base object of course. However with pointers who store
of code a_ptr->eat() produces two different screen outputs
addresses, you do not have to decide on the object type during
depending on the value of choice. If user keys in 1 (for
compile time. You can do so during run-time.
herbivore), then the base pointer, a_ptr, is smart enough to
figure out that the animal is indeed a Herbivore (instead of its Animal a_obj;
base, Animal) and appropriately calls the virtual function in
Herbivore. The same goes for carnivore. a_obj.eat();
/*
One other point is that you only need to declare a function virtual There is no room for polymorphism because the
in its base class. All functions of the same name in its derived type of a_obj has been fixed during compile
classes will be assumed virtual automatically. Of course, there is time, i.e., Animal. Thus, the code a_obj.eat()
no harm redeclaring virtual for the functions in the derived will always call the eat function of the Animal
class, not any other classes.
classes to increase clarity and readability.
*/

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

Wheres my green green grass?"

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;
}

// Derived class : Cat


class Cat : public Pet
Rat Derived classes Cat {
public:
Cat() {}
Figure 5.3a ~Cat() {} // Virtual
void speak(); // Virtual
};
1
Many thanks to http://cplus.about.com

12
void Cat::speak()
{
cout << Meow << endl;
}

// ========== END OF CLASSES ==========

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

ptr = new Pet;


cout << Pets singing << endl; (A)
chorus(*ptr, ptr, *ptr);
cout << endl << endl;
delete ptr; // Prevents memory leaks

ptr = new Rat; // Note - derived class


cout << Rats singing << endl; (B)
chorus(*ptr, ptr, *ptr);
cout << endl << endl;
delete ptr;

ptr = new Cat; // Note - derived class


cout << Cats singing << endl; (C)
chorus(*ptr, ptr, *ptr);
cout << endl << endl;
delete ptr;
Section (A)
return 0;
} The line ptr = new Pet; declares a Pet object and its address is
stored in ptr. Just for your information, the Pet object is stored in
the virtual memory.
In the three classes Pet the base class, Rat and Cat the derived
classes their speak functions are declared virtual. This means
The chorus function is called. Three arguments are passed in.
the speak function of the derived classes override the speak
*ptr is the Pet object, while ptr is the address of the Pet object.
function of the base class.

The chorus function accepts three parameters: A Pet object, a


pointer to a Pet and a reference to a Pet.

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.

chorus(*ptr, ptr, *ptr);

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:

inline double Account::GetBalance()


6.2 friend Functions
{
return balance; A friend function is in most ways similar to a global function, i.e.,
} a function that is readily accessed from the main program. The
only differences are:
inline double Account::Deposit( double Amount )
{ 1. A friend function is declared in a class.
return ( balance += Amount ); 2. A friend function has access to private (and protected)
} members of that class but you must pass in an object of that
class type as an input parameter!
inline double Account::Withdraw( double Amount )
{
return ( balance -= Amount ); Example:
}
Listing 6.1 friend function in Account class
b) Inside:
#include <iostream.h>
class Account
class Account
{ {
public: friend void PrintBalance(const Account&);
public:
Account(double bal):balance(bal) {};
// Automatic inline functions if functions
// (incl constructors) are defined within the private:
// class body: double balance;
};
Account(double init_bal) { bal = init_bal; }
double GetBalance() { return balance; } void PrintBalance(const Account& acct)
{
cout << The balance of the account is RM;
cout << acct.balance;
// Or you can explicitly specify inline:
}
inline double Deposit( double Amount )
main()
{ balance += Amount; }
inline double Withdraw( double Amount ) {
{ balance -= Amount; } Account acct(500);

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).

Assignment to objects of class type (struct, union, and class


6.3.3 operator >> types) is performed by a function named operator =. The
default behavior of this operator function is to perform a bitwise
Can you suggest an appropriate overloaded operator >> copy; however, this behavior can be modified using overloaded
function for the Complex class? operators as I have just shown.

An object of any unambiguously derived class from a given base


6.3.4 operator = class can be assigned to an object of the base class. The reverse is
not true because there is an implicit conversion from derived class
to base class but not from base class to derived class.
The assignment operator = takes in the right hand side (RHS)
value of the operator. Usually, the idea is to assign the RHS
parameter to the LHS parameter. In an opposite manner to the
insertion operator <<, the assignment operator = cannot be 6.3.5 Other operators
declared as a friend function. It must be a member function.
Most operators can be overloaded. Try coding overloaded
operators ++, += and != for yourself. Only the following
class Complex{ operators cannot be overloaded:
public: . (member selection),
Complex operator=( const Complex & )
private:
.* (pointer-to-member selection),
... :: (scope resolution),
}; ?: (conditional),
# (preprocessor symbol),

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;

6.5 3 Commonly Used Data Types 6.6 Scope Nesting


long, short, unsigned Introducing
extern accessing a global variable from a local context
The data types outlined in chapters 1.2.3, i.e., int, float, double, :: file scope operator (used in local scope)
char and bool, have certain size and precision. If you have a
large positive integer, it would probably not fit into int. In such a A program usually has several nested scopes, where it can be
case, you can explicitly append the prefix long to allocate more thought of as deeper layers. Consider the following:
memory space to store your integer:
int x = 10;
long x = 12345L; // integer assumed char c;
// Note trailing L. Small letter l is OK double y;
// Optional
void my_function(void)
long double pi = 3.141592653589793L; {
extern double y; // Using global y;
char x;
Of course, if you are sure of a short integer, like the age of a x = c;
programmer, you may not need too much memory. In this case, ::x++; // Global x increased, x = 11
simply declare a short data type replacing the int. {
float c = 3.14f;
short age = 21; extern int x;
// Global x, although char x is
If you are dealing with only positive integers, you can double the // declared in outer scope
size of your storage space by appending the prefix unsigned. x++; // Global x increased, x = 12
This is because an int variable can store both positive as well as }
}

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:

6.7.4 Others double x = 8.5;


int y;
The other statements you can place in the header including float z;
y = (int) x; // convert to int
structures, function prototypes, as well as keywords using and
z = (float) x; // convert to float
namespace.
Alternatively, write the static cast statement to perform the same
6.8 Data Type Cast conversion:

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;
}

6.8.2 Explicit Type Casting Cat Rat

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

Potrebbero piacerti anche