23

这是在 g++ 4.7 和 vs2012 (cl17) 中产生不同输出的代码。

#include <iostream>

using namespace std;

class A
{
public:
    A() { cout << "1" << endl; }
    ~A() { cout << "2" << endl; }
};

class B : public A
{
public:
    B() { cout << "3" << endl; }
    ~B() { cout << "4" << endl; }
};

void func(A a) {}

int main()
{
    B b;
    func(b);
    return 0;
}

GCC 输出是13242,而 cl 输出132242

为什么 cl 编译器A在堆栈上创建副本时会生成第二个对象,目的是什么?

4

3 回答 3

5

这似乎是一个编译器错误。
C++ 标准不使用术语对象切片,您将类型的对象传递B给接收类型参数的函数A。编译器将应用通常的重载决议来找到合适的匹配。在这种情况下:
Base 类A 具有编译器提供的复制构造函数,它将引用A并且在没有其他转换函数的情况下这是最佳匹配并且应该由编译器使用。

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

于 2012-11-14T08:40:45.280 回答
0

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

  1. 当类包含存在复制构造函数的类的成员对象时。
  2. 当类派生自存在复制构造函数的基类时。
  3. 当类声明一个或多个虚函数时
  4. 当类派生自一个或多个基类是虚拟的继承链时。

我们可以看到 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) 
于 2012-11-25T12:58:39.630 回答
-3

您遇到了编译器的错误。

正确的功能解释如下:


该函数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 在调用结束时被销毁时,会进行额外的 ~A()func调用。

于 2012-11-14T08:14:53.917 回答