1
#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; 
} 
+1

你应该添加更多的文字来解释你在找什么。 – Sean

+4

注意'const Base * pb =&Base()'是* not * ok,并且应该从编译器产生一个警告 – stijn

+2

这个'const Base * pb =&Base();'肯定不是* OK。这是一个微软的扩展。 – ybungalobill

回答

1

compiler's reported errors给你答案:

const Base &qb = Base() ; 

调用CBase类的拷贝构造函数,这在您的顶级示例中是私有的,因此无法访问。

而且,这样的:

const Base *pb = &Base(); 

是不确定的行为,并会导致崩溃,因为pb点临时对象。更具体地说,什么该行代码的作用是:

  1. 堆栈上的一个临时基地对象创建房间,并调用基()构造函数就可以了
  2. 分配pb在临时对象的地址点。
  3. Destroy the temporary object, because it hasn't been assigned to anything。 (您只将其地址指定给了一个不会影响其寿命的指针)。
  4. pb现在指向垃圾。
+0

您可能有兴趣查看u p @ sehe的回答和评论。 (不,我不是说你错了,我只是怀疑你是多么的正确)。 –

+0

'const Base&qb = Base();'应该是引用绑定,你能解释为什么应该调用复制构造函数吗? – fefe

+0

@fefe大卫罗德里格斯有更详细的答案。 – Crashworks

2

忽略const *Base = &Base();这需要一个暂时的地址,和在大多数情况下将导致未定义行为作为表达式中的临时将被破坏的端部与所述指针的任何非关联是UB

当您尝试将常量引用绑定到临时对象时,该语言指出该操作(语义上)是将临时对象复制到未命名的变量,然后将引用绑定到该变量。

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()不能通过编译)它告诉你。

现在的标准允许编译器的Elid的变量副本,特别是在该行中,编译器被允许放置__tmpf()在完全相同的位置在存储器结果,避免执行复制。在第二种情况下,编译器已经检查了允许复制(复制构造函数可用),但已经优化了副本,因此它不调用该函数。

为什么它认为构造函数是可用的,即使它没有被定义?那么,这是独立编译模型的一部分,编译器在决定构造函数是否有效时只检查当前的翻译单元,并且不知道复制构造函数是否可以在不同的TU中使用。因为副本没有被使用,所以在二进制文件中不会有复制构造函数的调用,并且链接器不需要解析该符号,因此它编译并链接正常。


有关详细信息,标准的范围之外,大多数编译器(我所知道的),实现由大对象的值(一个不适合在寄存器中)返回将隐藏指针传递给该函数,因此调用方为本地堆栈中的__tmp保留空间,并将该指针传递给f()。使用这个调用约定,在这种情况下返回的对象根本就不存在,即使调用不是用来初始化一个新对象也是如此。

在示例中,返回的类型适合寄存器的情况下,很多编译器会在返回前将结果值存储在寄存器中。这是确切的情况复制(概念)也要做,因为你不能绑定到寄存器的引用,但再次调用拷贝构造函数会被省略,编译器只是存储寄存器__tmp的位置。

+0

const * Base =&Base()也被GCC所允许(尽管这显然是无稽之谈)。 – Crashworks

+0

@Crashworks你是对的,我很习惯在程序中出现*错误*,我希望编译器不要接受它。代码没有错误,但可能是语义无效的(你不能取消引用指针,因为这将是UB)。无论如何,GCC会警告你做错了事。感谢您指出错误。 –