C++: Вектор объектов по сравнению с вектором указателей на новые объекты?

Я стремлюсь улучшить свои навыки C++ путем записи демонстрационного рендерера программного обеспечения. Это берет объекты, состоящие из точек в 3-м пространстве, и отображает их на 2-ю область просмотра и рисует круги переменного размера для каждой точки в поле зрения. Который лучше:

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());

или...

class World{
    vector<ObjectBaseClass*> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(new DerivedClass1());
        object_list.push_back(new DerivedClass2());

?? Использовал бы указатели в 2-м примере для создания нового поражения объектов точка использования векторов, потому что векторы автоматически называют деструкторы DerivedClass в первом примере, но не в 2-м? Указатели к новым объектам, необходимым при использовании векторов, потому что они обрабатывают управление памятью сами, пока Вы используете их методы доступа? Теперь скажем, у меня есть другой метод в мире:

void drawfrom(Viewport& view){
    for (unsigned int i=0;i<object_list.size();++i){
        object_list.at(i).draw(view);
    }
}

При вызове это выполнит метод ничьей для каждого объекта в мировом списке. Скажем, я хочу, чтобы производные классы смогли иметь свои собственные версии ничьей (). Список должен был бы иметь указатели затем для использования селектора метода (->)?

27
задан metamemetics 22 April 2010 в 19:03
поделиться

4 ответа

С помощью этого кода вы не получите того, что хотите.

class World{
    vector<ObjectBaseClass> object_list;
public:
    void generate(){
        object_list.clear();
        object_list.push_back(DerivedClass1());
        object_list.push_back(DerivedClass2());

То, что должно произойти, называется нарезкой объекта. Вы получите вектор ObjectBaseClass.

Чтобы полиморфизм работал, вы должны использовать какие-то указатели. Вероятно, есть некоторые умные указатели или ссылки в boost или других библиотеках, которые можно использовать и сделать код намного безопаснее, чем второе предложенное решение.

19
ответ дан 28 November 2019 в 04:46
поделиться

Поскольку вы явно указываете если вы хотите улучшить свой C ++, я рекомендую вам начать использовать Boost . Это может помочь вам решить вашу проблему тремя разными способами:

Использование shared_ptr

Использование shared_ptr может объявить ваш вектор следующим образом:

std::vector< boost::shared_ptr< ObjectBase > > object_list;

И использовать его так:

typedef std::vector< boost::shared_ptr< ObjectBase > >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

​​Это даст вам полиморфизм и будет использоваться так же, как обычный вектор указателей, но shared_ptr будет выполнять управление памятью за вас, уничтожая объект, когда последний shared_ptr ссылающийся уничтожен.

Примечание о C ++ 11: В C ++ 11 shared_ptr стал частью стандарта как std :: shared_ptr , поэтому для этого больше не требуется Boost подход. Однако, если вам действительно не нужно совместное владение, рекомендуется использовать std :: unique_ptr , который был недавно введен в C ++ 11.

Используя ptr_vector

Используя ptr_vector , вы должны сделать это так:

boost::ptr_vector< ObjectBase > object_list;

И использовать его так:

typedef boost::ptr_vector< ObjectBase >::iterator ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    (*it)->draw(view);

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

Используя reference_wrapper

Используя reference_wrapper , вы должны объявить его так:

std::vector< boost::reference_wrapper< ObjectBase > > object_list;

А затем использовать его так:

typedef std::vector< boost::reference_wrapper< ObjectBase > >::iterator 
    ObjectIterator;

for ( ObjectIterator it = object_list.begin(); it != object_list.end(); it++ )
    it->draw(view);

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

Примечание о C ++ 11: reference_wrapper также стандартизирован в C ++ 11 и теперь может использоваться как std :: reference_wrapper без Boost.

Как указано в ответе Maciej H , ваш первый подход приводит к нарезке объекта . В общем, вы можете изучить итераторы при использовании контейнеров.

27
ответ дан 28 November 2019 в 04:46
поделиться

Ну, это зависит от того, что вы пытаетесь сделать со своим вектором.

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

2
ответ дан 28 November 2019 в 04:46
поделиться

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

Если объекты не могут быть скопированы или назначены, вы все равно не сможете поместить их прямо в std :: vector , поэтому вопрос спорный. Если операции копирования и / или присваивания являются дорогостоящими (например, объект хранит большой объем данных), вы можете захотеть сохранить указатели из соображений эффективности. В противном случае, как правило, лучше не хранить указатели именно по той причине, которую вы упомянули (автоматическое освобождение)

Что касается вашего второго вопроса, да, это еще одна веская причина для хранения указателей. Динамическая отправка (вызовы виртуальных методов) работает только с указателями и ссылками (и вы не можете хранить ссылки в std :: vector ). Если вам нужно хранить объекты нескольких полиморфных типов в одном векторе, вы должны хранить указатели, чтобы избежать разделения.

13
ответ дан 28 November 2019 в 04:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: