We’re going to think a little bit about constructing and destroying objects in C++. Let’s say we have three very simple classes, Base
, Derived
and DerivedAgain
.
class Base
{
public:
Base()
{
std::cout << "Base Constructor" << std::endl;
}
~Base()
{
std::cout << "Base Destructor" << std::endl;
}
};
class Derived : public Base
{
public:
Derived()
{
std::cout << "Derived Constructor" << std::endl;
}
~Derived()
{
std::cout << "Derived Destructor" << std::endl;
}
};
class DerivedAgain : public Derived
{
public:
DerivedAgain()
{
std::cout << "DerivedAgain Constructor" << std::endl;
}
~DerivedAgain()
{
std::cout << "DerivedAgain Destructor" << std::endl;
}
};
Now suppose we run the following code,
int main()
{
DerivedAgain derivedAgainObject;
return 0;
}
The output will be:
Base Constructor
Derived Constructor
DerivedAgain Constructor
DerivedAgain Destructor
Derived Destructor
Base Destructor
Why is this? Well, to construct the DerivedAgain
object, first we must construct aDerived
object and to construct a Derived
object, first we must construct a Base
object. Similarly, when the object goes out of scope, to destroy it, first the DerivedAgain
destructor is called, then the Derived
destructor and then the Base
Destructor. So, when creating an object we step down through the inheritance hierarchy, and when destroying it we step back up through the hierarchy.
We can call these constructors explicitly. Indeed the above code is exactly quivalent to:
class Base
{
public:
Base()
{
std::cout << "Base Constructor" << std::endl;
}
~Base()
{
std::cout << "Base Destructor" << std::endl;
}
};
class Derived : public Base
{
public:
Derived() : Base()
{
std::cout << "Derived Constructor" << std::endl;
}
~Derived()
{
std::cout << "Derived Destructor" << std::endl;
}
};
class DerivedAgain : public Derived
{
public:
DerivedAgain() : Derived()
{
std::cout << "DerivedAgain Constructor" << std::endl;
}
~DerivedAgain()
{
std::cout << "DerivedAgain Destructor" << std::endl;
}
};
Let’s change things up a little bit, suppose we have the following situation.
class Derived : public Base
{
public:
Derived()
{
std::cout << "Derived Constructor" << std::endl;
}
Derived(int i)
{
std::cout << "Derived Constructor With Parameter" << std::endl;
}
~Derived()
{
std::cout << "Derived Destructor" << std::endl;
}
};
class DerivedAgain : public Derived
{
public:
DerivedAgain() : Derived(5)
{
std::cout << "DerivedAgain Constructor" << std::endl;
}
~DerivedAgain()
{
std::cout << "DerivedAgain Destructor" << std::endl;
}
};
Now we are explicitly calling a non-default constructor from the Derived
class. All the other implicit calls to default constructors remain the same. When we run the main function as before, we will see:
Base Constructor
Derived Constructor with parameter Constructor
DerivedAgain Constructor
DerivedAgain Destructor
Derived Destructor
Base Destructor
You cannot however do something like this:
class DerivedAgain : public Derived
{
public:
DerivedAgain() : Base()
{
std::cout << "DerivedAgain Constructor" << std::endl;
}
~DerivedAgain()
{
std::cout << "DerivedAgain Destructor" << std::endl;
}
};
Here, we are trying to call the Base constructor from the DerivedAgain class. This will not compile, we can only call constructors from the classes that we directly inherit from.
So far we have only looked at constructors and destructors for local objects, that is, without using the new
keyword. In part 2, we will look at what happens when we construct and destroy objects dynamically.