C ++,如何在复制构造函数中正确复制std :: vector <class * =“”>?</class>

时间:2012-01-14 21:50:28

标签: c++ vector containers copy-constructor

我正在使用这两个类

// 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应该是必需的)。

只复制vector会复制指针值而不是整个对象吗?

最后新数据初始化......有什么办法可以用较小的代码替换整个循环和/或是否有任何标准如何为包含对象的std :: containers编写复制构造函数指针

子问题:我假设使用vector<A *>vector<A>更加合适和有效,而不是每次都复制,决定是否(不)复制对象。 。)

2 个答案:

答案 0 :(得分:10)

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以及没有迭代器的方式,如果你宁愿这样做的话。

答案 1 :(得分:0)

C++11 十年后,您应该能够利用其功能。 reserve() 预分配足够的空间并且仍然能够 push_backemplace_back,尽管对于普通指针来说这无关紧要。和 range-based for loop 以避免显式迭代器

B::B(const B &orig) {
    data.reserve(orig.data.size());
    for (auto p : orig.data)
        data.push_back(new A(*p));
}

最后,您可以使用 std::unique_ptr 来确保安全并避免内存泄漏以及需要提供显式析构函数

class B {
public:
    B();
    B(const B&);
protected:
    std::vector<std::unique_ptr<A> > data;
};

将构造函数更改为

B::B(const B &orig) {
    data.reserve(orig.data.size());
    for (auto &p : orig.data)
        data.emplace_back(new A(*p));
}