Rajinder Yadav - Windows/Linux C++ Development Tools & Resources :Design, Code, Test, Deploy

C++ Big Three Rules Explained

Author: Rajinder Yadav
Date: Sept 22, 2007

When you develop and build a trivial C++ class, the C++ Language compiler by default will provide the following three for you:

The default ctor allows an object to get created and initialized. The default dtor allows the object to perform trivial cleanup, and finally the default copy ctor allows an object to clone itself. One thing to keep in mind is that the type of copying done by the default copy ctor is a shallow copy, this type of copy simply does a member to member copy. If there are referenced objects or resources, then the reference to them is simply copied, that is the referenced object or referenced resource does not get cloned. The other non-trivial coping that can be done is called a deep copy which is something that the developer is required and responsible to provide with respect to cloning referenced objects and referenced resources.

The follow code example doesn't do much, there is no output but all the above 3 rule have been implement.

#include <iostream>
#include <string>
using namespace std;

class 
Student
{
}
;

int 
main(int argc, char* argv[])
{
   Student suggy
;
   return 
0;
}

To see that this is indeed happening we can create a base class for Student called Person to output messages for us.

#include <iostream>
#include <string>
using namespace std;

class 
Person
{
public:
   Person() {
      cout << 
"[ctor] creating person object" << endl;
   
}

   ~Person() {
      cout << 
"[dtor] destroying person object" << endl;
   
}

   Person(Person& obj) {
      cout << 
"[copy ctor] cloning person object" << endl;
   
}
}
;

class 
Student : public Person
{
}
;

int 
main(int argc, char* argv[])
{
   Student suggy
;
   
Student suggyClone( suggy );
   
return 0;
}
OUTPUT
[ctor] creating person object
[copy ctor] cloning person object
[dtor] destroying person object
[dtor] destroying person object

Resource Acquisition is Resource Allocation!

Now we're going to take a look at what can go wrong when the Big Three Rules are not observed. Earlier I talked about how C++ does a good job of providing us with a default copy ctor. I also mentioned that the type of copying that take place is called a shallow copy. In the case of class Student which is non-trivial because it contains a pointer to class Book and does the allocation for this object. What happens here and goes horribly wrong is that the default copy ctor for class Student simply copies the pointer to the Book object. When the Student object is destroyed, it will destroy the Book object. Likewise the copied Student object tries to destroy the small Book object and we get a crash because of a double delete to the same Book object!
#include <iostream>
#include <string>
using namespace std;

class 
Person
{
public:
   Person() {
      cout << 
"[ctor] creating person object" << endl;
   
}

   ~Person() {
      cout << 
"[dtor] destroying person object" << endl;
   
}

   Person(Person& obj) {
      cout << 
"[copy ctor] cloning person object" << endl;
   
}
}
;

class 
Book 
{
   std::
string m_strTitle;
   
std::string m_strISBN;

public
:
   Book( std::
string strTitle, std::string strISBN ) :
      m_strTitle( strTitle ), 
      m_strISBN( strISBN )
   { 
      cout << 
"[book] purchased" << endl
   
}

   ~Book() { cout << 
"[book] sold" << endl}
}
;

class 
Student : public Person
{
   Book* m_pBook
;

public
:
   Student() : m_pBook( 
) {
   }
   virtual ~Student() {
      
delete m_pBook;
   
}
   
void buyBook( std::string strTitle, std::string strISBN ) {
      m_pBook 
= new Book( strTitle, strISBN );
   
}
}
;

int 
main(int argc, char* argv[])
{
   Student suggy
;
   
suggy.buyBook( "Big3Rules""237-3874-1" );

   
Student suggyClone( suggy );
   return 
0;
}
OUTPUT
[ctor] creating person object
[book] purchased
[copy ctor] cloning person object
[book] sold
[dtor] destroying person object
[book] sold

To correct this problem, a copy ctor would need to implemented in both the Student and Book classes to perform a deep copy as shown below.

class Book 
{
   std::
string m_strTitle;
   
std::string m_strISBN;

public
:
   
// copy ctor
   
Book( Book& objBook ) {
      
this->m_strTitle objBook.m_strTitle;
      this
->m_strISBN  objBook.m_strISBN;
   
}
   Book( std::string strTitle, std::string strISBN ) :
      m_strTitle( strTitle ), 
      m_strISBN( strISBN )
   { 
      cout << 
"[book] purchased" << endl
   
}

   ~Book() { cout << 
"[book] sold" << endl}
}
;

class Student : public Person
{
   Book* m_pBook
;

public
:
   
//copy ctor
   
Student( Student& obj ) {
      m_pBook 
= new Book( *obj.m_pBook );
   
}
   Student() : m_pBook( ) {
   }
   virtual ~Student() {
      
delete m_pBook;
   
}
   
void buyBook( std::string strTitle, std::string strISBN ) {
      m_pBook 
= new Book( strTitle, strISBN );
   
}
}
;
OUTPUT
[ctor] creating person object
[book] purchased
[copy ctor] cloning person object
[book] sold
[dtor] destroying person object
[book] sold
[dtor] destroying person object

Now the program runs fine, the cloned person object get deleted correctly along with the cloned book object.

Home

Copyright © 2007 Rajinder Yadav, All rights reserved