我知道如果你没有自己实现,编译器有时会提供一个默认的复制构造函数.我很困惑这个构造函数到底是做什么的.如果我有一个包含其他对象的类,其中没有一个具有声明的复制构造函数,那么行为是什么?例如,像这样的类:
class Foo {
Bar bar;
};
class Bar {
int i;
Baz baz;
};
class Baz {
int j;
};
Run Code Online (Sandbox Code Playgroud)
现在,如果我这样做:
Foo f1;
Foo f2(f1);
Run Code Online (Sandbox Code Playgroud)
默认的复制构造函数会做什么?将编译器生成的复制构造函数Foo调用编译器生成的构造函数Bar进行复制bar,然后调用编译器生成的复制构造函数Baz吗?
根据C++ 11规则,默认情况下会生成6个内容(默认构造函数,复制构造函数,移动构造函数,复制赋值,移动赋值和析构函数).根据第二个规则,当定义任何自定义副本,移动或析构函数时,不会生成这些默认操作.但是在我之后的代码中并非如此.但是这段代码无法编译错误
call to implicitly deleted copy constructor of 'Uni'
Run Code Online (Sandbox Code Playgroud)
当我为Uni编写自己的复制构造函数时,一切正常.(在代码中注释,供参考)
任何想法都非常感激.
最后,我在Mac上运行它,使用LLVM编译器运行Xcode.
非常感谢...
#include <iostream>
class A
{
public:
A(int i) :num{i}
{
std::clog<< "ctor A() num = " << num << "\n";
}
A( A const &aRef)
:num{aRef.num}
{
std::clog << " copy ctor A( A const &aRef) num = " << num << "\n";
}
int value()
{
return num;
}
private:
int num;
};
class Uni
{
public:
Uni(A* aptr) : up{aptr}
{
std::clog << " …Run Code Online (Sandbox Code Playgroud) 我曾经认为,当遵循最佳实践时,C++的对象模型非常强大.
就在几分钟前,我意识到我以前没有过.
考虑以下代码:
class Foo
{
std::set<size_t> set;
std::vector<std::set<size_t>::iterator> vector;
// ...
// (assume every method ensures p always points to a valid element of s)
};
Run Code Online (Sandbox Code Playgroud)
我写了这样的代码.直到今天,我还没有看到它的问题.
但是,考虑到它更多,我意识到这个类非常破碎:
它的copy-constructor和copy-assignment 复制里面的迭代器vector,这意味着它们仍然会指向旧的 set!毕竟新的不是真正的副本!
换句话说,我必须手动实现copy-constructor,即使这个类没有管理任何资源(没有RAII)!
这令我惊讶.我以前从未遇到过这个问题,我也不知道有什么优雅的方法来解决它.关于它的思考多一点,在我看来,那拷贝构造是默认不安全的 -事实上,在我看来那类应该不会是在默认情况下拷贝,但因为他们的实例变量之间的任何一种耦合的风险再现默认副本- 构造函数无效.
迭代器是否从根本上说不安全?或者,默认情况下类是否真的不可复制?
我在下面想到的解决方案都是不可取的,因为它们不会让我利用自动生成的复制构造函数:
根据与c ++ 0x相关的N2628,非静态数据成员初始化器可以被显式定义的构造函数覆盖,但它似乎对隐式定义的复制构造函数略显模糊.
特别是,我注意到使用Apple clang 3.0版时,行为会根据结构(或类)是否为POD而有所不同.
以下程序返回输出"1",表示复制构造函数忽略右侧,而是替换新的非静态数据成员初始值设定项(在此示例中,X :: a的布尔值为true) ).
#include <iostream>
#include <string>
struct X
{
std::string string1;
bool a = true;
};
int main(int argc, char *argv[])
{
X x;
x.a = false;
X y(x);
std::cout << y.a << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
但是,令人困惑的是,如果你注释掉string1:
// std::string string1;
Run Code Online (Sandbox Code Playgroud)
然后行为按照我的预期工作(输出为"0"),大概是因为没有隐式生成的复制构造函数,因此复制了数据.
C++ 0x规范是否真的建议允许隐式定义的拷贝构造函数不复制右侧的内容?这不是那么有用和不直观吗?我发现非静态成员初始化程序功能非常方便,但如果这是正确的行为,那么由于其棘手和不直观的行为,我将明确地避免使用该功能.
请告诉我,我错了?
更新:此错误已在Clang源存储库中修复.见此修订版.
更新:此错误在Apple clang 3.1版(标签/ Apple/clang-318.0.45)中出现(基于LLVM 3.1svn).这个版本的clang是作为Lion的Xcode 4.3的一部分发布的.
我有一个结构,MyStruct有一个私有成员private bool[] boolArray;和一个方法ChangeBoolValue(int index, bool Value).
我有一个班,MyClass有一个领域public MyStruct bools { get; private set; }
当我从现有的MyStruct对象创建一个新的MyStruct对象,然后应用方法ChangeBoolValue()时,两个对象中的bool数组都会被更改,因为引用而不是引用的引用被复制到新对象.例如:
MyStruct A = new MyStruct();
MyStruct B = A; //Copy of A made
B.ChangeBoolValue(0,true);
//Now A.BoolArr[0] == B.BoolArr[0] == true
Run Code Online (Sandbox Code Playgroud)
有没有办法强制副本实现更深层次的副本,或者有没有办法实现这个不会有同样的问题?
我特意将MyStruct设为结构,因为它是值类型,我不希望引用传播.
我只是使用此代码试验参考:
class A
{
};
class B
{
public:
B(A& a): m_a(a){}
A& m_a;
};
int main()
{
A a;
B b(a);
B b1 = b;
}
Run Code Online (Sandbox Code Playgroud)
我期待两者都B b1 = b;产生错误.相反,当我使用VS2008编译时,我只是收到警告
警告C4512:'B':无法生成赋值运算符
我理解为什么我会收到这个警告.但是编译器不应该为B b1 = b;语句生成错误吗?它就像它生成了复制构造函数但没有生成赋值运算符.这两者本身并不相互联系吗?当另一个无法生成时,为其中一个生成默认实现是否有意义?
对于过于含糊的标题感到抱歉.(由于缺乏我的英语技能).请建议一个更好的标题.
请考虑以下代码.
struct A {
typedef std::vector<double> State;
// template <class... Args>
// A(Args... args)
// : a(args...)
// {}
template <class... Args>
A(Args&&... args)
: a(std::forward<Args>(args)...)
{}
A(const A&) = default;
A(A&&) = default;
State a;
};
int main(){
A a(3,2);
A b = a; // This line triggers an error!!
}
Run Code Online (Sandbox Code Playgroud)
Gcc 4.8.0无法使用错误消息进行编译
error: no matching function for call to 'std::vector<double>::vector(A&)' : a(std::forward<Args>(args)...).
我无法理解为什么这段代码错了.在我看来,编译器应该在行中调用复制构造函数A b = a;.
但是,如果我用注释的(它只是取值)替换构造函数.它确实编译.此外,现在不需要默认复制(和移动)构造函数的行.这里发生了什么?
在C++中,如果我定义了一个复制构造函数和operator =对该类采用非const引用,那么编译器是否应该仍然为const引用提供默认版本?
struct Test {
Test(Test &rhs);
Test &operator=(Test &rhs);
private:
// Do I still need to declare these to avoid automatic definitions?
Test(const Test &rhs);
Test &operator=(const Test &rhs);
};
Run Code Online (Sandbox Code Playgroud) 可能重复:
如何在Java中复制对象?
我需要在Java中复制一个对象(即通过值而不是通过引用复制对象,以便新对象不仅仅是对旧对象的引用).我厌倦了实现clonable并且更喜欢使用复制构造函数.但是,我需要复制的类有多个需要复制的成员变量(超过100个),所以在类中添加一个新的构造函数只是为了复制(只需要在我的应用程序的一部分中使用),这似乎是一个糟糕的解决方案由于它的长度很大.
有更好的解决方案吗?我应该只使用clone()吗?我可以创建一个复制构造函数,而不是将所有字段复制到1比1,我可以反思吗?谢谢.
我基本上只需要创建一个与旧对象相同的新对象,但是有一些(大约10个中的100个)字段已更改(但我仍然需要两个对象..所以新的对象不能作为对象的引用旧的).我对任何建议持开放态度.
鉴于:
class Foo {
private:
static int cntFoos;
//... stuff...
public:
Foo() { cntFoos++; }
~Foo() { cntFoos--; }
};
Run Code Online (Sandbox Code Playgroud)
......"stuff"可以是任何属性集.(想法是有一个该类实例的计数器)
然后:
Foo aFoo;
Foo twoFoo=aFoo;
Run Code Online (Sandbox Code Playgroud)
将调用自动复制构造函数,因此我会想念这个.
有没有办法让该计数器反映自动创建的新实例?如果我实现显式复制构造函数,我将不得不逐个分配所有属性.但是,我想要一个浅的成员副本.我不需要执行深层复制,因此实现显式复制构造函数似乎需要做很多工作.