I need to derive a class Child from the class Base with the following requirements:
(there are some similarities with How to remind a param passed to the base class constructor? however exception-safety is almost not discussed in the above mentioned question)
While implementing it, I encountered the following difficulties:
A. It seems natural to have object of class Foo to be a member of Child
class Base {
public:
Base(Foo& foo);
};
class Child: public Base {
public:
Child(int par);
private:
Foo m_foo;
};
However, how do I initialize it now in Child constructor?
Child::Child(int par):
Base(Foo(par)),
m_Foo(par) ...
This will pass a ref to temporary to class Base Constructor (will it even compile?) and then initialize m_Foo separately. This is bad.
If not for requirements #4 and #5, I could have used a static method co construct Foo when Base is called and then pass it to m_Foo:
Foo Child::s_Helper(int par) {
if (!s_FooConstructed) {
s_Foo = Foo(par);
s_FooConstructed = true;
}
return s_Foo;
}
I would love to use unique_ptr or shared_ptr as Child class member:
class Child: public Base {
public:
Child(int par);
private:
unique_ptr<Foo> m_Foo;
bool m_IsFooConstructed;
unique_ptr<Foo> x_FooHelper(int par);
};
Child::Child(int par):
Base(x_FooHelper(par)),
m_Foo(x_FooHelper(par))
{}
unique_ptr <Foo> Child::x_FooHelper(int par)
{
if(!m_FooConstructed) {
m_Foo.reset(new Foo(par)); ///< Problem here
m_FooConstructed = true;
}
return m_Foo;
}
however, here m_Foo is not constructed at the time x_FooHelper is called... same with shared_ptr
I can create a pointer instead:
class Child: public Base {
public:
Child(int par);
private:
Foo* m_Foo;
bool m_IsFooConstructed;
Foo* x_FooHelper(int par);
};
Child::Child(int par):
Base(*x_FooHelper(par)),
m_Foo(x_FooHelper(par))
{}
Foo* Child::x_FooHelper(int par)
{
if(!m_FooConstructed) {
m_Foo = new Foo(par);
m_FooConstructed = true;
}
return m_Foo;
}
However here, if Base constructor throws, Foo is already created, and it will not be destroyed as it is kept by the raw pointer (m_Foo) ~Child will also not be called. What can I do?
Use the Base-from-Member Idiom - create another class that owns the Foo
as the first base of Child
:
class Base {
public:
Base(Foo& foo);
};
struct FooOwner {
FooOwner(int par)
: m_foo(par)
{ }
Foo m_foo;
};
class Child
: private FooOwner // first, so Foo is initialized
, public Base // before Base
{
public:
Child(int par)
: FooOwner(par)
, Base(FooOwner::m_foo)
{
/* something else */
}
};
The Foo
's lifetime is still tied to Child
, and you can safely pass a reference to it to Base
- since it definitely will have been constructed before Base
.
If you find that too verbose for some reason, you can also use boost::base_from_member
to achieve the same thing:
class Child
: private boost::base_from_member<Foo>
, public Base
{
typedef boost::base_from_member<Foo> FooOwner;
Child(int par)
: FooOwner(par)
, Base(FooOwner::member)
{ }
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.