将一个物体的浅拷贝推后/放置到另一个物体上

2024-09-30 03:26:14 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有以下代码

class Car {
    public:
        string color;
        string name;
        Car(string c, string n): color(c), name(n){}            
}

int main() {
    vector<Car> collection1;
    vector<Car> collection2;
    collection1.emplace_back("black", "Ford");
    collection1.emplace_back("white", "BMW");
    collection1.emplace_back("yellow", "Audi");

    //Question comes here
    collection2.push_back(collection1[0]);

}

现在我相信这是collection1[0]的一个深度复制。 我尝试过使用collection2.emplace_back(move(collection1[0])),但是{}的数据字段就会消失。我只希望这个“黑福特”存在于两个向量中,通过任何一个向量对这个特定对象所做的改变将反映在两个向量上。在

我猜对于一个真实物体的向量,这个向量的元素需要实际的内存。因此collection1的元素必须独立于collection2的任何元素。我认为最简单的方法是让collection1和{}作为指针的向量,并指向相同的{}向量。但是,有没有任何可能的方法可以使上述代码工作,而不使用指针向量。我最终希望将这两个集合都返回到前一个函数,因此生成指针向量是没有意义的。在

简而言之,我想在python中模拟List.append()方法。在

^{pr2}$

Tags: 方法代码name元素stringbackcar向量
3条回答

既然你提到你不喜欢指针,你可以使用引用,但是向量不能存储引用(因为它们不可复制和分配)。但是,std::reference_wrapper将引用包装在可复制和可分配对象中。在

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

source: http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper

vector<Car> collection1;
collection1.emplace_back("black", "Ford");
collection1.emplace_back("white", "BMW");
collection1.emplace_back("yellow", "Audi");

vector<std::reference_wrapper<Car>> collection2{collection1.begin(),
                                                collection1.end()};

这样,collection2引用的对象与collection1引用的对象相同。例如:

^{pr2}$

重要:

请注意,不建议使用这种方式,因为您必须有另一个实体来管理对collection1的插入和删除,并对collection2执行适当的操作。@Serge Ballesta的答案比我的好。使用std::shared_ptr。试着去爱和拥抱指针:)

简短回答: >不能完全模仿C++中的Python <强> >。在

不像Python变量,C++变量是真实对象,而不仅仅是引用(对象),复制对底层对象没有任何作用,因此总是浅的(Python使用类型擦除来允许其变量引用任何可能的对象)。在

在C++中,也可以实现同样的设计。因为只要一个对象的任何引用仍然存在,它就必须保持活动状态,但一旦最后一个引用超出范围,就会被删除(并释放所有内存),所以这样的对象就被共享。处理共享对象的C++方法是通过<强> ^ {< CD1> }。请参见下面的内容,了解为什么它必须是一个类似指针的对象而不是一个类似于引用的对象(例如python变量)。在

这样,用C++方式使用C++,你的代码将是

std::vector<std::shared_ptr<Car>> collection1, collection2;

collection1.push_back(std::make_shared<Car>("black", "Ford"));
collection1.push_back(std::make_shared<Car>("white", "BMW"));
collection1.push_back(std::make_shared<Car>("yellow", "Audi"));

collection2.push_back(collection1[0]);
collection2[0]->color = "blue";
std::cout<<collection1[0]->color;   // "blue"

std::shared_ptr<T>的行为类似于指针,但这与引用非常相似(其语法与指针不同,但实现方式相同)。在


注意,在C++中,设计一个与Python变量相同的函数,即^ {< CD1>},但使用^ {CD5>}代替^ {< CD6>},并保证一个有效对象(无空/空引用/指针)是不可能的。原因是.运算符不能重载。例如

^{pr2}$

然后我们可以让代码

shared_reference<car> Car;
Car.color = "blue";

工作。这就是C++的原理。这意味着对于间接寻址,应该使用指针。在

在C++语言中,标准集合实际上包含对象,而在其他语言中,如python或java,它们实际上包含了存储在别处的对象的引用EME>(或指针)。但是,由于C++不包含垃圾回收,所以必须明确管理对象的生命周期别处。在

设计的结果是,为了允许在两个不同的集合中使用同一个对象,您<>强>必须/s>使用指针或引用集合(谨防,C++不直接允许引用集合;但是,为此创建了{{CD1>})。在

根据您的用例,您可以使用原始指针(如果已经管理了实际对象的生存期),也可以使用内部管理引用计数的智能指针(这里是std::shared_ptr),以确保在销毁最后一个shared_ptr时自动销毁该对象。这离Python对对象的引用不远,前提是您知道最后一个shared_ptr的销毁实际上会销毁对象(*)。换一种说法,如果你不想让它悬挂起来,就不要保留任何其他的指针或引用。在

或者,如果集合不是对称的,也就是说,如果一个集合实际包含所有对象,而另一个集合只包含对前一个集合的对象的引用-引用将是您的最佳选择,而第二个集合可能是std::vector<std::reference_wrapper<Car>>。在


根据MvG注释添加。在

Python对象和C++ SyrdY-PTR之间可能存在恼人的区别。Python有一个完整的垃圾收集器,它非常聪明,可以检测循环引用,并在没有外部引用时立即销毁循环。示例:

>>> b = ['x']
>>> a = ['y']
>>> b.append(a)
>>> a.append(b)
>>> a
['y', ['x', [...]]]
>>> b
['x', ['y', [...]]]

a包含对b的引用,其中包含对a的引用。。。在

如果a被删除(或超出范围),b仍将包含整个链

^{pr2}$

但如果a和b都被删除(或超出范围),gc将检测到不再有外部ref,并将销毁所有内容。在

遗憾的是,如果您使用^ {CD2>}来管理C++对象的循环,因为它只使用本地REF计数,每个对象都有一个REF到另一个,并且它们永远不会被删除,即使它们将超出范围,这将导致内存泄漏。例如:

struct Node {
    int val;
    std::shared_ptr<Node> next;
};

a = make_shared<Node>();  // ref count 1
b = make_shared<Node>();
a.next = std::shared_ptr<Node>(b);
b.next = std::shared_ptr<Node>(a); // ref count 2!

地狱来了:即使a和b都超出范围,ref计数仍然是1,共享指针永远不会删除它们的对象,没有循环引用通常会发生什么。程序员必须明确地处理这个问题并打破循环(并禁止它发生)。例如,b.next = make_shared<Node>();在b超出范围之前就足够了。在

相关问题 更多 >

    热门问题