使用new运算符将对象复制到堆而不知道其类型

时间:2011-11-06 11:32:45

标签: c++ inheritance new-operator

我有一个疑问,下面的函数可以接收类型为A的对象或派生类型的对象。

A *copyToHeap(A &obj) {
    A *ptr=new A(obj);
    return ptr;
}

如果我们这样称呼它:

//B inherits from A
B bObj;
B *hPtr=copyToHeap(bObj);

hPtr指向的对象实际上是A或B类型的对象? 这样做安全吗?

6 个答案:

答案 0 :(得分:7)

在代码中执行以下操作时:

A* ptr = new A(obj);

你总会得到一个A实例。 obj将被视为A,新的A将根据obj的“A部分”创建。

更好的方法是在早期的回复中指出,将虚拟MakeCopy方法添加到基类并为派生类实现它。

virtual A* MakeCopy();

通过制作被调用对象的副本来实现此方法。然后它在派生类中实现,所以如果你有一个实际上是B对象的A指针,你将得到一个真正的B副本,并避免在你的例子中出现“切片”。

答案 1 :(得分:5)

返回的对象的类型为pointer to A,这意味着hPtr指向的对象的类型为A。这是不安全的,因为调用B独有的方法或成员将导致崩溃或未定义的行为。您可能正在寻找factory pattern

答案 2 :(得分:3)

安全的方法是提供虚拟克隆方法

#include <memory>

class Base
{
public:
    virtual std::unique_ptr<Base> Clone() = 0;
};

class Derived : public Base
{
public:
    Derived(int i) : i_(i)
    {

    }

    std::unique_ptr<Base> Clone()
    {
        return std::unique_ptr<Derived>(new Derived(i_));
    }

private:
    int i_;
};


std::unique_ptr<Base> copyToHeap(std::unique_ptr<Base> obj) 
{
    return obj->Clone();
}

答案 3 :(得分:1)

它不安全,不正确,编译器应该给你一些诊断。如果使用GCC,您是否尝试使用g++ -Wall进行编译?

答案 4 :(得分:1)

不编译:

B *hPtr=copyToHeap(bObj); //error: invalid conversion from ‘A*’ to ‘B*’

如果您将hPtr的类型更改为A*,则会进行编译,但您仍会获得A个对象。您使用的A的默认复制构造函数将创建一个A对象,并复制B对象的字段,这些字段在A中定义,切片{{1}部分关闭。

答案 5 :(得分:0)

由于上述/本文所述的所有问题 - 如果有的话你可以避免它(我想不出你为什么不能这样做的原因) - 你不应该设计你的代码要求“ copyToHeap”。

正如Luchian指出的那样,你可能想要一家工厂。工厂在堆上创建您的对象以开始(并返回一个智能指针来管理对象/指针/内存的生命周期)。