从重载的复制构造函数中调用默认复制构造函数
我需要编写一个复制构造函数来深度复制 std::shared_ptr
的内容。然而,类中还定义了一堆变量int a, b, c, d, e;
。有没有一种方法可以在我的新重载代码中生成默认的复制构造函数代码(或调用默认的复制构造函数)。
这是一个带有注释的代码片段,希望能够澄清该问题。
class Foo {
public:
Foo() {}
Foo(Foo const & other);
...
private:
int a, b, c, d, e;
std::shared_ptr<Bla> p;
};
Foo::Foo(Foo const & other) {
p.reset(new Bla(*other.p));
// Can I avoid having to write the default copy constructor code below
a = other.a;
b = other.b;
c = other.c;
d = other.d;
e = other.e;
}
I need to write a copy constructor that deep copies the contents of a std::shared_ptr
. However, there are a bunch of variable int a, b, c, d, e;
also defined in the class. Is there a way to generate the default copy constructor code (or call the default copy constructor) inside my new overloaded one.
Here is a code snippet with a comment that hopefully clarifies the issue.
class Foo {
public:
Foo() {}
Foo(Foo const & other);
...
private:
int a, b, c, d, e;
std::shared_ptr<Bla> p;
};
Foo::Foo(Foo const & other) {
p.reset(new Bla(*other.p));
// Can I avoid having to write the default copy constructor code below
a = other.a;
b = other.b;
c = other.c;
d = other.d;
e = other.e;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我一直认为像这样的问题应该至少有一个来自标准的答案引用,供未来的读者参考,所以就在这里。
该标准第 12.8.4 条规定:
这意味着,当类定义确实显式声明复制构造函数时,不会隐式声明复制构造函数。因此,如果您显式声明一个,则隐式的不存在,因此您无法调用它。
I always think that questions like this should have at least one answer quote from the standard for future readers, so here it is.
§12.8.4 of the standard states that:
This implies that when a class definition does explicitly declare a copy constructor, one is not declared implicitly. So if you declare one explicitly, the implicit one does not exist, so you can't call it.
这是我写这篇文章时的问题代码:
上面的代码很可能错误,因为
默认构造函数留下
a
,b
、c
、d
和e
未初始化,并且代码不负责赋值复制,并且
new Bla(other.p)
要求Bla
有一个采用std::shared_ptr
的构造函数,即极不可能。对于
std::shared_ptr
,这必须是 C++11 代码才能在形式上正确的语言。但是,我相信这只是使用编译器可用内容的代码。因此我认为相关的 C++ 标准是 C++98,并进行了 C++03 修订案的技术修正。即使在 C++98 中,您也可以轻松利用内置(生成的)复制初始化,例如请
注意,此代码确实在默认构造函数中正确初始化,并且负责复制分配(使用 交换惯用法),并且不需要特殊的智能指针感知
Bla
构造函数,而是使用普通的Bla
复制构造函数进行复制。Here’s the question code as I’m writing this:
The above code is most likely wrong, because
the default constructor leaves
a
,b
,c
,d
ande
uninitialized, andthe code does not take charge of assignment copying, and
the expression
new Bla(other.p)
requires thatBla
has a constructor taking astd::shared_ptr<Bla>
, which is extremely unlikely.With
std::shared_ptr
this would have to be C++11 code in order to be formally correct language-wise. However, I believe that it’s just code that uses what’s available with your compiler. And so I believe that the relevant C++ standard is C++98, with the technical corrections of the C++03 amendment.You can easily leverage the built-in (generated) copy initialization, even in C++98, e.g.
Note that this code does initialize correctly in the default constructor, does take charge of copy assignment (employing the swap idiom for that), and does not require a special smart-pointer-aware
Bla
constructor, but instead just uses the ordinaryBla
copy constructor to copy.编写内置深度复制的
shared_ptr
变体会更容易。这样,您不必为主类编写复制构造函数;只是针对这个特殊的deep_copy_shared_ptr
类型。您的deep_copy_shared_ptr将有一个复制构造函数,并且它本身会存储一个shared_ptr。它甚至可以隐式转换为shared_ptr
,以使其更易于使用。It would be easier to write a variation on
shared_ptr
that has deep copying built into it. That way, you don't have to write a copy constructor for your main class; just for this specialdeep_copy_shared_ptr
type. Yourdeep_copy_shared_ptr
would have a copy constructor, and it would store ashared_ptr
itself. It could even have an implicit conversion toshared_ptr
, to make it a bit easier to use.那是不可能的。要么您编写一个自定义复制构造函数(完全由您自己编写),要么由编译器为您编写。
请注意,如果您编写复制构造函数,那么您可能还需要复制赋值和析构函数,因为编写这三个资源管理函数中的任何一个都意味着您正在管理资源。然而,使用复制和交换习惯用法,您只需在复制构造函数中编写一次复制逻辑,然后根据复制构造函数定义赋值运算符。
除此之外,我不完全确定您为什么使用
shared_ptr
。shared_ptr
的要点是允许多个指针安全地指向同一个对象。但你不是共享被指者,而是深度复制它。也许您应该使用原始指针,并在析构函数中释放它。或者,更好的是,用clone_ptr
替换shared_ptr
,然后完全消除复制构造函数、复制赋值和析构函数。That's not possible. It's either you write a custom copy constructor (entirely on your own) or the compiler writes it for you.
Note that if you write a copy constructor then you probably need a copy assignment and a destructor as well, because writing any of these three resource-management functions implies you're managing a resource. With the copy-and-swap idiom, however, you only need to write the copy logic once, in the copy constructor, and you then define the assignment operator in terms of the copy constructor.
Aside from that, I'm not entirely sure why you're using a
shared_ptr<>
. The point of ashared_ptr<>
is to allow multiple pointers to safely point at the same object. But you're not sharing the pointee, you deep-copy it. Maybe you should use a raw pointer instead, and free it in the destructor. Or, better yet, replace theshared_ptr<>
with aclone_ptr
, and then eliminate the copy constructor, copy assignment and destructor altogether.据我所知,但无论如何,您可以(并且应该)做的是使用初始值设定项列表:
这不会使您免于编写,但它可以使您免于分配(不必要的)默认构造的可能的性能损失成员(尽管在这种情况下可能没问题)以及这可能带来的许多其他陷阱。如果可能,请始终在构造函数中使用初始值设定项列表。
顺便说一下,Kerrek 的评论是正确的。如果您无论如何都进行深层复制,为什么还需要
shared_ptr
呢?在这种情况下,unique_ptr
可能更合适。除了它的好处之外,shared_ptr
并不是一个通用的无需再考虑的释放解决方案,您应该始终考虑是否需要智能指针以及什么类型的智能指针最合适。Not to my knowledge, but what you can (and should) do is use an initializer list, anyway:
This won't save you from the writing, but it will save you from the possible performance penalty of assigning (unneccessarily) default-constructed members (although in this case it might be fine) and the many other pitfalls this could bring. Always use initializer lists in constructors, if possible.
And by the way, Kerrek's comment is right. Why do you need a
shared_ptr
, if you make a deep copy anyway. In this case aunique_ptr
might be more appropriate. Besides it's benefitsshared_ptr
is not a general no-more-to-think-about-deallocation solution and you should always think if you need a smart pointer and what type of smart pointer is most appropriate.默认赋值运算符与默认复制构造函数具有相同的代码。尽管您无法从重载中调用默认的复制构造函数,但在 allocateemnt so 中存在相同的代码。
应该给你你需要的东西。
编辑:没关系,它实际上是相同的代码,并且显式声明复制构造函数可以防止它生成:(
The default assignment operator has the same code as the default copy constructor. Though you can't call the default copy constructor from your overload, the same code exists in the assignemnt so.
should get you what you need.
Edit: Nevermind it is in fact the same code, and explicitly declaring the copy construtor prevents it generation :(
一个技巧可以是将这些“简单”字段放入基类中
,然后,如果向 Foo 添加任何简单字段,则可以将它们放入 FooBase 中,而无需担心更新复制构造函数。
如果需要,不要忘记添加类似的复制赋值运算符。在现代编译器上,还添加移动构造函数和移动赋值运算符。这两个可能可以默认,因为您不需要在移动操作上进行深层复制。
One trick can be to put those "simple" fields into a base class
Then later, if you add any simple fields to Foo, you can put them to FooBase and don't need to worry about updating the copy constructor.
Don't forget to add a similiar copy assignment operator, if needed. And on modern compilers, also add move constructor and move assignement operator. These two can probably be defaulted, as you don't need deep copy on move operations.