SDSU 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

  1. Copy Constructor - X( const X& )
    1. Copy Constructors Solve Pointer Problems!
    2. Copying - Default Copy Constructor
    3. Copying - Implicit Call to Copy Constructor
  2. Overloading Functions
    1. Overloading Operators
    2. Overloadable Operators
    3. Converting User-Defined Classes via Convert Operators
    4. Conversion by "=" operator
  3. Review of Conversion Methods
  4. Constructors, Destructors and Pointers
  5. Stack Example Revisited
    1. 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	

Copy Constructor - X( const X& )


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:

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 1Destroy 1
Call TrickyPartDestroy 2
Construct 2Destroy 1
After PartAend
After PartBDestroy 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 You just killed: 1

Copy Constructors Solve Pointer Problems!

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

Copying - Default Copy Constructor

#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

Copying - Implicit Call to Copy Constructor

#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:

Trouble :: Trouble(  const Trouble& Copy )	: test( Copy.test ) {
	Location =  new int( *(Copy.Location) ) ;  };






(More to Come!)

Overloading Functions



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

Overloading Operators


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


Overloadable Operators



	+	~	++	+=	<<=
- ! -- -= >>=
* , << /= [ ]
/ = >> %= ( )
% < == ^= ->
^ > != &= ->*
& <= && |= new
| >= || *= delete



Non-Overloadable Operators



	::	.*	.	?:




Can Not Create New Operators(Such is life)

Converting User-Defined Classes via Convert Operators


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

Conversion by "=" operator

#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

Review of Conversion Methods

class BankAccount{
	public:
		float balance;
		BankAccount( float A ) {balance = A;};
};

main() {
	BankAccount test;
	test = 100.0; }

class BankAccount{
	public:
		float balance;
		void operator  = ( float A ) {balance = A;};
};

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:
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

Constructors, Destructors and Pointers


If a class has data members which are pointers:


Stack Example Revisited


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