Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor and initialization of custom classes/objects

I could imagine this question has already been asked, but I actually could not find any fitting solution, so please excuse if this is a redundant question.

I have a custom class

class myClass_A
{
public:
    myClass_A();          // Constructor
    myFunction_A();       // Some function from Class A
};

Now I have another custom class which has a member from the type of myClass_A

class myClass_B
{
public:
    myFunction_B();       // Some function from Class B

private:
    myClass_A m_instance; // Instance of Class A
}

Now myFunction_B() wants to call the method myFunction_A() from m_instance kinda like this:

myClass_B::myFunction_B()
{
    m_instance.myFunction_A();
}

Now if I compile my code ( which is basically like the example I posted above ) it will succeed without any warnings or errors. So my questions would be:

A. Will the constructor be called in this example?

B. Can I actually call methods from an uninitialized object?

C. Assuming the constructor is not called but I can still call the methods from that object -> This means still that the members of my class are not initialized?

Sorry if these questions is kinda stupid but I feel like I am slow on the uptake right now.

like image 282
Toby Avatar asked Mar 21 '12 10:03

Toby


People also ask

Which constructor is used to declare and initialize an object from another object?

A copy constructor is a member function that initializes an object using another object of the same class.


1 Answers

These are very good and important questions.

Regarding A:

Before executing the body of your constructor, C++ generates code which automatically calls the default constructor of all aggregated (i.e. member) objects of your class. Basically, what it does is transform the following code:

class myClass_B {
public:
    myClass_B()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

into the following code:

class myClass_B {
public:
    myClass_B()
        : m_instance()
        , m_pInstance()
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

The two lines the compiler automatically inserted are called initializer list, it calls the default constructor of each aggregate object before the body of your constructor is executed. Please note that the second one, m_pInstance() calls the "default constructor of pointer" which creates an uninitialized pointer ; this is almost always not what you want. See below on how to fix that.

Now let's assume that the constructor of myClass_A has the signature myClass_A(int someNumber), i.e. it takes an argument. Then, C++ cannot automatically generate the initializer list for myClass_B as it doesn't know which number to pass myClass_A's constructor. It will throw a compiler error at you, probably complaining about a missing default constructor for myClass_A. You will have to write the initializer-list on your own, for example:

class myClass_B {
public:
    myClass_B()
        : m_instance(21)
        , m_pInstance(new myClass_A(21))
    {
        m_instance.foo();
        m_pInstance->foo();
    }
private:
    myClass_A m_instance;
    myClass_A* m_pInstance;
};

This is correct code, which calls myClass_A constructor with the value 21 for the parameter someNumber. This also shows how you correctly initialize a pointer: make it point to some newly allocated object.

Regarding B:

Unlike some others say, you can! (Try it out)

But it results in unexpected behaviour, which is not what you want. (Including that it might do what you want only when the planets are aligned correctly.) It will most probably crash but is not guaranteed to crash. This can lead you to some long debugging nights. If your compiler is smart, it might recognize this and warn you, but it will not give you an error.

Also note that for non-pointer aggregate objects which have a default constructor, the default constructor will be called and you'll be all good. The problem comes when you use builtin types or pointers. This is use of uninitialized variables, and is one of the most frequent causes for a bug. If your code does something totally weird, always check whether you initialized all your variables. It should become a reflex to put an entry in the initializer-list for any member-variable, even if it is calling the default constructor. Makes things clear.

Regarding C:

Yes. See B for details. The interesting thing is that if the method you call does not use the "this" pointer (this includes not using any attribute variable and not calling any method which uses an attribute variable), your method is guaranteed to work. What happens when you call a method on an uninitialized object is that the "this" object within the method (i.e. all attribute variables too) is random memory. The code of the method will execute but use random memory and this is what fails.

I hope this clears things up a bit.

like image 179
LucasB Avatar answered Oct 11 '22 08:10

LucasB