C++,如何在复制构造函数中正确复制std :: vector <Class*>?

Vyk*_*tor 9 c++ containers vector 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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

我想这堂课会做到这一点,但我找到了如何达到完美的目标.

首先:data() - 这是初始化空向量所需的初始化(这是编写一个好的和干净的代码的一部分)?

如何vector::iterator在复制构造函数中使用,我找到的唯一方法就是我写入代码的方法(对于复制构造函数,const应该是必需的).

仅复制矢量会复制指针值而不是整个对象?

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

子问题:我假设使用vector<A *>比各种原因更适合和有效vector<A>(不是每次复制,决定是否(不)复制对象的权力......)

Set*_*gie 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]);
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不再使用,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);
}
Run Code Online (Sandbox Code Playgroud)

这样做的好处是它可以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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,如果其中一个new调用抛出异常,您就不会有内存泄漏.当然,try/catch如果你宁愿这样做,你可以使用没有迭代器的方式.

  • 还需要考虑的另一件事是错误处理:如果其中一个`new`s在循环中间失败,会发生什么?程序可能会终止,但如果在调用堆栈中进一步处理内存不足错误,则会发生内存泄漏. (2认同)
  • @someguy 实际上在空指针上调用 `delete` 被定义为什么都不做。不知道为什么我将 `*i` 设置为 null,当时似乎是个好主意 :) 至少现在我可以摆脱花括号了。 (2认同)