C++ c++;:按值复制到函数参数在vs2012中生成两个对象

C++ c++;:按值复制到函数参数在vs2012中生成两个对象,c++,visual-c++,gcc,visual-studio-2012,gcc4.7,C++,Visual C++,Gcc,Visual Studio 2012,Gcc4.7,下面是在g++4.7和vs2012(cl17)中生成不同输出的代码 #包括 使用名称空间std; 甲级 { 公众: A(){cout您遇到了编译器的错误 正确的功能说明如下: 函数func需要创建对象的副本(但要注意切片) 那么,发生的情况是: int main() { // create object B, which first creates the base object A B b; // create object A, using this copy co

下面是在g++4.7和vs2012(cl17)中生成不同输出的代码

#包括
使用名称空间std;
甲级
{
公众:

A(){cout您遇到了编译器的错误

正确的功能说明如下:


函数
func
需要创建对象的副本(但要注意切片)

那么,发生的情况是:

int main()
{
    // create object B, which first creates the base object A
    B b;
    // create object A, using this copy constructor : A( const B& )
    func(b);
}

额外的~A()调用是在
func
调用结束时复制构造的对象A被销毁时进行的。

这似乎是一个编译器错误。
C++标准不使用强>对象切片< /St>,将一个对象的代码<代码> b/COD>传递到一个函数,接收一个类型<代码> A/<代码>的参数。编译器将应用通常的过载分辨率来找到合适的匹配。 基类
A
具有编译器提供的复制构造函数,该构造函数将引用
A
,在没有其他转换函数的情况下,这是最佳匹配,编译器应该使用它


请注意,如果有更好的转换可用,则将使用它。例如:如果
A
有一个构造函数
A::A(B const&)
,除了复制构造函数之外,还将使用此构造函数,而不是复制构造函数。

C++编译器将在以下情况下合成默认的复制构造函数。(从C++对象模型内部)

  • 当类包含存在副本构造函数的类的成员对象时
  • 当该类派生自存在副本构造函数的基类时
  • 当类声明一个或多个虚拟函数时
  • 从继承链派生类时,其中一个或多个基类是虚拟的
  • 我们可以看到类A不在这4种情况下。所以cl不为它合成默认的复制构造函数。也许这就是为什么2个临时A对象被构造和销毁的原因

    从disassemly窗口中,我们可以看到以下代码,没有调用A::A.:

    B b;
    00B317F8  lea         ecx,[b]  
    00B317FB  call        B::B (0B31650h)  
    00B31800  mov         dword ptr [ebp-4],0  
    func(b);
    00B31807  mov         al,byte ptr [ebp-12h]  
    00B3180A  mov         byte ptr [ebp-13h],al  
    00B3180D  mov         byte ptr [ebp-4],1  
    00B31811  movzx       ecx,byte ptr [ebp-13h]  
    00B31815  push        ecx  
    00B31816  call        func (0B31730h)  
    
    但是如果我们将析构函数设为虚拟的,我们将得到下面的反汇编代码,我们可以看到调用了A::A。然后结果正如预期的那样,只创建了1个对象

    B b;
    00331898  lea         ecx,[b]  
    0033189B  call        B::B (03316A0h)  
    003318A0  mov         dword ptr [ebp-4],0  
    func(b);
    003318A7  push        ecx  
    003318A8  mov         ecx,esp  
    003318AA  mov         dword ptr [ebp-1Ch],esp  
    003318AD  lea         eax,[b]  
    003318B0  push        eax  
    003318B1  call        A::A (0331900h)  
    003318B6  mov         dword ptr [ebp-20h],eax  
    003318B9  call        func (03317D0h) 
    

    在VS2010上进行了测试,结果是“132242”,而发布版本仅生成13242,但在添加(常量A&A)时不生成调试版本{cout@ElektroKraut:不。只是不。只在必要时使用
    虚拟
    。另一方面,如果不是
    虚拟
    ,则
    受保护
    可能是个好主意;如果使用
    -Wdelete非虚拟数据或
    编译,编译器将为您标记所有不正确的使用。在Visual Studio 6上测试,结果相同,这是一个好主意这是一个很长的bug,自1998年(VS 6发布时)以来已有12年多了。在“cl输出”中,在
    func()
    的末尾有两次调用
    ~a
    。这似乎是个问题。@KevinBallard我解释了应该发生什么。我将这种行为称为编译器的bug。有些人会称之为扩展;)@应该发生什么已经是个问题了;这只是一个重述。
    a
    的显式琐碎复制构造函数修复了VS2010中的问题,这对我来说没有多大意义。@Gorpik:有更多的理由相信这是一个编译器错误。重载解析规则很复杂,但这种情况似乎是o更常见的错误之一。编译器错误是否有意义?强制编译器生成稍有不同的符号表示可以很容易地隐藏错误。@Gorpik VS2008Downvoter中的相同行为:与不合理的downvoting不同,请添加一个解释,说明为什么您认为此答案是错误的,最好是引用标准中的引用我们在这里帮助事业。
    B b;
    00331898  lea         ecx,[b]  
    0033189B  call        B::B (03316A0h)  
    003318A0  mov         dword ptr [ebp-4],0  
    func(b);
    003318A7  push        ecx  
    003318A8  mov         ecx,esp  
    003318AA  mov         dword ptr [ebp-1Ch],esp  
    003318AD  lea         eax,[b]  
    003318B0  push        eax  
    003318B1  call        A::A (0331900h)  
    003318B6  mov         dword ptr [ebp-20h],eax  
    003318B9  call        func (03317D0h)