CS 596 OODP
Constructors and Operators
[To Lecture Notes Index]
San Diego State University -- This page last updated Oct. 28, 1995

Contents of Constructors and Operators Lecture
- Copy Constructor - X( const X& )
- Copy Constructors Solve Pointer Problems!
- Copying - Default Copy Constructor
- Copying - Implicit Call to Copy Constructor
- Overloading Functions
- Overloading Operators
- Overloadable Operators
- Converting User-Defined Classes via Convert Operators
- Conversion by "=" operator
- Review of Conversion Methods
- Constructors, Destructors and Pointers
- Stack Example Revisited
- Class Templates
References
Lippman, C++ Primer, chapter 6
Explain This? - Symantec Compiler
#include <iostream.h>
class BankAccount
{ public :
float balance;
BankAccount( float amount = 0.0 );
~BankAccount();};
BankAccount::BankAccount( float amount )
{ balance = amount;
cout << "Start up an account" << balance << endl;}
BankAccount::~BankAccount()
{ cout << "Close out an account"<< balance << endl;}
BankAccount nestedFunction()
{ BankAccount you(10);
cout << "Inside nested Function\n";
return you;}
void main()
{ BankAccount me(5);
cout << "Before function call\n";
me = nestedFunction();
cout << "After function call\n";}
Output
Start up an account5
Before function call
Start up an account10
Inside nested Function
After function call
Close out an account10
Close out an account10
Explain This? Rohan CC
#include <iostream.h>
class BankAccount
{ public :
float balance;
BankAccount(float amount = 0.0);
~BankAccount();};
BankAccount::BankAccount( float amount )
{ balance = amount;
cout << "Start up an account" << balance << endl;}
BankAccount::~BankAccount()
{ cout << "Close out an account"<< balance << endl;}
BankAccount nestedFunction()
{ BankAccount you(10);
cout << "Inside nested Function\n";
return you;}
void main()
{ BankAccount me(5);
cout << "Before function call\n";
me = nestedFunction();
cout << "After function call\n";}
Output
Start
up an account5
Before function call
Start up an account10
Inside nested Function
Close out an account10
After function call
Close out an account10
Close out an account10
The Missing Constructors!
class BankAccount {
public :
float balance;
BankAccount( float amount = 0.0 );
BankAccount( const BankAccount& OldAccount );
~BankAccount(); };
BankAccount::BankAccount( float amount ){
cout << "Constructor: " << amount << endl;
balance = amount;}
BankAccount::BankAccount( const BankAccount& OldAccount ) {
balance = OldAccount.balance;
cout << "Copy Constructor: " << balance << endl; }
BankAccount::~BankAccount()
{ cout << "Close out an account: "<< balance << endl;}
BankAccount
MissingCopies( BankAccount In) {
BankAccount Out( In.balance + 100 );
cout << "End Function" << endl;
return Out;}
main() {
BankAccount Start(1);
BankAccount End = MissingCopies( Start );
cout << " the end" << endl;}
Output
Constructor: 1 Close out an account: 1
Copy Constructor: 1 the end
Constructor: 101 Close out an account: 101
End Function Close out an account: 1
Copy Constructor: 101
Close out an account: 101
The copy constructor is called in the following situations:
* Explicit initialization of one class object with another
- BankAccount First( 10 );
- BankAccount Second( First );
- BankAccount Third = First;
* Passing of object as an argument to a function
-
- BankAccount MissingCopies( BankAccount In )
- {
- BankAccount Out( In.balance + 100 );
- cout << "End Function" << endl;
- return Out;
- }
* Return of an object in a function
- BankAccount MissingCopies( BankAccount In )
- {
- BankAccount Out( In.balance + 100 );
- cout << "End Function" << endl;
- return Out;
- }
But Why not just use Normal Constructors????
Constructors, Destructors and Pointers
If a class has data members which are pointers:
- The class probably needs a destructor to deallocate the pointer
class ExamQuestion {
public :
int* data;
ExamQuestion(int amount);
~ExamQuestion();
};
ExamQuestion::ExamQuestion(int amount)
{ data = new int(amount);
}
ExamQuestion::~ExamQuestion()
{
delete data;
}
(to be continued)
Trouble Ahead
class ExamQuestion
{ public :
int* data;
ExamQuestion(int amount);
~ExamQuestion();
};
ExamQuestion::ExamQuestion(int amount)
{ data = new int(amount);
cout << "Construct " << *data << endl;}
ExamQuestion::~ExamQuestion()
{ cout << "Destroy "<< *data << endl; delete data;}
void TrickyPart(ExamQuestion why)
{
ExamQuestion PartA(2); cout << "After PartA\n";
ExamQuestion PartB = why; cout << "After PartB\n";
}
void main()
{
ExamQuestion Answer(1); cout << "Call TrickyPart\n";
TrickyPart(Answer); cout << "end" << endl;
}
Output
| Construct 1 | Destroy 1 |
| Call TrickyPart | Destroy 2 |
| Construct 2 | Destroy 1 |
| After PartA | end |
| After PartB | Destroy 1 |
Same Example Different Details
class Container {
public:
int value;
Container( int amount ) { value = amount };
~Container();
};
Container :: ~Container() {
cout << " You just killed: " << value << endl;
};
class ExamQuestion {
public :
Container* data;
ExamQuestion(int A) {data = new Container( A ); };
~ExamQuestion() { delete data;};
};
void TrickyPart(ExamQuestion why){
ExamQuestion PartB = why; cout << "After PartB\n";
}
void main()
{
ExamQuestion Answer(1); cout << "Call TrickyPart\n";
TrickyPart(Answer); cout << "end" << endl;
}
Output
Call TrickyPart
After PartB
You just killed: 1
You just killed: 1
end
class ExamQuestion
{ public :
int* data;
ExamQuestion(int amount);
~ExamQuestion();
ExamQuestion( const ExamQuestion& );};
ExamQuestion::ExamQuestion( const ExamQuestion& Hard )
{ data = new int( *(Hard.data) + 10 );
cout << "Special " << *data << endl;}
ExamQuestion::ExamQuestion(int amount)
{ data = new int(amount);
cout << "Construct " << *data << endl;}
ExamQuestion::~ExamQuestion()
{ cout << "Destroy "<< *data << endl; delete data;}
void TrickyPart(ExamQuestion why) {
ExamQuestion PartA(2); cout << "After PartA\n";
ExamQuestion PartB = why; cout << "After PartB\n";}
void main() {
ExamQuestion Answer(1); cout << "Call TrickyPart\n";
TrickyPart(Answer); cout << "end" << endl;}
Output
Construct 1 After PartB
Call TrickyPart Destroy 21
Special 11 Destroy 2
Construct 2 Destroy 11
After PartA end
Special 21 Destroy 1
Constructors, Destructors and Pointers
If a class has data members which are pointers:
- The class probably needs a destructor to deallocate the pointer
- The class probably needs a copy constructor
class ExamQuestion {
public :
int* data;
ExamQuestion(int amount);
ExamQuestion( const ExamQuestion& Copy )
~ExamQuestion();
};
ExamQuestion::ExamQuestion(int amount)
{ data = new int(amount);
}
ExamQuestion::~ExamQuestion()
{
delete data;
}
ExamQuestion::ExamQuestion( const ExamQuestion& Copy )
{
data = new int( *(Copy .data) );
}
(Not done yet)
#include <iostream.h>
class OK {
public:
int size;
OK( int initialSize = 10 );
~OK() { cout << "Kill OK: " << size << endl; };
};
OK :: OK( int initialSize ) {
size = initialSize;
cout << "New OK: " << initialSize << endl;
};
class Trouble {
public:
int Location;
OK test;
Trouble( int StartValue = 5 );
};
Trouble :: Trouble( int StartValue ) {
Location =StartValue ; test.size = 20;
};
void main() {
Trouble NotMe;
Trouble ThisIsFine = NotMe;
};
Output
New OK: 10
Kill OK: 20
Kill OK: 20
#include <iostream.h>
class OK {
public:
int* size;
OK( int initialSize = 10 );
OK( const OK& Copy );
~OK() {delete size; cout << "Kill OK " << *size << endl; };
};
OK :: OK( int initialSize ) {
size = new int( initialSize );
cout << "New OK: " << initialSize << endl; };
OK :: OK( const OK& Copy ) {
size = new int( *(Copy.size) );
cout << "New Copy OK: " << *size << endl; };
class Trouble {
public:
int Location;
OK test;
Trouble( int StartValue = 5 );
};
Trouble :: Trouble( int StartValue ) {
Location =StartValue ; *(test.size) = 20;};
void main() {
Trouble NotYet;
Trouble ThisIsFine = NotYet;};
Output
New OK: 10 Kill OK: 10
New Copy OK: 10 Kill OK: 10
Copying - Copy Constructor in Copy Constructor
class OK {
public:
int* size;
OK( int initialSize = 10 );
OK( const OK& Copy );
~OK() {delete size; cout << "Kill OK:" << *size << endl; }; };
// See previous page for OK constructors
class Trouble {
public:
int* Location;
OK test;
Trouble( int StartValue = 5 );
Trouble( const Trouble& Copy ); };
Trouble :: Trouble( int StartValue ) {
Location = new int(StartValue) ; *(test.size) = 20; };
Trouble :: Trouble( const Trouble& Copy ) {
Location = new int( *(Copy.Location) ) ; };
void main() {
Trouble Now;
Trouble ThisIsIt = Now; };
Output
New OK: 10 Kill OK: 10
New OK: 10 Kill OK: 20
Solution
class OK {
public:
int* size;
OK( int initialSize = 10 );
OK( const OK& Copy );
~OK() {delete size; cout << "Kill OK:" << *size << endl; }; };
// See previous page for OK constructors
class Trouble {
public:
int* Location;
OK test;
Trouble( int StartValue = 5 );
Trouble( const Trouble& Copy ); };
Trouble :: Trouble( int StartValue ) {
Location = new int(StartValue) ; *(test.size) = 20; };
Trouble :: Trouble( const Trouble& Copy ) : test( Copy.test ) {
Location = new int( *(Copy.Location) ) ; };
void main() {
Trouble Now;
Trouble ThisIsIt = Now; };
Output
New OK: 10 Kill OK: 10
New OK: 10 Kill OK: 20
Constructors, Destructors and Pointers
If a class has data members which are pointers:
- The class probably needs a destructor to deallocate the pointer
- The class probably needs a copy constructor
- The copy constructor should initialize the object data members in the
initialization phase
Trouble :: Trouble( const Trouble& Copy ) : test( Copy.test ) {
Location = new int( *(Copy.Location) ) ; };
(More to Come!)
#include <iostream.h>
class Test
{
public:
whichOne(float a);
whichOne(int b);
};
Test::whichOne(float a)
{
cout << "In A\n";
}
Test::whichOne(int b)
{
cout << "In B\n";
}
main()
{
Test me;
me.whichOne(1.1);
me.whichOne(2);
}
#include <iostream.h>
class BankAccount
{
public :
float balance;
BankAccount(float amount = 0.0);
BankAccount& operator+(const BankAccount a);
};
BankAccount& BankAccount::operator+(const BankAccount a)
{
BankAccount *newAccount;
newAccount = new BankAccount;
newAccount->balance = balance + a.balance;
return newAccount;
}
BankAccount::BankAccount(float amount)
{
balance = amount;
}
main()
{
BankAccount me(10), you(200), us;
us = me + you;
cout << us.balance << "\n";
}
+ ~ ++ += <<=
- ! -- -= >>=
* , << /= [ ]
/ = >> %= ( )
% < == ^= ->
^ > != &= ->*
& <= && |= new
| >= || *= delete
Non-Overloadable Operators
:: .* . ?:
Can Not Create New Operators(Such is life)
#include <iostream.h>
class BankAccount{ public: float balance; };
class WeirdStuff{
public:
operator float();
operator BankAccount() ;
};
WeirdStuff::operator float() {return 1;};
WeirdStuff::operator BankAccount() {
BankAccount* localAccount = new BankAccount;
localAccount->balance =10 ;
return *localAccount;
};
main()
{
WeirdStuff thisIs;
BankAccount What;
float a;
float b = thisIs;
a = 2 * thisIs;
What = thisIs;
cout << a << "\n" // prints 2
<< b << "\n" // prints 1
<< What.balance; // prints 10
}
More Conversion
#include <iostream.h>
class BankAccount{ public: float balance; };
class WeirdStuff{
public:
operator float();
operator BankAccount() ;
};
WeirdStuff::operator float() {return 1;};
WeirdStuff::operator BankAccount() {
BankAccount* localAccount = new BankAccount;
localAccount->balance =10 ;
return *localAccount;
};
void ImplicitConversion( float argument ) {
cout << argument << endl;
};
void main()
{
WeirdStuff thisIs;
BankAccount What;
ImplicitConversion( thisIs ); // OK
ImplicitConversion( What ); // Compile Error
}
#include <iostream.h>
class BankAccount{
public:
float balance;
void operator = ( float NewValue );
};
void BankAccount :: operator = ( float NewValue ) {
balance = NewValue;
};
void main() {
BankAccount goodCustomer;
goodCustomer.balance = 100;
goodCustomer = 200;
cout << goodCustomer.balance << endl;
};
Output
200
Which One is Used?
#include <iostream.h>
class BankAccount{
public:
float balance;
BankAccount( float StartValue );
void operator = ( float NewValue );
};
void BankAccount :: operator( float NewValue ) {
balance = NewValue;
cout << "= operator selected" << endl;
};
BankAccount :: BankAccount = ( float StartValue ) {
balance = StartValue;
cout << "Constructor selected" << endl;
};
void main() {
BankAccount goodCustomer( 100 );
goodCustomer = 200;
cout << goodCustomer.balance << endl;
};
Output
Constructor selected
= operator selected
200
- Convert into a class via constructor
class BankAccount{
public:
float balance;
BankAccount( float A ) {balance = A;};
};
main() {
BankAccount test;
test = 100.0; }
- Convert into a class via "=" operator
class BankAccount{
public:
float balance;
void operator = ( float A ) {balance = A;};
};
- Convert out of a class via convert operator
class BankAccount{
public:
float balance;
operator float() };
main() {
float Result
BankAccount test;
test.balance = 100.0;
Result = test;
}
Word to the Not So Wise
In these notes poor programming style is occasionally used:
- Public data members
- Inline functions
- No constructors
class BankAccount{
public:
float balance;
void operator = ( float A ) {balance = A;};
};
main() {
BankAccount test;
test = 100.0; }
class ExamQuestion
This is done mainly to keep examples to one page of 18 point text
The goofy names hopefully make it easier to sit through lectures
Please do not copy this in your programs
Assignment is not Initialization
class ExamQuestion
{ public :
int* data;
ExamQuestion(int amount);
ExamQuestion(const ExamQuestion&);
~ExamQuestion();
};
ExamQuestion::ExamQuestion(const ExamQuestion& Hard)
{ data = new int(*(Hard.data) +10);
cout << "Special " << *data << endl;
}
ExamQuestion::ExamQuestion(int amount)
{ data = new int(amount);
cout << "Construct " << *data << endl;
}
ExamQuestion::~ExamQuestion()
{ cout << "Destroy "<< *data << endl; delete data;
}
void main()
{
ExamQuestion Start(1);
ExamQuestion Copy(2);
Copy = Start;
}
Output
Construct 1
Construct 2
Destroy 1
Destroy 1
Use the "=" Operator
class ExamQuestion {
public :
int* data;
ExamQuestion(int amount);
ExamQuestion(const ExamQuestion&);
~ExamQuestion();
ExamQuestion& operator=(const ExamQuestion&);
};
ExamQuestion&
ExamQuestion :: operator=( const ExamQuestion& In )
{
if (this != &In) { // beware of a = a
delete data;
data = new int( *(In.data) +15 );
cout << "= " << *data << endl;
}
return *this;
}
void main()
{
ExamQuestion Start(1);
ExamQuestion Copy(2);
Copy = Start;
}
Output
Construct 1
Construct 2
= 16
end
Destroy 16
Destroy 1
If a class has data members which are pointers:
- The class probably needs a destructor to deallocate the pointer
- The class probably needs a copy constructor
- The copy constructor should initialize the object data members in the
initialization phase
- The class probably needs an = operator to properly copy the pointers
class Stack
{
friend ostream& operator<<( ostream& , Stack& );
public:
Stack( int StartSize = 10 );
Stack( const Stack& );
~Stack( );
Stack& operator=( const Stack& );
int isEmpty( ) const;
int isFull( ) const;
void push( int item );
float pop( );
private:
float* stack;
int nextFreeLocation;
int size;
void grow( );
void copyStack( const Stack& );
};
Stack Example Revisited - New Operations
void Stack :: copyStack( const Stack& oldStack )
{
size = oldStack.size;
stack = new float[size];
for ( int K = 0; K < oldStack.nextFreeLocation; K++ )
push( oldStack.stack[K] );
}
Stack :: Stack( const Stack& oldStack )
{
nextFreeLocation = 0;
size = 0;
copyStack( oldStack );
}
Stack& Stack :: operator=( const Stack& oldStack )
{
if ( this != &oldStack ) {
delete stack;
copyStack( oldStack );
}
return *this;
}
Stack Example Revisited - Previous Operations
Stack :: Stack( int StartSize )
{
size = StartSize;
stack = new float[StartSize];
nextFreeLocation = 0;
}
Stack :: ~Stack( )
{
delete stack;
}
void Stack :: grow( )
{
float* oldStack = stack;
int oldSize = size;
size += size/2 + 1;
stack = new float[ size ];
assert( stack != 0 ); // Why not set_new_handler?
// copy elements of old array into new
for ( int ix = 0; ix < oldSize; ++ix )
stack[ ix ] = oldStack[ ix ];
delete oldStack;
}
int Stack :: isEmpty( ) const
{
if ( nextFreeLocation == 0 ) return 1;
else return 0;
}
Stack Example Revisited - Previous Operations
int Stack :: isFull( ) const
{
return 0;
}
void Stack :: push( int item )
{
if ( nextFreeLocation == size ) grow( );
stack[nextFreeLocation++] = item;
}
float Stack :: pop( )
{
return stack[--nextFreeLocation];
}
ostream& operator<<( ostream& output, Stack& aStack ) {
output << "Stack( " ;
output << aStack.stack[aStack.nextFreeLocation - 1];
for ( int K = aStack.nextFreeLocation - 2; K>= 0; K-- )
output << "," << aStack.stack[K];
output << " )";
return output;
}
void main( )
{
Stack books( 11 );
for ( int k = 0; k < 200; k++ )
books.push( k );
cout << books << endl;
};