如何保持堆栈上分配的对象的动态类型?

时间:2014-08-24 18:08:42

标签: c++ c++11

假设我有:

class A { };
class B : public A { };
A f() { 
    if (Sunday())
        return A;
    else
        return B;
}

显然这不起作用,因为A的复制构造函数会被调用。无论如何返回堆栈分配的对象而不丢失它的类型?

我已尝试使用std::shared_ptr<A>,但由于std::shared_ptr<B>不是std::shared_ptr<A>,因此我遇到了另一个问题。

3 个答案:

答案 0 :(得分:2)

由于slicing,这是不可能的。请改用std :: unique_ptr。您不会丢失动态类型,但只能通过A

的界面访问它

答案 1 :(得分:2)

不能立即从创建该对象的函数中返回堆栈分配的(即本地)对象。本地对象在函数返回时被销毁。您可以使用各种“智能指针”和类似技术隐藏/混淆对象分配的实际性质,但对象本身应该动态分配

除此之外,只要遵守本地对象生存期规则,本地对象的多态性的工作方式与对任何其他对象的工作方式完全相同。只需使用指针或参考

A a;
B b;

A *p = Sunday() ? &a : &b;
// Here `*p` is a polymorphic object

只要本地对象存在,上例中的指针p仍然有效,这意味着您无法从函数返回p

另外,正如您在上面的示例中所看到的,它无条件地提前创建两个对象,然后选择其中一个,而不使用第二个。这不是很优雅。您无法在if语句的不同分支中创建此类对象的不同版本,原因与您无法以多态方式从函数返回本地对象的原因相同:一旦创建该对象的本地块完成,该对象就是破坏。

后一个问题可以通过使用原始缓冲区和手动就地构建来解决

alignas(A) alignas(B) char object_buffer[1024]; 
// Assume it's big enough for A and B

A *p = Sunday() ? new(buffer) A() : new (buffer) B();
// Here `*p` is a polymorphic object
p->~A(); // Virtual destructor is required here

但看起来并不漂亮。类似的技术(涉及复制缓冲区)可能用于使局部多态对象在块边界内存活(参见@DietmarKühl的答案)。

所以,再次,如果你只想创建两个对象并使你的对象在块边界中存活,那么立即解决方案放置本地对象是不可能的。您将不得不使用动态分配的对象。

答案 2 :(得分:0)

最简单的方法当然是使用合适的智能指针,例如std::unique_ptr<A>作为返回类型,并在堆上分配对象:

std::unique_ptr<A> f() {
    return std::unique_ptr<A>(Sunday()? new B: new A);
}

对于返回可能指向std::unique_ptr<A>的{​​{1}}的方法,B必须有A析构函数,否则代码可能会导致未定义的行为当virtual实际指向std::unique_ptr<A>对象时。如果B没有A析构函数且无法更改,则可以通过使用合适的virtual或使用{{1}的合适删除器来避免此问题}:

std::shared_ptr<...>

如果您不想在堆上分配对象,则可以使用存储类型,该类型存储std::unique_ptr<...> std::unique_ptr<A, void(*)(A*)> f() { if (Sunday()) { return std::unique_ptr<A, void(*)(A*)>(new B, [](A* ptr){ delete static_cast<B*>(ptr); }); } else { return std::unique_ptr<A, void(*)(A*)>(new A, [](A* ptr){ delete ptr; }); } } union,然后对其进行适当构造和破坏(下面的代码假定AB的副本不会抛出异常;如有必要,可以添加合适的移动构造和移动分配):

A