2012-05-31 44 views
14

以下是实际问题的简化版本。代码似乎不会调用Base::operator=(int),而是生成一个临时对象Derived,并将其复制。为什么不使用基赋值运算符,因为函数签名似乎完全匹配?这个简化的例子不会显示任何不良影响,但原始代码在析构函数中有一个副作用,导致各种破坏。为什么派生类不使用基类操作符=(赋值操作符)?

#include <iostream> 
using namespace std; 

class Base 
{ 
public: 
    Base() 
    { 
     cout << "Base()\n"; 
    } 

    Base(int) 
    { 
     cout << "Base(int)\n"; 
    } 

    ~Base() 
    { 
     cout << "~Base()\n"; 
    } 

    Base& operator=(int) 
    { 
     cout << "Base::operator=(int)\n"; 
     return *this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived() 
    { 
     cout << "Derived()\n"; 
    } 

    explicit Derived(int n) : Base(n) 
    { 
     cout << "Derived(int)\n"; 
    } 

    ~Derived() 
    { 
     cout << "~Derived()\n"; 
    } 
}; 

class Holder 
{ 
public: 
    Holder(int n) 
    { 
     member = n; 
    } 

    Derived member; 
}; 

int main(int argc, char* argv[]) 
{ 
    cout << "Start\n"; 
    Holder obj(1); 
    cout << "Finish\n"; 

    return 0; 
} 

的输出是:

Start 
Base() 
Derived() 
Base(int) 
Derived(int) 
~Derived() 
~Base() 
Finish 
~Derived() 
~Base() 

http://ideone.com/TAR2S

回答

19

这是一个编译器生成的operator=方法和member function hiding之间的微妙相互作用。由于Derived类没有声明任何operator =成员,因此编译器隐含地生成了一个:Derived& operator=(const Derived& source)。此运算符= 隐藏运算符=在基类中,因此无法使用。编译器仍然可以通过使用Derived(int)构造函数创建一个临时对象并使用隐式生成的赋值运算符复制该对象,从而完成分配。

因为执行隐藏的功能是隐式生成的,并且不是源的一部分,所以很难发现。

通过在构造函数int上使用explicit关键字可以发现这个问题 - 编译器会发出错误,而不是自动生成临时对象。在原始代码中,隐式转换是一个很好用的功能,所以不使用explicit

的解决方法是相当简单的,派生类可以在定义明确拉从基类:

using Base::operator=; 

http://ideone.com/6nWmx

+0

好消息!我假设'operator ='是唯一一个表现出这种行为的人(对构造函数没有意义,也没有任何其他编译器生成的方法)。 –

+0

@LuchianGrigore,我希望如此!这一个引起我们各种头部划伤,我不想再次遇到它。 –

+0

单参数(能)构造函数应该几乎总是自动让你输入'explicit'。 –

相关问题