Как выделить память для объекта, я не знаю, реальный тип? C ++ - PullRequest
0 голосов
/ 29 ноября 2010

Я хочу сохранить объект некоторого производного класса как свойство другого объекта.

Проблема для меня состоит в том, чтобы знать размер объекта, который будет сохранен, поскольку это может быть любой производный класс данного базового класса. Я не могу просто выделить память для объекта базового класса, так как он меньше. Полагаю, оператор typeof также просто даст размер базового класса, верно?

Вот мой пример кода. Пожалуйста, смотрите комментарии, чтобы понять, что я имею в виду. Извините за неоригинальность ...

BaseA {};

DerivedA1 : BaseA {public: void property() { cout << 1; }};
DerivedA2 : BaseA {public: void property() { cout << 2; }};
// etc. - several derived classes.

BaseB   // Contains (or links to) an instance of class derived from BaseA.
{
public:
    BaseA * instanceA;

    BaseB (BaseA instanceAX)
    {
        // ??? => How to allocate memory for the object
        // I don't know the real type of?
        instanceA = new BaseA (instanceAX);
    }

    BaseB (BaseA instanceAX) { delete instanceA; }
};

main()
{
    DerivedA1 instanceA1;
    BaseB instanceB (instanceA1);

    // Use "property" regardless of which derived class it belongs to.
    instanceB.instanceA->property();
} 

Я мог бы хранить указатель вместо самого объекта, это было бы проще. Но я не уверен, что хочу рассчитывать на то, что вызывающая сторона сохранит объект свойства в течение жизни экземпляра instanceB .

Спасибо!

Ответы [ 2 ]

3 голосов
/ 29 ноября 2010

Ну, во-первых, поскольку вы передаете свой BaseA по значению, а не по ссылке или указателю, вы действительно знаете тип;это BaseA.BaseB был скопирован в BaseA, и эта копия BaseA отправлена ​​в функцию.Любые дополнительные данные в BaseB не содержатся в instanceAX.Это называется «нарезка».

Но, чтобы ответить на ваш вопрос, вы просто не делаете это таким образом.Вам нужна функция clone () в вашем классе BaseB, если она должна работать таким образом.В качестве альтернативы вы можете создать умный указатель или другой объект, который может клонировать экземпляр для вас.Если вы ищете мое имя в comp.lang.c ++ вместе с clone (), вы можете встретить обсуждение этой последней идеи, которое у меня было там.

Функция clone - это, конечно, виртуальная функция, которая возвращает копиютекущего объекта:

struct BaseA
{
  virtual BaseA* clone() const = 0;
};

struct BaseB : BaseA
{
  BaseB* clone() const { return new BaseB(*this); }
};
2 голосов
/ 29 ноября 2010

Я вижу две возможности.

  • Дать BaseA чисто виртуальную функцию-член, которая может называться clone, которая отвечает за генерацию копии экземпляра, для которого она вызывается,Тогда конструктор BaseB может стать

    BaseB (BaseA const &instanceAX) { instanceA = instanceAX.clone (); }

  • Вы также можете просто избежать передачи необработанных указателей и использовать вместо них boost::shared_ptr.Таким образом, поскольку он подсчитывается по ссылке, ваш вызывающий не может вызвать преждевременное уничтожение деривации BaseA, которая использовалась для построения BaseB.Вам нужно настроить BaseA * instanceA; чтобы прочитать boost::shared_ptr <BaseA> instanceA; Тогда конструктор BaseB может выглядеть так:

    BaseB (boost::shared_ptr <BaseA> a) : instanceA (a) { }

ПроблемаПервый подход заключается в том, что некоторый клиентский программист, который наследует, скажем, DerivedA1, может забыть реализовать clone в своем производном.Основная проблема второго подхода заключается в том, чтобы потребовать от каждого пользователя BaseB выделить объект свойства в куче (поскольку вы все равно делаете копию внутри конструктора BaseB s, по крайней мере, общее потребление памяти не возрастет,хотя)

...