Использование оператора new для копирования объекта в кучу, не зная его типа - PullRequest
9 голосов
/ 06 ноября 2011

У меня есть сомнения, функция ниже может получить объект типа 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 ]

8 голосов
/ 06 ноября 2011

когда вы сделаете следующее в своем коде:

A* ptr = new A(obj);

вы всегда получите экземпляр A.obj будет рассматриваться как A, а новый A будет создан на основе «части A» obj.

Лучший подход, как указано в предыдущем ответе, добавить виртуальный метод MakeCopy в базовый класс иреализовать его для производных классов.

virtual A* MakeCopy();

Этот метод реализован путем создания копии объекта, для которого он вызван.Затем он реализуется в производных классах, поэтому, если у вас есть указатель A, который на самом деле является объектом B, вы получите истинную копию B и избежите "нарезки", которая происходит в вашем примере.

5 голосов
/ 06 ноября 2011

Возвращаемый объект имеет тип pointer to A, что означает, что объект, на который указывает hPtr, имеет тип A. Это небезопасно, поскольку вызов методов или членов, исключающих B, вызовет сбой или неопределенное поведение. Вы, наверное, ищете фабричный шаблон .

3 голосов
/ 06 ноября 2011

Безопасный способ - предоставить метод виртуального клона

#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();
}
1 голос
/ 06 ноября 2011

Не компилируется:

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

Если вы измените тип hPtr на A*, он скомпилируется, но вы все равно получите объект A. Конструктор копирования по умолчанию для A, который вы используете, создаст объект A и скопирует поля объекта B, которые были определены в A, отрезав часть B.

1 голос
/ 06 ноября 2011

Это не безопасно, это неправильно, и компилятор должен дать вам некоторую диагностику.Вы пытались скомпилировать с g++ -Wall при использовании GCC?

0 голосов
/ 06 ноября 2011

Из-за всех проблем, описанных выше / в этом посте - если вы вообще можете этого избежать (и я не могу придумать причину, по которой вы не смогли) - вы не должны разрабатывать свой код так, чтобы он требовал »copyToHeap».

Как указывает Лучиан, вам, вероятно, нужна фабрика.Фабрика создает ваш объект в куче для начала (и возвращает умный указатель для управления временем жизни объекта / указателя / памяти).

...