Suppose there is a hierarchy of two classes (class Derived: public Base
). Both these classes have big memory footprint and costly constructors. Note that nothing in these classes is allocated in heap: they just have a big sizeof
.
Then there is a function with a fast path (executed always) and a slow path (executed conditionally). Fast path needs a Base
instance, and slow path needs a Derived
instance constructed from existing base. Also, slow path decision can be made only after the fast path.
Current code looks like this:
void f()
{
Base base;
/* fast path */
if (need_slow_path) {
Derived derived (base);
/* slow path */
}
}
This is inefficient, because the base needs to be copied into derived; also the base is allocated twice and there is a risk of overflowing the stack. What I want to have:
Derived
instanceBase
ctor on itDerived
ctor on the existing Base
instance and execute the slow pathIs it possible in C++? If not, what are possible workarounds? Obviously, I'm trying to optimize for speed.
I am afraid this is not possible just as you wrote - any constructor of Derived
must call a constructor of the Base
subobject, so the only way to do that legally would be to call Base
's destructor first, and I believe you don't want that.
However, it should be easy to solve this with a slight redesign - prefer composition over inheritance, and make Derived
a separate class that will store a reference (in the general sense; it can of course be a pointer) to Base
and use it. If access control is an issue, I feel a friend
is justified here.
You should change your design slightly to change your reliance on inheritance to that on composition.
You could encapsulate members of derived class (not present in the base class) into another class, and keep it's null reference in the derived class.
Now directly initialize derived class without initializing new class's object.
Whenever slow path is required, you can initialize and use it.
Benefits
I can fake it.
Move/all the data of derived into an optional
(be it boost
or std::ts::optional
proposal for post C++14, or hand rolled).
Iff you want the slow path, initialize the optional
. Otherwise, leave it as nullopt
.
There will be a bool
overhead, and checks when you assign/compare/destroy implicit. And things like virtual
functions will be derived
(ie, you have to manage dynamic dispath manually).
struct Base {
char random_data[1000];
// virtual ~Base() {} // maybe, if you intend to pass it around
};
struct Derived:Base {
struct Derived_Data {
std::string non_trivial[1000];
};
boost::optional< Derived_Data > m_;
};
now we can create a Derived
, and only after we m_.emplace()
does the Derived_Data
get constructed. Everything still lives is in one contiguous memory block (with a bool
injected by the optional
to track if m_
was constructed).
Not sure if you can do exacactly what you want i.e execute "fast" path before second contructor but i think you use 'placement new' feature - manually call contructors based on need_slow_path
predicate. i.e but that changes flow a little:
The example code
#include <memory>
void f(bool need_slow_path)
{
char bufx[sizeof(Derived)];
char* buf = bufx;
Derived* derived = 0;
Base* base = 0;
if (need_slow_path ) {
derived = new(buf) Derived();
base = derived;
} else {
base = new(buf) Base();
}
/* fast path using *base */
if (need_slow_path) {
/* slow path using *base & * derived */
}
// manually destroy
if (need_slow_path ) {
derived->~Derived();
} else {
base->~Base();
}
}
Placement new is well described here: What uses are there for "placement new"?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With