Introduction
In our previous tutorial, we looked at a simple C++ class, and the implementation of a constructor and member function by using pure C as a means of highlighting the compiler code that is generated 'behind the scenes'. This tutorial moves us forward and looks at how the C++ compiler generated code to handle inheritance. So without further ado, let's dive straight in!
Class Inheritance
Using the foo class from the previous tutorial as a base class, we are going to introduce a new class called fooed that inherits from foo. Here are the two classes without any methods.
class foo {
public:
int m_counter;
float m_value;
char * m_buffer;
};
class fooed : public foo {
public:
double m_double;
};
I have chosen to specify one extra data member in the derived fooed class for simplification. The equivalent C code as structures would be:
typedef struct {
int m_counter;
float m_value;
char * m_buffer;
} foo;
typedef struct {
foo m_foopart;
double m_double;
} fooed;
As in the previous tutorial, both the class fooed and the structure fooed have exactly the same size of twenty bytes (a double field being eight bytes in length).
Creation and Deletion of Dynamically Inherited Objects
Let's now take a look at the C++ code for creating and deleting a fooed object. The small program below will do nicely.
#include <Windows.h>
class foo {
public:
int m_counter;
float m_value;
char * m_buffer;
public:
foo(int counter, float value, int length);
~foo();
};
foo::foo(int counter, float value, int length)
: m_counter(counter), m_value(value), m_buffer(new char[length]) {
};
foo::~foo() {
delete m_buffer;
}
class fooed : public foo {
public:
double m_double;
public:
fooed(int counter, float value, int length, double big_value);
~fooed();
};
fooed::fooed(int counter, float value, int length, double big_value) : foo(counter, value, length), m_double(big_value) {
}
fooed::~fooed() {
}
int main(int argc, char **argv) {
fooed * a = new fooed(10, 3.1f, 256, 20.0);
delete a;
}
What happens behind the scenes in this example is
1. A call is made to the new operator to allocate the twenty bytes of member data
2. A call is made to the fooed constructor which in turn calls the foo constructor
3. A call is made to the fooed destructor which in turn calls the foo destructor
4. A call is made to the delete operator within the foo destructor for the m_buffer data member
5. A call is made to the delete operator within main to delete the fooed object
The equivalent code in pure C would therefore be:
#include <Windows.h>
typedef struct {
int m_counter;
float m_value;
char * m_buffer;
} foo;
void foo_constructor(foo * this, int counter, float value, int length) {
this->m_counter = counter;
this->m_value = value;
this->m_buffer = (char *)malloc(length);
}
void foo_destructor(foo * this) {
free(this->m_buffer);
}
typedef struct {
foo m_foopart;
double m_double;
} fooed;
void fooed_constructor(fooed * this, int counter, float value, int length, double big_value) {
foo_constructor((foo *)this, counter, value, length);
m_double = big_value;
}
void fooed_destructor(fooed * this) {
foo_destructor((foo *)this);
}
int main(int argc, char **argv) {
fooed * a = (fooed *)malloc(sizeof(fooed));
fooed_constructor(a, 10, 3.1f, 256, 20.0);
fooed_destructor(a);
free(a);
}
Tutorial Conclusion
In this tutorial, we can see that the C++ compiler generates even more code behind the scenes than we saw in the previous tutorial.
The next tutorial concerns itself with how C++ implements pure virtual functions, and the equivalent C code to help demystify that aspect of implementation. This tutorial may be found here.
This post has been edited by Martyn.Rae: 27 March 2011 - 09:17 AM






MultiQuote


|