在 C++ 中复制包含所有元素的对象! (构造函数和赋值,最佳实践?)

Copying objects with all elements in C++! (Constructors and Assignment, best practice?)

我翻遍了 SO,学到了很多关于默认构造函数、复制构造函数、对象赋值、智能指针、shallow/deep 复制以及它们与动态内存分配的关系(例如 This, , That 和 ... ).但是,对于处理复制对象元素(例如向量(或列表))的最佳实践是什么,我仍然不清楚得出什么结论。

我学习了 STL 向量,特别是通过其默认复制构造函数处理此问题,在这种情况下的最佳做法是不要自己管理资源。但似乎我理解错了。

我在提问之前所做的努力:我也能够通过引用传递对象来解决这个问题,但我最终有太多的遵从运算符(即 **)。

对于简单的小对象(例如以下代码中的对象),这里的最佳做法是什么?矢量中的元素未正确复制。 (如果我在这里犯了非常简单的错误,我不会感到惊讶。另外,如果可能的话,最好不要使用 raw/shared/smart 指针)。

#include <iostream>
#include <vector>
using namespace std;

class A{
    public:
    int id;
    A(int id_):id(id_){}
    vector<A> childs;
};

int main()
{
    A a0(0), a1(1);

    a0.childs={a1}; //node0.childs.push_back(node1);
    a1.childs={a0}; //node1.childs.push_back(node0);

    cout << a0.childs.size() << endl; // 1
    cout << a1.childs.size() << endl; // 1
    cout << a0.childs[0].childs.size() << endl; // expecting 1 but 0
    //Probably since they're not pointing to the same address of memory
    //I was hoping vector handle this by itself (was told best practice in this case is to not manage resources yourself)

    return 0;
}
a0.childs={a1}; //a1.childs is empty here, so a0.childs will be empty too
a1.childs={a0}; //a0.childs contains one element here and so will a1.childs

所以

 cout << a0.childs[0].childs.size() << endl; // This size will be 0

但是

cout << a1.childs[0].childs.size() << endl; // This will contain one element and the output shows the same.

输出:

a.exe
a0.childs.size():1
a1.childs.size():1
a0.childs[0].childs.size():0
a1.childs[0].childs.size():1

我想我理解您想要实现的目标,但是如果 objective 正在学习,那么我强烈建议您了解为什么,您期望发生的事情没有发生。在继续寻找 "workaround" 来实现您想要实现的目标之前。

为了更好地理解,编写演示相同行为的简化代码可能会有所帮助。你写的是more-or-less相当于:

struct A {
    int childCount = 0;
};

int main() {
    A a1;
    std::vector<A> vecA{a1};
    a1.childCount = 1;
    std::cout << vecA[0].childCount<< "\n"; // What do you expect here?
}

相当于:

A a1;
A copyOfA1 = a1;
a1.childCount= 1;
std::cout << copyOfA1.childCount << "\n"; // What do you expect here?

相当于:

int a1 = 0;
int copyOfA1 = a1;
a1 = 1;
std::cout << copyOfA1 << "\n";  // What about here?

a0 持有 a1 的单独 副本 而不是对 a1 的引用,因此如果您对原始 a1,保存在a0中的a1副本不变。

编辑: 至于如何实现你想实现的。我假设 A 不应该拥有它的 children。您希望它包含对别处保存的 A 的 non-owning 引用。不幸的是,std::vector 不能包含 C++ 引用。 std::vector 可以保存原始指针,但您明确要求不要使用原始指针。

另一种选择是 std::reference_wrapper<A>,它的行为有点像 C++ 引用,但可以分配,因此可以在 std::vector 中使用。您可以通过提供成员函数来隐藏 std::reference_wrapper 以通过索引访问 child:

#include <iostream>
#include <vector>
#include <functional>

struct A {
    int id;
    A(int id_):id(id_){}
    std::vector<std::reference_wrapper<A>> childs;
    A& at(size_t index) { return childs[index]; }
};

int main()
{
    A a0(0), a1(1);

    a0.childs={a1};
    a1.childs={a0};

    std::cout << a0.childs.size() << "\n";
    std::cout << a1.childs.size() << "\n";
    std::cout << a0.at(0).childs.size() << "\n";
}

Live demo.

但需要明确的是,std::reference_wrapper 基本上只是一个原始指针的包装,由您来确保它指向的 object 仍然存在。

Edit2:根据要求,这是一个使用原始指针向量的版本:

#include <iostream>
#include <vector>

struct A {
    int id;
    A(int id_):id(id_){}
    std::vector<A*> childs;
    A& at(size_t index) { return *childs[index]; }
};

int main()
{
    A a0(0), a1(1);

    a0.childs={&a1};
    a1.childs={&a0};

    std::cout << a0.childs.size() << "\n";
    std::cout << a1.childs.size() << "\n";
    std::cout << a0.at(0).childs.size() << "\n";
}

Live demo.

注意初始化vector时必须使用&获取a1的地址。