Question

I have the following C++-class:

// Header-File
class A
{
    public:
    A();

    private:
    B m_B;
    C m_C;
};

// cpp-File
A::A()
: m_B(1)
{
    m_B.doSomething();
    m_B.doMore();
    m_C = C(m_B.getSomeValue());
}

I now would like to avoid the class A to call any constructor of C m_C. Because on the last line in A::A(), I'm anyways going to initialize m_C myself because I need to prepare m_B first. I could provide an empty default constructor for class B. But that's not the idea.

I have already tried to add m_C(NULL) to the init-list of A::A(). Sometimes it worked, sometimes it said there was no constructor taking NULL as an argument.

So how can I have m_C left uninitialized? I know that with pointers, the m_C(NULL)-way works. And I don't want to allocate it dynamically using new.

Any idea is appreciated.

Was it helpful?

Solution

What you ask is forbidden - and correctly so. This ensures that every member is correctly initialized. Do not try to work around it - try to structure your classes that they work with it.

Idea:

  • C has a constructor that does nothing
  • C has an initialization method that makes the class usable
  • C tracks whether it has been initialized correctly or not and returns appropriate errors if used without initialization.

OTHER TIPS

How about using technique described in this QA?

Prevent calls to default constructor for an array inside class

std::aligned_storage<sizeof(T[n]), alignof(T)>::type

Or, you also can consider using of union. AFAIK, unions will be initialized only with first named member's constructor.

For example,

union
{
   uint8_t _nothing = 0; 
   C c;
};

According to the standard mentioned in the QA, c will be zero-initialized, and its constructor will not be called.

You can't.

All member variables are full constructed when the construcotr code block is entered. This means there constructors must be called.

But you can work around this restriction.

// Header-File
class A
{
    struct Initer
    {
         Initer(B& b)
             : m_b(b)
         {
             m_b.doSomething();
             m_b.doMore();
         }
         operator int()  // assuming getSomeValue() returns int.
         {
             return m_b.getSomeValue();
         }
         B& m_b;
    };
    public:
    A();

    private:   // order important.
    B m_B;
    C m_C;
};


// cpp-File
A::A()
: m_B(1)
, m_C(Initer(m_B))
{
}

I don't see a good way to achieve what you want. This must be a workaround:

// Header-File
class A
{
    public:
    A();

    private:
    B m_B;
    C m_C;
    static int prepareC(B& b);
};

// cpp-File
A::A()
: m_B(1)
, m_C(prepareC(m_B))
{
}

int A::prepareC(B& b)
{
    b.doSomething();
    b.doMore();
    return b.getSomeValue();
}

Please ensure that m_B.doSomething(), m_B.doMore() and m_B.getSomeValue() don't touch m_C (directly or indirectly).


As @Tobias correctly mentions, this solution depends on the order of initialization. You need to ensure that the definitions of m_B and m_C are in this order.


Updated the code according to @Loki's idea.

The pointer sounds like the only clean solution to me. The only other solution I see is to have a default constructor for C that does nothing and have an initialising method in C you call yourself later.

m_C.Initialise( m_B.getSomeValue() );

Easiest is storing pointers to a B and a C. These can be initialized to 0, omitting any construction. Be careful not to dereference a null pointer and delete it in the destructor of A (or use std::unique_ptr/boost::scoped_ptr).

But why not initialize m_B first (through a proper constructor call, not in A::A(), and then use that initialized B instance to initialize m_C? It will call for a small rewrite, but I bet it'll be worth the code cleanup.

If you don't want to allocate it dynamically using new for code clutter/exception safety reasons, you can use a std::unique_ptr or std::auto_ptr to solve this problem.

A solution that avoids new is to edit C to have a two-step initialization process. The constructor would then construct a "zombie" object, and you'd have to call an Initialize method on that m_C instance to finish your initialization. This is similar to the existing cases you found where you could pass NULL to the constructor, and later go back to initialize the object.

Edit:

I thought of this earlier (even though it looks much like other people's solutions). But I had to get some confirmation that this wouldn't break before I added this solution - C++ can be quite tricky, and I don't use it very often :)

This is cleaner than my other suggestions, and doesn't require you to mess with any implementation but that of A.

Simply use a static method as the middle-man on your initialization:

class A
{
public:
    A();

private:
    static int InitFromB(B& b)
    {
        b.doSomething();
        b.doMore();
        return b.getSomeValue();
    }

    // m_B must be initialized before m_C
    B m_B;
    C m_C;
};

A::A()
    : m_B(1)
    , m_C(InitFromB(m_B))
{
}

Note that this means you can't allow m_B to depend on the instance of A or C at all, whereas the solutions at the top of this answer might allow you to pass A or m_C into m_B's methods.

Just use comma expressions:

A::A()
  : m_B(1)
  , m_c(m_B.doSomething(), m_B.doMore(), m_B.getSomeValue())
{
}

Obviously, as others have explained, m_B better be declared before m_C else m_B.doSomething() invokes undefined behavior.

Here we have the building blocks:

#include <iostream>

class C
{
public:
  C(int i){std::cout << "C::C(" << i << ")" << std::endl;}
};

class B
{
public:
  B(int i){std::cout << "B::B(" << i << ")" << std::endl;}
  void doSomething(){std::cout << "B::doSomething()" << std::endl;}
  void doMore(){std::cout << "B::doMore()" << std::endl;}
  int getSomeValue(){return 42;}
};

If you want to make a new kind of construction for B consider making a derived class:

class B1 : public B
{
public:
  B1() : B(1)
  {
    doSomething();
    doMore();
  }
};

Now use the class B1 that is derived from B:

class A
{
private:
  B1 _b;
  C _c;
public:
  A() : _c(_b.getSomeValue()){std::cout << "A::A()" << std::endl;}
};

And then:

int main()
{
  A a;
}

Output:

B::B(1)
B::doSomething()
B::doMore()
C::C(42)
A::A()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top