#include "stdafx.h"
class Base
{
public:
Base(){}
virtual ~Base(){}
private:
Base(const Base &other) ; // Only declaration! No definition.
Base &operator=(const Base &other);
} ;
int _tmain(int argc, _TCHAR* argv[])
{
const Base b ; // ok
const Base *pb = &Base() ; // ok
const Base &qb = Base() ; // Illegal, why?
return 0;
}
然后,请参阅以下代码:
#include "stdafx.h"
class Base
{
public:
Base(){}
virtual ~Base(){}
public:
Base(const Base &other) ; // Only declaration! No definition.
Base &operator=(const Base &other);
} ;
int _tmain(int argc, _TCHAR* argv[])
{
const Base b ; // ok
const Base *pb = &Base() ; // ok
const Base &qb = Base() ; // It's ok! why?
return 0;
}
答案 0 :(得分:2)
忽略const *Base = &Base();
,它取一个临时的地址,并且在大多数情况下会导致未定义的行为,因为在表达式结束时临时将被销毁,并且指针的任何取消引用都是UB < / em>的
当您尝试将常量引用绑定到临时时,该语言表示该操作(在语义上)是将临时文件复制到未命名变量,然后将该引用绑定到该变量。
const type& r = f(); // where f() returns a type (not a reference)
相当于:
const type __tmp = f(); // __tmp variable created by the compiler
const type& r = __tmp;
这解释了为什么在第一种情况下,因为复制构造函数不可访问,所以不允许编译器创建__tmp
变量(const type __tmp = f()
无法编译)并且它会告诉您。
现在标准允许编译器忽略变量的副本,特别是在该行中,允许编译器将__tmp
和f()
的结果放在内存中的完全相同的位置< sup> 1 并避免执行复制。在第二种情况下,编译器已检查是否允许复制(复制构造函数可用),但已优化了复制,因此它不会调用该函数。
为什么即使没有定义构造函数也可以认为它是可用的?那么,这是单独的编译模型的一部分,编译器在决定构造函数是否有效时,只检查当前的转换单元,并且它不知道复制构造函数是否可用于不同的TU 。由于副本被省略,因此不会在二进制文件中放置对复制构造函数的调用,并且链接器不需要解析该符号,因此它可以编译和链接。
1 有关更多详细信息,并且超出了标准范围,大多数编译器(我所知道的)都实现了一个大对象的值的返回(一个不适合寄存器的对象)通过将隐藏指针传递给函数,调用者在本地堆栈中保留__tmp
的空间并将该指针传递给f()
。使用此调用约定,在这种情况下返回的对象根本不存在,即使调用未用于初始化新对象也是如此。
在示例中,返回类型适合寄存器的情况下,许多编译器会在返回之前将结果值存储在寄存器中。这是必须完成复制(概念上)的确切情况,因为您无法绑定对寄存器的引用,但是再次调用复制构造函数并且编译器只将寄存器存储在__tmp
。
答案 1 :(得分:1)
compiler's reported errors给你答案:
const Base &qb = Base() ;
调用CBase的复制构造函数,在你的顶层示例中,它是私有的,因此无法访问。
另外,这个:
const Base *pb = &Base();
是未定义的行为,会导致崩溃,因为pb
指向临时对象。更详细地说,这行代码的作用是:
pb
指向临时对象的地址。pb
现在指向垃圾。