(使用Visual C ++ 2010,在调试中进行编译并关闭优化)
我有以下非常简单的课程:
class exampleClass
{
public:
exampleClass()
{
cout << "in the default ctor" << endl;
}
private:
exampleClass (const exampleClass& e)
{
cout << "in the copy ctor" << endl;
}
};
当我尝试使用以下main编译它时:
#include <iostream>
using namespace std;
int main()
{
exampleClass e1=exampleClass();
return 0;
}
我收到编译错误:
'exampleClass::exampleClass' : cannot access private
member declared in class 'exampleClass'
当我从复制文件中删除访问修饰符“private”时,程序会编译并打印 :
in the default ctor
为什么会这样?如果编译器无论如何都不会调用copy ctor,为什么它会烦扰我?
由于有些人错过了第一行(至少在一些编辑之前),我将重复它:
答案 0 :(得分:16)
这种类型的初始化称为复制初始化。我相信C ++ 11标准中的以下条款适用于此处(第8.5.16段,第204页):
如果初始化是直接初始化,或者是初始化 copy-initialization所在的cv-nonqualified版本的源码 type与类的类相同,或者是类的派生类 目的地,建设者被考虑。适用的构造函数 列举(13.3.1.3),并通过过载选择最好的一个 决议(13.3)。如此选择的构造函数被调用以初始化 对象,初始化表达式或表达式列表作为其对象 参数(一个或多个)。如果没有构造函数适用,或者重载解析是 暧昧,初始化是不正确的。
在这种情况下,最适用的构造函数是copy ctor,它是私有的,因此是错误消息。
为了进一步回答您的问题,当复制版本是私有的时,由于标准规定的规则,您的程序根本不允许通过编译器检查。当您公开复制ctor时,程序将变为有效,但对复制程序的调用将被优化掉。
编辑: 好的,详细说明上一段。你在这里处理所谓的copy elision。虽然在这种情况下可以进行复制省略,但标准要求您为班级提供可访问复制文件。
答案 1 :(得分:12)
exampleClass e1=exampleClass();
这将首先使用默认构造函数创建临时exampleClass
,然后使用复制构造函数将其复制到e1
。这将调用私有拷贝构造函数,从而给你错误。使用默认构造函数实例化类实例的正确方法是:
exampleClass e1;
答案 2 :(得分:4)
编译器需要在那里 bug 。虽然可以省略副本,但标准要求可以为该类型的构造访问复制构造函数。当然,您可以简化代码并完全避免复制构造:
exampleClass e1; // Will call exampleClass::exampleClass()
答案 3 :(得分:1)
exampleClass e1=exampleClass();
与:
相同exampleClass e1(exampleClass());
即它调用(私有)复制构造函数。
答案 4 :(得分:1)
这是因为在编译时,编译器会检查用户尝试访问的函数是否真的可以访问。因此,当您使用exampleClass e1=exampleClass();
时,它首先检查复制构造函数是否可访问。它会发出错误,因为复制构造函数不是私有的。 记住,在这一点上,编译器还没有进入优化阶段,在那里它做了一些聪明的事情,就是跳过复制构造函数。
当你将copy-constructor公之于众时,编译器成功地完成了解析代码的阶段,并确保一切都是可访问的并且是有序的(实际上还有更多),然后在优化阶段,通常在“释放”模式下,它会执行聪明的操作并绕过复制构造函数的使用。但是,如果您在“调试”模式下尝试相同的代码,您会看到复制构造函数被调用。
答案 5 :(得分:1)
每个人都解释了如何实例化一个对象,而@Grigory Javadyan在复制省略方面做得很好。看起来,即使在调试模式下,MSVC也会进行这种优化(所谓的返回值优化)。
exampleClass e1=exampleClass();
与
相同exampleClass giveExample()
{
return exampleClass();
}
exampleClass e1 = giveExample();
您将看到不会调用复制ctor。
但是在这里:
exampleClass giveExample()
{
exampleClass example;
return example;
}
exampleClass e1 = giveExample();
您将看到另一个输出行:
in the copy ctor
因为您强制编译器首先生成一个对象然后将其返回。
PS。链接#2来自另一个Q&amp; A站点。我希望这不是问题。
答案 6 :(得分:0)
因为复制构造函数是私有的..
您的代码是
exampleClass()
答案 7 :(得分:0)
这不是你如何在C ++中实例化一个对象。如果你想在堆栈上分配它,你写:
exampleClass e1;
并且你已经完成了,因为exampleClass'构造函数不接受任何参数。
否则,如果要在堆上分配它,请编写:
exampleClass e1 = new exampleClass();
您编写它的方式实际上创建了一个临时对象,并在该临时对象上调用复制构造函数来创建e1。问题是你的copy-ctor是私有的,所以编译器的错误信息。
答案 8 :(得分:0)
当你写
exampleClass e1 = exampleClass()
与写作相同
exampleClass e1( exampleClass() );
调用副本ctor。