C ++, как правильно скопировать std :: vector <Class *> в конструкторе копирования? - PullRequest
9 голосов
/ 15 января 2012

Я использую эти два класса

// This is generic data structure containing some binary data
class A {
public:
    A();
    A(const A&);
    ~A();
}

// Main data container
class B {
public:
    B();
    B( const B&);
    ~B();
protected:
    std::vector<A *> data;
}

// Copy constructor for class b
B::B( const B& orig):data() {
    for( std::vector<A *>::const_iterator it = orig.data.begin();
        it < orig.data.end(); ++it){
        data.push_back( new A( *(*it)));
    }
}

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

Сначала :data() - требуется ли эта инициализация для правильной инициализации пустого вектора (и это часть написания хорошего и чистого кода)?

Как использовать vector::iterator в конструкторе копирования, я нашел только один способ, который я написал в коде (const должен быть обязательным для конструктора копирования).

Копирование только вектора будет копировать только значения указателя, а не целые объекты?

И, наконец, инициализация новых данных ... Есть ли способ, как я могу заменить весь цикл меньшим фрагментом кода и / или есть ли какой-нибудь стандарт, как написать конструктор копирования для std :: Containers, который содержит объект указатели

Подвопрос: я предполагаю, что использование vector<A *> гораздо более удобно и эффективно по разным причинам, чем просто vector<A> (не копирование каждый раз, способность решать, копировать объекты или нет) ... *

1 Ответ

10 голосов
/ 15 января 2012

data() не требуется, поскольку это будет сделано автоматически для вектора перед вводом конструктора. Вам нужно только инициализировать элементы, являющиеся типами POD или типами, которые не имеют конструктора по умолчанию (или ссылок, констант и т. Д.).

Вы можете инициализировать вектор количеством элементов, которые есть у другого, чтобы вектор не изменял свой размер по мере роста. Если вы этого не сделаете, вы начинаете с небольшого вектора и постепенно увеличиваете его размер до целевого значения путем перераспределения и перераспределения. Это сделает вектор с самого начала правильным размером:

B::B(const B& orig) : data(orig.data.size()) {
    for (std::size_t i = 0; i < orig.data.size(); ++i)
        data[i] = new A(*orig.data[i]);
}

Обратите внимание, что вы больше не используете push_back, потому что вектор уже заполнен orig.data.size() количеством элементов, которые созданы по умолчанию (в случае указателей это NULL).

Это также обрезает код, потому что вы можете использовать целое число для его итерации вместо итератора.

Если вы действительно хотите использовать итераторы, вы можете сделать

B::B(const B& orig) : data(orig.data.size()) {
    // auto is preferable here but I don't know if your compiler supports it
    vector<A*>::iterator thisit = data.begin();
    vector<A*>::const_iterator thatit = orig.data.cbegin();

    for (; thatit != orig.data.cend(); ++thisit, ++thatit)
        *thisit = new A(**thatit);
}

Преимущество этого в том, что он будет работать с другими типами контейнеров (например, list), просто изменяя типы итераторов (но, конечно, это исчезнет, ​​если у вас будет auto).

Если вы хотите добавить исключительную безопасность, вам нужен блок try/catch:

B::B(const B& orig) : data(orig.data.size()) {
    try {
        // auto is preferable here but I don't know if your compiler supports it
        vector<A*>::iterator thisit = data.begin();
        vector<A*>::const_iterator thatit = orig.data.cbegin();

        for (; thatit != orig.data.cend(); ++thisit, ++thatit)
            *thisit = new A(**thatit);
    } catch (...) {
        for (vector<A*>::iterator i = data.begin(); i != data.end(); ++i)
            if (!*i)
                break;
            else
                delete *i;

        throw;
    }
}

Таким образом, у вас не будет утечки памяти, если один из вызовов new вызовет исключение. Конечно, вы можете использовать try/catch вместе с способом без итераторов, если вы предпочитаете делать это таким образом.

...