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.doSom
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.
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.
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.
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()
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.
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))
{
}