Page 1 of 1

Behind the scenes - How C++ Classes Work : Part I Rate Topic: ***** 2 Votes

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 27 March 2011 - 09:11 AM

*
POPULAR

Behind the scenes - How C++ Classes Work : Part I

Introduction

This three part tutorial looks at how C++ implements object classes with the aim of improving the readers understanding of the language. To achieve this, we will be using the Microsoft compiler, implementing a set of C++ classes using pure C.

This tutorial looks at class definitions and how they are implemented, tutorial II looks at the implementation of inheritance and finally tutorial III will look at pure virtual functions.

Compiling pure C using the Microsoft Compiler

When compiling the samples provided in these tutorials, it is important to remember to compile the C code using the /TC command line option. If you are using Visual Studio, then right-click on the project and select properties at the bottom of the menu. You then select Configuration Properties -> C/C++ -> Advanced -> Compile As -> Compile as C code (/TC).

Remember to switch back to compiling C++ code when you have finished.

C++ Classes and C structures

Let's begin by declaring a simple class foo in C++.

class foo {
    public:
        int    m_counter;
        float  m_value;
        char * m_buffer;
};



Now, foo contains three member fields namely an integer we have called m_counter, a float called m_value and finally a character pointer m_buffer. Assuming the size of a pointer is four bytes (i.e. a 32-bit system) and the packing is set to byte, the data of this class occupies a total of twelve consecutive bytes.

The equivalent C code for this class would be:

struct {
        int    m_counter;
        float  m_value;
        char * m_buffer;
} foo;



The structure also occupies a total of twelve consecutive bytes given the same environment as it's class counterpart. This is of course, as you would expect - there is not a scrap of difference between the two declarations.

Creation and Deletion of Dynamic Objects

We would use the new operator in C++ to create a pointer to an object of type foo, and the delete operator to destroy the object.

    foo * a = new foo();
    foo * b = new foo;
    ...
    delete b;
    delete a;



NB: There is a difference between using the expression foo * a = new foo(); and foo * b = new foo; in C++. In our C++ declaration of foo, we have not defined a constructor, so when the compiler encounters foo * a = new foo(); it generates default constructor code (the compiler generates code to ensure the twelve bytes of data are set to zero). Using the foo * b = new foo; method effectively bypasses the generation of the default constructor code so the member variables of object b will be uninitialized.

The equivalent C code is as follows:

    struct foo *a = (struct foo *)malloc(sizeof(struct foo));
    memset(a, 0, sizeof(foo));
    struct foo *b = (struct foo *)malloc(sizeof(struct foo));
    ...
    free(b );
    free(a);



C++ Constructors and C Function Equivalents

Let's add a constructor to our class, which we will use to set m_counter to 10, m_value to 3.1 and allocate a 256 byte character buffer to m_buffer.

The C++ code to achieve this would be

class foo {
    public:
        int    m_counter;
        float  m_value;
        char * m_buffer;
    public:
        foo(int counter, float value, int length); 
};

...

    foo::foo(int counter, float value, int length) 
            : m_counter(counter), m_value(value), m_buffer(new char[length]) { 
    };

...

    foo * a = new foo(10, 3.1f, 256);



The equivalent code in C would be:

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

...

    struct foo *a = (struct foo *)malloc(sizeof(struct foo));
    foo_constructor(a, 10, 3.1f, 256);



Please note: If you are using Visual Studio, even though you have set the language to C, the editor will underline the use of this as erroneous, as it is a reserved keyword in C++ but NOT in C.

These two implementations are very different, yet oddly the same. The C++ constructor implementation only has three parameters, whereas the C implementation has four. Do not be duped by the C++ shorthand notation - the compiler generates four parameters even though you have stated three. The hidden parameter is the first parameter and is referenced by the this pointer within the method itself.

C++ Member Functions and C Functions

Let's now add a routine called set_value to set the structure/member variable called m_value. In C++, we would have:

class foo {
    public:
        int    m_counter;
        float  m_value;
        char * m_buffer;
    public:
        foo(int counter, float value, int length); 
        void set_value(float value);
};

...

    void foo::set_value(float value) { 
        m_value = value;
    }



The equivalent C code would be as follows:

struct {
        int    m_counter;
        float  m_value;
        char * m_buffer;
} foo;

    void foo_set_value(struct foo * this, float value) {
        this->m_value = value;
    }



Tutorial Conclusion

So far, we have seen that the compiler generates considerably more code than we would have to provide using a non-object oriented language such as C. This, in addition to it's strong type checking mechanisms, ensures that many of the 'schoolboy' type errors associated with older programming languages such as C are alleviated.

The next tutorial concerns itself with how C++ implements inheritance, and the equivalent C code to help demystify that aspect of implementation. This tutorial may be found here.

This post has been edited by NickDMax: 01 August 2011 - 06:15 AM
Reason for edit:: fixed B caps issue.


Is This A Good Question/Topic? 20
  • +

Replies To: Behind the scenes - How C++ Classes Work : Part I

#2 PlasticineGuy  Icon User is offline

  • mov dword[esp+eax],0
  • member icon

Reputation: 281
  • View blog
  • Posts: 1,436
  • Joined: 03-January 10

Posted 27 June 2011 - 03:24 AM

Great tutorial Martyn. However I noticed you have a caps issue here:
struct foo *a = (struct foo *)malloc(sizeof(struct foo));
memset(a, 0, sizeof(foo));
struct foo *b = (struct foo *)malloc(sizeof(struct foo));
...
free(B)/>;
free(a);
I see this error all the time in the C/C++ forums :).
Was This Post Helpful? 1
  • +
  • -

#3 AZOGTHOTH  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 14-March 12

Posted 14 March 2012 - 11:08 AM

Nice topic. I didn't realize the subtlety of "new foo()" and "new foo" when a constructor hasn't been defined. Also, a deeper look into the assembly generated from the C++ and the C versions could be helpful as well (at least if one understood assembler). Thanks for the article.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1