C++ C++;,如何正确复制std::vector<;类别*>;复制构造函数?

C++ C++;,如何正确复制std::vector<;类别*>;复制构造函数?,c++,vector,containers,copy-constructor,C++,Vector,Containers,Copy Constructor,我用这两个类 //这是包含一些二进制数据的通用数据结构 甲级{ 公众: A(); A(常数A&); ~A(); } //主数据容器 B类{ 公众: B(); B(常数B&); ~B(); 受保护的: std::矢量数据; } //复制b类的构造函数 B::B(常量B和原始):数据(){ 对于(std::vector::const_迭代器it=orig.data.begin(); it

我用这两个类

//这是包含一些二进制数据的通用数据结构
甲级{
公众:
A();
A(常数A&);
~A();
}
//主数据容器
B类{
公众:
B();
B(常数B&);
~B();
受保护的:
std::矢量数据;
}
//复制b类的构造函数
B::B(常量B和原始):数据(){
对于(std::vector::const_迭代器it=orig.data.begin();
it
我想这门课可以胜任,但我正在寻找一种方法来达到这门课的完美

首先
:data()
-正确初始化空向量是否需要此初始化(这是编写良好干净代码的一部分)

如何在复制构造函数中使用
vector::iterator
,我找到的唯一方法是我写进代码中的方法(复制构造函数必须使用const)

只复制向量只会复制指针值,而不会复制整个对象

最后是新数据初始化。。。有没有办法用更小的代码替换整个循环和/或有没有标准方法为包含对象指针的std::containers编写复制构造函数

子问题:我假设使用
vector
比只使用
vector
(不每次复制,有权决定是否复制对象…)更合适、更有效,原因有很多
不是必需的,因为这将在输入构造函数之前自动对向量执行。您只需要初始化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]);
}
这样做的好处是,只需更改迭代器的类型,它就可以与其他容器类型(如
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;
    }
}
B::B(常量B&orig):数据(orig.data.size()){
试一试{
//auto在这里更可取,但我不知道您的编译器是否支持它
向量::迭代器thisit=data.begin();
vector::const_迭代器thatit=orig.data.cbegin();
for(;thatit!=orig.data.cend();++thisit,++thatit)
*thisit=新的A(**thatit);
}捕获(…){
对于(向量::迭代器i=data.begin();i!=data.end();++i)
如果(!*i)
打破
其他的
删除*i;
投掷;
}
}

这样,如果其中一个
new
调用抛出异常,就不会出现内存泄漏。当然,如果您愿意这样做,您可以在不使用迭代器的情况下使用
try/catch

在C++11十年后,您应该能够利用它的特性。 预先分配足够的空间,并且仍然能够
向后推
向后放置
,尽管对于普通指针来说这并不重要。避免显式迭代器

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

最后,为了安全起见,您可以使用,以避免内存泄漏和需要提供显式析构函数

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

您的意思是“子问题:我假设使用一个指针向量…”在初始值设定项列表中预先分配
数据。像那样使用
push_back()
是非常无效的。子答案:我认为,如果你不能使用指针,你就不应该使用它们。我从未使用过boost,但我确信它有解决这个问题的方法(它总是有解决常见问题的方法)。正如公认的答案所示,通过所有的异常处理来正确实现这一点是一件痛苦的事情。智能指针向量或对象的
std::deque
通常更好。(对于小的对象,指针的向量通常比对象的向量慢;太多的间接和调用新的/删除)需要解引用<代码> Org.DAT[i] < /代码>。还有一件事要考虑的是错误处理:如果一个代码<新的< /代码> s在循环中途失败,会发生什么?程序可能会终止,但如果在调用堆栈中进一步处理内存不足错误,则会出现内存泄漏。@EmilStyrke请查看我的最新编辑,看看这是否能处理您所说的情况。@Seth Carnegie:您只需在case
*i
为空指针时添加一点检查(在这种情况下,您只需跳出循环):@someguy在空指针上实际调用
delete
,定义为不执行任何操作。不知道为什么我将
*I
设置为null,在当时似乎是个好主意:)至少现在我可以去掉大括号了。
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));
}