В C ++ возможно ли совместить управление памятью на основе стека и полиморфизм? - PullRequest
2 голосов
/ 09 февраля 2010

Мне нравится объявлять переменные в стеке, особенно при использовании стандартного контейнера. Каждый раз, когда вы избегаете new, вы избегаете потенциальной утечки памяти.

Мне также нравится использовать полиморфизм, то есть иерархии классов с виртуальными функциями. Однако, кажется, что эти функции немного несовместимы: вы не можете сделать:

std::vector<BaseType> vec;
vec.push_back(DerivedType())

или, по крайней мере, кажется, что вы потеряете полиморфную природу объекта, в который вы нажимаете.

Итак, есть ли способ согласовать управление памятью на основе стека и использование виртуальных функций?

Ответы [ 7 ]

6 голосов
/ 09 февраля 2010

Ну очевидный ответ:

std::vector<BaseType*> vec;
DerivedType d;
vec.push_back(&d);

Но, вероятно, не то, что вы хотели. d и vec лучше умирают одновременно; если vec переживает d, у вас плохой указатель.

Я думаю, что вы действительно хотите что-то вроде Boost контейнера указателя :

boost::ptr_vector<BaseType> vec;
vec.push_back(new DerivedType());

Так что вам не нужно беспокоиться об утечках. Контейнеры-указатели были созданы специально для облегчения использования и хранения полиморфных объектов.

3 голосов
/ 09 февраля 2010

В этом случае вы не только теряете полиморфную природу, вы вырезаете ваш объект производного типа в объект базового типа.

Но вместо этого вы можете использовать умные указатели.

std::vector<std::tr1::shared_ptr<BaseType> > vec;
vec.push_back(std::tr1::shared_ptr<BaseType>(new DerivedType()));
2 голосов
/ 09 февраля 2010

Вы можете использовать реализацию интеллектуального указателя из библиотек Boost или библиотек POCO C ++ http://www.pocoproject.org.

include "Poco/SharedPtr.h"

include "vector.h"

class Base
{
   protected:
      std::string somestring;
   public:
      Base()
      {
          somestring = "Hello";
      }
      virtual void Method1() { 
          std::cout << "Method1 " << somestring.c_str() << std::endl;
      }
      virtual ~Base()
      {
          std::cout << "Base" << std::endl;
      }
};

class Derived : public Base
{
   public:
      void Method1() 
      { 
          std::cout << "overriden Method1 " << somestring.c_str() << std::endl;
      }
     ~Derived() 
     {
        std::cout << "Derived" << std::endl;
     }
};

int main()
{
    std::vector<Poco::SharedPtr<Base> > someVector;

    for (int i = 0; i < 20 ; i++)
    {
        Poco::SharedPtr<Base> obj = new Base();
        Poco::SharedPtr<Derived> dObj = new Derived();
        someVector.push_back(obj);
        someVector.push_back(dObj);
    }

    std::vector<Poco::SharedPtr<Base> >::iterator itr = someVector.begin();
    for (int i = 0; i < someVector.size(); i++)
    {
        someVector[i]->Method1();   
    }

    return 0;
}
2 голосов
/ 09 февраля 2010

Когда вы объявляете переменную std::vector в стеке происходит динамическое выделение памяти для внутреннего массива вектора. Это не обязательно из кучи, хотя. Второй параметр шаблона std::vector по умолчанию равен std::allocator, который вы можете заменить своим собственным.

Для управления полиморфными типами существуют такие инструменты, как boost::smart_ptr и boost, intrusive library.

1 голос
/ 09 февраля 2010

Конфликт не между полиморфизмом и распределением (куча или стек), поскольку стандартные контейнеры обычно размещают свои элементы в куче.

Конфликт между полиморфизмом и (значением или эталоном) семантикой. Полиморфизм нуждается в дополнительном уровне косвенности, введенном эталонной семантикой, но стандартные контейнеры имеют семантику значений.

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

1 голос
/ 09 февраля 2010

Если ваш основной интерес заключается в предотвращении утечек памяти, вы можете посмотреть на интеллектуальные указатели. Я бы порекомендовал Умные указатели Boost .

0 голосов
/ 09 февраля 2010

Умные указатели являются лучшим решением, как предлагают комментарии.

Другой метод - если вы используете полиморфизм в основном для сокрытия деталей реализации вашего класса - это Pimpl идиома .

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