10

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.

Toby
  • 3,815
  • 14
  • 51
  • 67

5 Answers5

8

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.

LucasB
  • 3,253
  • 1
  • 28
  • 31
  • Wow, thank you for the detailled answer. That pretty much covers it all I wanted to know!!! – Toby Mar 21 '12 at 10:57
  • You're welcome. Once you get that, you will be able to find (or avoid) a _lot_ of bugs much quicker :) – LucasB Mar 21 '12 at 11:51
4

Will the constructor be called in this example?

Yes
The constructors for myClass_B will have been called after call to the constructor myClass_A() is complete.
Usually You would initialize the myClass_B object in constructor of my_class_A using a
Member Initializer List.

Can I actually call methods from an uninitialized object?

When an object is created it's constructor will always be called, Unless you construct an object you cannot call any method on it. This is the very purpose of constructors to create an object and initialize it.
So If you have a object it is never uninitialized.

If you are referring to pointers, then pointers by themselves are not objects they can point to a valid or invalid object. Derefencing a pointer(for calling a member function or whatever) not pointing to a valid object will result in Undefined Behavior.

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?

Answer to the second Question answers this.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
2

Class members are automatically initialized by the compiler using the default constructor, unless you specify otherwise in your owning class's constructor initialization list. So yes, the constructor will be called.

You can test this for yourself by either:

  • having the constructor print a message (which you will see), or
  • making the constructor private, or declaring a constructor with arguments so that the default constructor is no longer automatically generated (the compiler will refuse to compile, since it can no longer default-construct the aggregate object)

If you do call methods or access members of an object that has not been initialized or one that has already been destructed (both scenarios are possible to engineer) then you have undefined behavior.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Okay I tried as you said and now I get it! Stilla question: You say if I call methods of an object hat has not been initialized I have undefined behavior - okay I get that - but how do I get an uninitiliazed object? I mean it seems it always calls the constructor, so how can it left uninitialized? ( Please neglect pointers on this question ) – Toby Mar 21 '12 at 10:22
  • @Toby: Create a pointer to an object but do not use `new` to create an *actual instance*; assign any random memory location to the pointer and then try to access the "object" it points to with `->`. For added fun, engineer the contents of the location you point to in any manner you choose before derefing the pointer. – Jon Mar 21 '12 at 10:24
  • Okay thank you - I am aware that I can just create a pointer without the object being actually initialized ( or being there at all ). But now it's clear that if I just declare an object it gets initialized too! – Toby Mar 21 '12 at 10:34
2

A. yes: constructors of members are called before constructor of the containing class. B. yes, but the result is is undefined behavior (= anything can happen; even appearing to work.).

zvrba
  • 24,186
  • 3
  • 55
  • 65
  • So just that I get this straight - if I have a class which has a member of any type, the constructor for this member will be called when I create the containing class? – Toby Mar 21 '12 at 10:13
  • The compiler will attempt to call a default constructor and will report an error if is doesn't exist (in that case you have to explicitly call it in the initializer list of the containing class' constructor). Also note that the default constructor for primitive types (e.g., int, float, any pointer) is a noop - the field will be uninitialized. – zvrba Mar 21 '12 at 10:33
0

A. The constructor for your class myClass_A will be called the moment the object of the class myClass_B is instantiated.

B. You cannot call methods of unintialized objects for example

myClass_A *pObj;
pObj->myFunction_A();

will throw you an exception.

C. You can never call a method succesfully without the object being fully constructed.

Jeeva
  • 4,585
  • 2
  • 32
  • 56