C++ 将带有指针的构造函数复制到自己的类
假设我们有这样一个类:C++ 将带有指针的构造函数复制到自己的类,c++,c++11,c++14,C++,C++11,C++14,假设我们有这样一个类: class C { public: C() {} virtual ~C() noexcept { if (c) { delete c; } } protected: int a; float b; C* c; } 如何正确实现复制和移动构造函数?通常,您只需调用需要复制的对象的复制构造函数,但由于它是同一个类,您将如何正确处理它?这就是智能指针被发明的原因之一。它们是自我管理的,所以您需要做的唯一一件事就是在您需要的时候设置它们,当不再使用时,它们
class C
{
public:
C() {}
virtual ~C() noexcept { if (c) { delete c; } }
protected:
int a;
float b;
C* c;
}
如何正确实现复制和移动构造函数?通常,您只需调用需要复制的对象的复制构造函数,但由于它是同一个类,您将如何正确处理它?这就是智能指针被发明的原因之一。它们是自我管理的,所以您需要做的唯一一件事就是在您需要的时候设置它们,当不再使用时,它们负责释放内存
#include <memory>
class C
{
public:
C() {}
protected:
int a;
float b;
std::shared_ptr<C> c;
}
#包括
C类
{
公众:
C(){}
受保护的:
INTA;
浮球b;
std::共享的ptr c;
}
附言:我没有问,但是你需要一个指向自我的指针的情况并不常见;你确定你的情况没有解决办法吗
通常,您只需调用需要复制的对象的复制构造函数,但由于它是同一个类,您将如何正确处理它
以与调用同一类的析构函数相同的递归方式。由于析构函数承担指向对象的所有权,因此必须进行深度复制
移动很简单,只需清除指针,这样当从对象中移动的对象被销毁时,指针就不会被删除。就像任何其他拥有的原始指针一样
C(const C& other): a(other.a), b(other.b), c(other.c) {
if(other.c)
this->c = new C(*other.c);
}
C(C&& other): a(other.a), b(other.b), c(other.c) {
other.c = nullptr;
}
请记住,堆栈大小将限制数据结构的递归。如果将内存管理留在类之外,那么可以使用循环而不是递归来迭代链接对象
如何正确实现复制和移动构造函数
移动很容易:将受害者的指针复制到目标,然后将受害者的指针设为空,以表明它不再拥有任何东西。当然,还要复制其他值
对于复制,您需要选择所需的语义:唯一所有权(在这种情况下不允许复制)、共享所有权(在这种情况下增加引用计数,并更改析构函数以减少引用计数)、深度复制(在这种情况下分配新对象,复制旧对象)或其他
通常,您只需调用需要复制的对象的复制构造函数,但由于它是同一个类,您将如何正确处理它
这不一定是个问题。如果指针链在某个地方结束,您将递归地复制该链上的所有内容;尽管编写循环以避免不确定递归可能更好。如果没有结束,那么它必须是循环的,因此您需要检查是否返回到开始的对象。好的,问题的目的还不清楚,编写复制和移动操作符的方式将取决于您想要的行为:
- 是否要在移动时将从属C的所有权移动到新类
- 如果是副本,是否要复制下级C?(深拷贝)
- 复制时,目标C是否应与源C共享从属C
class C
{
public:
// custom destructor == rule of 5
C() {}
C(C&& r) noexcept
: _child { r._child }
{
r._child = nullptr;
}
C& operator=(C&& r) noexcept {
swap(r);
return *this;
}
C(const C& r)
: _child { r._child ? r._child->clone() : nullptr }
{
}
C& operator=(const C& r) {
C tmp { r };
swap(tmp);
return *this;
}
// test for null is not necessary
virtual ~C() noexcept { delete _child; }
public:
void swap(C& r) noexcept {
using std::swap;
swap(_child, r._child);
}
// in case C is a polymorphic base class
virtual C* clone() const {
return new C { *this };
}
private:
C* _child = nullptr;
};
c
在哪里初始化?为什么有c
而有this
?无论如何,两个操作符都应该将c
设置为指向新对象。投票关闭,因为不清楚。我可以回答,例如一个单链表,这可能是,但它可能是无数的其他东西。如果你要处理原始指针和内存管理,我建议你使用它,这取决于新的C实例应该指向哪个C实例。你需要告诉我们更多关于你试图用这种结构解决的问题。智能指针对于链表来说是非常不合适的,这可能是(而且看起来很可能是)。特别是当列表可能是循环的时候。我认为这是一个糟糕的建议。你是对的,这就是我为什么写PS的原因。OP没有说明任何关于应用程序的信息,因此没有进一步的信息,这是一个完全有效的解决方案。我不知道我对投票的感觉如何,因为它是如何被使用的。刀子可以用来杀人,尽管剪刀可能比刀子更好,但刀子仍然是切割纸张等物品的完美选择。对于无保留建议,重要的是在最有可能的情况下,它会失败或增加不合理的开销。当我写“坏”建议时,我的意思是“最有可能直接有害”的建议,并不是说它不完美。@Cheersandhth.-Alf不用担心,无论如何,你说OP不清楚是对的:)对此有一个评论,但如果你使用此解决方案,请在所有非复制构造函数中将c
初始化为nullptr
@约翰说得对。我假设初始化在那里,但为了简单起见,没有给出示例代码。嗯,在循环列表的情况下会发生什么,这里?这也是问题缺乏明确性的一个方面@干杯-阿尔夫许多这样的维度之一:)