Вектор указателей объекта, инициализация - PullRequest
3 голосов
/ 09 мая 2011

Я пока не очень разбираюсь в C ++, так что потерпите меня, если это базовые вещи.

У меня есть такой код ниже.L является абстрактным классом (он имеет ряд чисто виртуальных функций), а A, B и C являются регулярными классами, которые все являются производными от L.Их может быть любое количество, и все они разные.

int main() {
    // ...

    std::vector<L*> ls(3) ; 

    ls[0] = new A ;
    ls[1] = new B ;
    ls[2] = new C ;

    int i ;
    for (i = 0 ; i < ls.size() ; i++) {
        if (ls[i]->h()) {
            // ...
        }
    }

    // ...
}

Это работает, но действительно должен быть лучший способ инициализировать этот вектор.Правильно?

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

В идеале я хотел бы вытащить определение и инициализацию вектора из main и предпочтительно в отдельный файл, который я затем смогу #include.Когда я пытаюсь это сделать, компилятор жалуется, что он «ожидал конструктор, деструктор или преобразование типов перед '=' token".Все классы A, B и C имеют конструкторы по умолчанию.

Кроме того, у меня сложилось впечатление, что я должен delete вручную что-либо создать, используя new, но он победит '• удалите ls с помощью delete или delete[].Если я пытаюсь delete ls;, компилятор жалуется, что "тип" класс std :: vector> 'аргумент' delete ', ожидаемый указатель ".

Вышеупомянутое даже безопасно или вызывает некоторые проблемы с памятью?

Ответы [ 4 ]

4 голосов
/ 09 мая 2011

но действительно должен быть лучший способ инициализировать этот вектор. Правильно?

Я так не думаю, по крайней мере без C ++ 0x. Какой путь вы бы предпочли? Ваш код инициализации полностью в порядке.

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

Вы все еще можете сделать сам вектор const, только его тип члена не может быть указателем на const тогда.

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

Вам не нужно отслеживать длину в постоянных массивах:

L* ls[] = { new A, new B, new C };
// with <boost/range/size.hpp>
std::size_t num = boost::size(ls);
// without Boost, more error-prone
// std::size_t num = sizeof ls / sizeof ls[0];

И часто вам все равно не нужен размер, например с Boost.Range.

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

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

Кроме того, у меня сложилось впечатление, что я должен вручную удалить что-либо, созданное с помощью new, но он не удалит ls с помощью delete или delete [].

Ваше впечатление правильное, но вы не создали ls с new, только его элементов . После использования векторов вы должны delete каждый из его элементов, но не сам вектор.

Рекомендованной альтернативой контейнерам STL, содержащим полиморфные указатели, является библиотека контейнеров указателей Boost .

1 голос
/ 09 мая 2011

Вы действительно должны использовать delete для созданных вами объектов. Вы вызываете delete для вектора, а не для объектов. Что-то вроде:

for(size_t i = 0; i < ls.size(); i++){
    delete ls[i];
}

Для вашей проблемы построения вы можете обернуть их в функцию и поместить эту функцию в собственный заголовочный файл. Вам также необходимо включить все заголовочные файлы соответствующих классов.

void init_vector(std::vector<LS*> & v){
    ls[0] = new A ; 
    ls[1] = new B ;
    ls[2] = new C ;
}
0 голосов
/ 09 мая 2011

Поскольку вы знаете размер во время компиляции, я предлагаю использовать array вместо vector. Использование шаблона класса array вместо массива в стиле C дает вам преимущество стандартного интерфейса контейнера, такого как vector. То есть вы можете вызвать size() для массива и получить итераторы и т. Д.

И чтобы вы не забыли delete объекты, я предлагаю использовать умные указатели:

#include <boost/array.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

boost::array<boost::shared_ptr<L>, 3> ls = { {
    boost::make_shared<A>(),
    boost::make_shared<B>(),
    boost::make_shared<C>(),
} };

Современные компиляторы поставляют свои собственные версии array и shared_ptr в стандартной библиотеке:

#include <array>
#include <memory>

std::array<std::shared_ptr<L>, 3> ls = { {
    std::make_shared<A>(),
    std::make_shared<B>(),
    std::make_shared<C>(),
} };

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

В идеале я хотел бы вытащить определение и инициализацию вектора из main и, предпочтительно, в отдельный файл, который я затем смогу #include

В этом случае вам понадобится заголовочный файл с объявлением и файл реализации с определением ls:

// file ls.h

#ifndef LS_H
#define LS_H

#include <boost/array.hpp>
#include <boost/shared_ptr.hpp>

extern boost::array<boost::shared_ptr<L>, 3> ls;

#endif

// file ls.cpp

#include "ls.h"
#include <boost/make_shared.hpp>

boost::array<boost::shared_ptr<L>, 3> ls = { {
    boost::make_shared<A>(),
    boost::make_shared<B>(),
    boost::make_shared<C>(),
} };
0 голосов
/ 09 мая 2011

Если C ++ 11 приемлем, вам может быть лучше использовать std::array вместо std::vector:

std::array<L *, 3> = {new A(), new B(), new C()};
...