 |
 |
|
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:
- default ctor (constructor)
- default dtor (destructor)
- default copy ctor
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( 0 ) {
}
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( 0 ) {
}
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