Как заставить абстрактный класс правильно возвращать конкретный экземпляр другого абстрактного класса? - PullRequest
0 голосов
/ 25 июля 2010

Я недавно переработал одну из своих библиотек, чтобы попробовать отделить интерфейс от реализации. У меня возникла последняя проблема с классом, который предназначен для возврата экземпляра другого класса.

В определении интерфейса я делаю что-то вроде

struct IFoo
{
    virtual const IBar& getBar() = 0;
}

а затем в бетоне Foo getBar выглядит как

const IBar& Foo::getBar()
{
    Bar ret = Bar();
    return ret;
}

Проблема в том, что ret удаляется сразу после завершения getBar, вызывая большой сбой, когда конструктор копирования пытается использовать Bar следующим образом:

const Bar myBar = myFoo.getBar();

Я читал разные вещи, и я знаю, что возврат по ссылке осуждается, но я не вижу другого пути (я не хочу возвращать Bar *, потому что я не хочу вручную возвращать возвращаемое значение ).

Каков правильный (если таковой существует) абстрактный класс для возврата экземпляра конкретного класса, производного от другого абстрактного класса?

Примечание. Я видел это решение: возвращение абстрактного класса из функции но я не хочу, чтобы возвращаемое значение было статичным, а безопасность потоков - свободна.

Ответы [ 6 ]

8 голосов
/ 25 июля 2010

Используйте умные указатели.Эти указатели удаляются, когда они больше не используются (см., Например, http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/smart_ptr.htm).

4 голосов
/ 25 июля 2010

Вы также можете вернуть объект по значению.

Некоторые компиляторы предоставляют Оптимизацию возвращаемого значения , которая оптимизирует удаление копии при возврате объекта.

Edit:
Сожалею. Я пролистал вопрос и почему-то упустил тот факт, что наследование связано. Предполагая, что getBar () может возвращать различные виды IBar, возвращающие указатель IBar , имеет большой смысл.

Возвращая указатель на базу , конкретный объект сохраняется. Устранена проблема среза , и имеется исходный указатель vtbl для выполнения вызовов виртуальных функций. Также (как вы отметили в своем комментарии) вернуть экземпляр абстрактного класса просто невозможно.

Вместо того, чтобы возвращать необработанный указатель, я предлагаю вам вернуть shared_ptr<IBar>, чтобы упростить управление памятью.

const shared_ptr<IBar> Foo::getBar()
{
    shared_ptr<IBar> ret(new Bar());
    return ret;
}

Тогда используйте это так:

shared_ptr<IBar> pIBar(foo.getBar());
pIBar->myVirtualFunction();

shared_ptr - наиболее часто используемый тип интеллектуального указателя в C ++ 0x. Если у вас достаточно свежий компилятор, он будет в пространстве имен std. Более старый компилятор может иметь его в пространстве имен tr1, и это также является частью boost.

2 голосов
/ 25 июля 2010

Вы возвращаете ссылку на локальную переменную.Как только функция возвращает ссылку, стек извлекается, и этот объект Bar перестает существовать.

РЕДАКТИРОВАТЬ: я не прочитал все это.Вам, вероятно, потребуется использовать умный указатель.

На самом деле, есть ли причина, по которой вам нужно возвращать ссылку на базовый класс?Вы могли бы избежать беспорядка в умных указателях, возвращая объект самого конкретного типа, поскольку C ++ допускает ковариантные возвращаемые типы.

1 голос
/ 25 июля 2010

Объект, созданный в стеке, будет уничтожен при удалении вашего стека. Стек удаляется при выходе из функции.

Вместо этого попробуйте что-то вроде этого:


struct Foo : public IFoo
{
  Bar m_Bar;
public:
  virtual const IBar& getBar() 
  { 
    return m_Bar;
  }
}
1 голос
/ 25 июля 2010

Если вы не хотите заботиться об удалении, вам придется использовать SmartPointers.В C ++ это единственный способ заставить объект «удалять себя», когда он присваивается.

http://en.wikipedia.org/wiki/Smart_pointer

1 голос
/ 25 июля 2010

Поскольку вы хотите передать право собственности на возвращенный объект вызывающей стороне, вызывающая сторона должна будет уничтожить объект. Другими словами, возврат IBar * - ваш лучший выбор. Если вас беспокоит необходимость ручного вызова delete, вам следует использовать пакет интеллектуальных указателей, например, повышение :: shared_ptr.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...