Professional Documents
Culture Documents
Part 1
- constructor, destructor, copy constructor, deep copy,
assignment operator
-Tapestry Section 12.3.5 and partially 12.3.3 (a bit limited)
- Horton parts of Chapter 7 and 8 (detailed discussion)
- operator overloading
-Horton pp.446... (a bit better)
- Appendix E and 9.4 in Tapestry
Part 2
- iterators and friend classes, using static variables in
classes
Part 3
sharing a variable among several objects of the
same class (reference variables, pointers)
Constructor
Constructors are special member fuctions of a class.
When an object of a class is created, C++ calls the constructor for that
class.
Properties:
Constructors have the same name as the class.
Constructors do not return any values
Constructors are invoked first when a class is initialized. Any
initializations for the class members, memory allocations are done
at the constructor.
Constructors may or may not take parameters
A constructor with no parameter is called default constructor
Default Constructor
Constructor with no parameter.
Node::Node()
{
. . . //initialization code
}
myNode();
myOtherNode (10);
* ptr = new Node();
* ptr2 = new Node(10, ptr);
* ptr3 = new Node (100);
3
Default Constructor
What happens if the programmer does not define any constructor (with
and without parameters)
The compiler attempts to generate a default constructor
Not visible to the programmer
When a object is created this default constructor is invoked
automatically
This compiler-generated constructor allocates memory for the
class private members and calls default constructors of them (if
applicable)
Constructor Example
//point.h
// default constructor
#ifndef POINT_H
Point::Point() {
#define POINT_H
x = 0;
class Point {
y = 0;
public:
}
Point(); //default constructor
Point(int xx, int yy);
// constructor with parameters
// constructor with parameters
Point::Point(int xx, int yy) {
. . .
x = xx;
private:
y = yy;
int x;
}
int y;
Instead of these two constructors you can have
};
#endif
Point::Point(int xx=0, int yy=0) {
x = xx;
y = yy;
}
}
Examples
// default constructor
Point::Point() : x(0), y(0)
{
}
//Alternative default constructor implementation
Point::Point() : x(0)
{
y = 0;
}
// constructor with parameters
Point::Point(int xx, int yy) : x(xx), y(yy)
{
}
6
Destructor
Tapestry pp. 624-625
Each class should also have a destructor which should take care of
returning any unreturned memory.
The destructor has name ~classname() (put ~ character before the class
name)
No parameters
This is in the class definition under public
class LinkedList
{
private:
node * head;
int size;
public:
LinkedList ();
~LinkedList (); //destructor
void printList();
void addToBeginning(int n);
//more functions here
};
Destructor
Destructor function is conceptually the inverse of constructor
They are called when an object is destroyed. Typical job is to return
dynamic memory allocated (using new, malloc, calloc) to the heap.
You do not need to take an action for automatic (regular) data members
(i.e. the ones allocated from the runtime stack). They are automatically
deallocated.
LinkedList::~LinkedList ()
{
node * ptr = head;
while (ptr != NULL)
{
node * temp = ptr->next;
delete ptr;
ptr = temp;
}
}
8
Destructor
The destructor is called automatically when the object goes out of scope.
For local (automatic) objects, when the block in which the object is
created finishes
For static or global objects, when the program finishes
If you do not provide a destructor for your class, the compiler provides one, but
thats just an empty function, so it does not serve any purpose beside
providing a required function.
This may be enough when your class object does not use any heap memory.
For instance a Date class that has only int, day, month, year member
variables does not need anything specific in its destructor.
You can also explicitly call a destructor, but this is not so needed in practice.
LinkedList mylist;
. . .
mylist.~LinkedList();
//mylist destroyed
9
Destructor
int CountUnique (ifstream & input)
{
string word;
LinkStringSet set;
//similar to a linkedlist of strings; also has a size field
// LinkStringSet is a Tapestry class
while (input >> word) //by inserting words into a set which
set.insert(word);
//skips the insertion if the element is already in the set
//we can count how many elements (words) are unique
return set.size();
}
What happens to set when this function returns?
At the end of the function, destructor of LinkStringSet class is called on set since
set goes out of scope.
10
Destructor
Since the compiler makes this call automatically, it creates a a dummy
destructor for each class that does not contain a destructor. So,
if the programmer has not supplied a destructor, the one which is
created by the compiler is called
This basically prevents the program from not compiling, but as a dummy
function, it is not guaranteed to do the right thing (e.g. doesnt free all
memory)
list2 = list1;
//assign list1 to list2
list3 = list1 + list2; //addition of lists, with suitable meaning
linkedlist list4(list1); //construct list4 from list1
First two are typically operators that you have come to take for granted for built-in
types.
Now you are asked to give the same functionality (as long as meaningful for your
class).
12
Copy Constructor
Special constructor called when an object is first declared and initialized from
another object of the same type
Example:
LinkedList list3(list);
Date today;
Date tomorrow (today+1);
Date yesterday (today-1);
Date yesterday = tomorrow;
Syntax Examples for the Copy constructor definition (also add a prototype to class definiton):
LinkedList::LinkedList (const LinkedList & copy)
{
//copy constructor code comes here
}
Date::Date (const Date & d)
{
//copy constructor code comes here
}
//copy constructor
Copy Constructor
The copy constructor in the previous slide makes "shallow copy" ; only the
private data of a class instance is copied.
- In the case of a linked list, this would be the head pointer, NOT the whole
list
- The new list share the same memory as the old one!
node * head
int size = 3
list2
node * head
int size = 3
15
Copy Constructor
For every class, there is a default copy constructor (compiler provided).
- Compiler provides this if you do not declare a copy constructor.
- Work exactly as shallow copy mentioned before
Simply copies the value of each instance variable (e.g. head, size for
linked lists) from one object to the other.
The entire list is not copied.
Shallow copy may not what we want. It may cause several problems.
- See next slide and let's see linkedlistextra class and demo
(linkedlistextra.cpp) for example cases.
- Also try that we do not actually need to define a copy constructor for shallow
copy, default copy constructor would do the same job.
It is always better to use const reference parameters instead of value
parameters for class types. In value parameter passing implicitly
copy constructor is invoked. Using const reference parameter avoids
this and unexpected effects of default/shallow copy constructors and
destructors.
We want to be able to do this! But the program crashes since the list is
deleted from the memory.
LinkedList list2(list1);
list1
node * head
int size = 3
list2
node * head
int size = 3
//first generate the first clone node and connect to head of clone
node * headClone = new node (head->info, NULL);
node * ptr = head->next; //second node in orig.
node * ptrClone = headClone; //to track the clone list
while (ptr != NULL)
{
ptrClone->next = new node (ptr->info, NULL);
ptr = ptr->next;
Let's trace this
ptrClone = ptrClone->next;
}
on the board for
return headClone;
a sample case
header
list1.myFirst
Operator Overloading
Overloading in C++
Overloading is the practice of supplying more than one definition for a given function name.
The compiler picks the appropriate version of the function or operator based on the arguments
with which it is called.
In case of an ambiguity, syntax error occurs
Both free and member functions can be overloaded
double max( double d1, double d2 )
{
if ( d1 > d2 )
return d1;
else
return d2;
}
int max( int i1, int i2 )
{
if ( i1 > i2 )
return i1;
else
return i2;
}
int main()
{
int
i = max( 12, 8 ); //calls second one
double d = max( 17.4, 32.9 ); //calls first one
}
Assignment Operator
Lets overload (make it work for your own class) the assignment operator.
Usage: list2 = list1;
Compiler interprets this as: list2.operator=(list1);
Syntax for function header for operators
Return_Type classname::operator Operator_Symbol (parameters)
Syntax Examples for function headers that define operators:
const myclass & myclass ::operator = (const myclass & rhs)
const linkedlist & linkedlist::operator = (const linkedlist & list1)
this
When you apply a member function to an object
say calling member function func on object obj
obj.func()
the program invisibly executes
this = & obj;
before the function starts to execute. Thus,
this is a pointer to the object on which the function is being
executed (in our example obj).
therefore, the function can use *this to refer to the current object
on which the function is executing
28
class LinkedList
{
private:
node * head;
int size;
public:
LinkedList ();
LinkedList (const LinkedList &); //copy constructor
~LinkedList ();
//destructor
void printList() const;
void addToBeginning(int n);
void deleteList ();
const LinkedList & LinkedList::operator = (const LinkedList &
rhs);
node * createClone () const;
};
list1.deleteList();
Linksetdemo.cpp
int main()
{
LinkStringSet a,b;
a.insert("apple");
a.insert("cherry");
cout << "a : "; Print(a); //cherry apple 2
b = a;
cout << "b : "; Print(b); //cherry apple 2
a.clear();
cout << "a : "; Print(a); //
0
cout << "b : "; Print(b); //cherry apple 2
//as intended with =, provided by deepcopy in linkstringset.cpp
return 0;
}
ClockTime Class
class ClockTime
{
public:
ClockTime();
ClockTime(int secs, int mins, int hours);
int Hours()
const;
int Minutes()
const;
int Seconds() const;
string tostring() const;
// returns # hours
// returns # minutes
// returns # seconds
// converts to string
ClockTime Class
ClockTime::ClockTime (int secs, int mins, int hours)
: mySeconds(secs), myMinutes(mins), myHours(hours)
// postcondition: all data fields initialized
{
Normalize();
}
void ClockTime::Normalize()
{
myMinutes += ...
mySeconds ...
myHours += ...
myMinutes ...
}
ClockTime Class
ClockTime::ClockTime (int secs, int mins, int hours)
: mySeconds(secs), myMinutes(mins), myHours(hours)
// postcondition: all data fields initialized
{
Normalize();
}
void ClockTime::Normalize()
{
myMinutes += mySeconds/60;
mySeconds %= 60;
myHours
myMinutes
}
+= myMinutes/60;
%= 60;
Overloading Operators
Now lets overload the operator >=. First notice that we should be able to use the
>= operator with clocktimes in 3 ways:
ClockTime c1, c2;
if ( c1 >= c2)
if ( c1 >= 1)
if ( 1 >= c1)
...
if ( c1 >= 1)
calls c1.operator>=(1) : let this mean "is the time more than 1 hr"
if ( 1 >= c1)
cannot call 1.operator>=(c1) since 1 is a constant, so
of the operator>= must be a free function
this version
if ( c1 >= 1)
calls c1.operator>=(1) : let this mean "is the time more than 1 hr"
if ( 1 >= c1)
cannot call 1.operator>=(c1) since 1 is a constant, so
of the operator>= must be a free function
bool operator>=(const int & lhs, const ClockTime & rhs)
{ ClockTime temp(0, 0, lhs);
return temp >= rhs;
}
this version
Operator +=
Usage:
c1 += ct;
Interpreted as:
c1.operator+=(ct);
Operator +=
Usage:
c1 += ct;
Interpreted as:
c1.operator+=(ct);
Operator +=
why operator += returns *this?
same arguments as for the assignment operator (=)
in case someone uses: e.g.
c1 = c2 += c3;
c1 += c2 += c3
or
ostream &
operator <<
ClockTime
operator +
bool
bool
bool
bool
bool
bool
operator ==
operator !=
operator <
operator >
operator <=
operator >=
However, most of these particular functions can - or even should be - member functions
since they have a left-hand-side which is a clocktime object, they can be
member functions (which is better)
The only exception here is the << operator which has to be a free function since
lhs is not a clocktime object
When we are writing versions of the overloaded operators which has constants on the
left-hand-side, they have to be free functions
Operator +
Usage:
c = c1 + c2;
Interpreted as:
c = (operator+(c1,c2));
Operator +
The free function operator+ can be implemented using the previously defined operator+= of the
class:
ClockTime operator+ (const ClockTime & lhs, const ClockTime & rhs)
// postcondition: return lhs + rhs (normalized for myMinutes, mySeconds)
{
ClockTime result(lhs);
//uses the default (compiler generated) copy constructor
result += rhs;
//uses the previously defined operator+=
return result;
}
Why is the return type just ClockTime but not a reference as in = and +=?
We return result, which is a local object. Returning reference of a local object/variable is very
problematic since the scope ends after the function. In the current form of ClockTime class this may
not cause a problem but if we had a destructor for ClockTime, that would be a problem. Let's simulate
this case by having a destructor that initializes private data members.
Why did we need result? Why not simply have only return lhs+= rhs?
Because you cannot change the value of a const reference parameter, compiler does not allow this.
Friend functions
A friend function is used for accessing the non-public members of a class.
However, you have to add a prototype for the function in the class declaration by
putting the keyword friend at the beginning
Use friend keyword in the prototype only, not in the function definition.
When a class is declared as a friend, the friend class has access to the private data
of the class that made this a friend.
Will see friend classes later
The function is invoked without the use of an object: objects are passed to it as
arguments.
49
Adds lhs hours to rhs and returns the new clock value.
Let's implement this as a free function. Actually ClockTime class has enough
accessors for implementation, but let's do it by accessing private data
members. We add the following function to clockt.cpp
ClockTime operator + (int lhs, const ClockTime& rhs)
{
ClockTime temp (rhs.mySeconds, rhs.myMinutes, rhs.myHours + lhs);
return temp;
}
Since we use private data members of the class, this function should be a
friend function. To do so also add the following prototype to clocktime.h
within the class declaration
friend ClockTime operator + (int lhs, const ClockTime& rhs);
{
os << ct.tostring();
return os;
}
string ClockTime::tostring() const
{
ostringstream os;
//to use a string as a stream
os.fill('0');
//unused spaces on the left fill with 0s
os << Hours() << ":" << setw(2) << Minutes() << ":"
<< setw(2) << Seconds();
return os.str();
}
1:09:59
{
os << ct.tostring();
return os;
}
What is the difference between these and using just the ClassName as
the return type (value return)?
ClassName FunctionName (Parameters)
Using just the class name (value return) generates a copy of the return
value when returning
This invokes copy constructor (user defined or if it does not exist default one)
This is risky if not implemented (shallow copy problem)
And it is ineffient to call the copy constructor for this purpose
55