为什么C++赋值运算符重载函数不能被继承?

来源:互联网 发布:ubuntu mate 安装pyqt 编辑:程序博客网 时间:2024/06/13 23:04
为什么C++赋值运算符重载函数不能被继承?
这个问题曾经困扰过我一阵子。请先看一下下面的源代码:

class A1{public:         int perator=(int a)         {                 return 8;         }         int operator+(int a)         {                 return 9;         }};class B1 : public A1{public:         int operator-(int a)         {                 return 7;         }};int main(){                 B1 v;         cout << (v + 2) << endl; // OK, print 9         cout << (v - 2) << endl; // OK, print 7         cout << (v = 2) << endl; // Error, see below         return 0;}


VC编译器的错误提示:
error C2679: binary '=' : no operator defined which takes a right-hand operand of type 'const int' (or there is no acceptable conversion)

意思是说编译器找不到int perator=(int a)这个成员函数可以调用。

真是怪了?明明int perator=(int a)这个函数是我从基类公有继承下来的函数,怎么编译器识别不了呢?遇到这种问题,第一反应就是查找MSDN以求得解释,微软告诉我:
“All overloaded operators except assignment (operator=) are inherited by derived classes.”
意思是说:除了赋值运算符重载函数以外,所有的运算符重载函数都可以被派生类继承。

我这个函数int perator=(int a)很不幸就是微软所说的“赋值运算符重载函数”,当然不能被继承!


可是到此为止,我心中的疑问依然没有消除。为什么“赋值运算符重载函数”不能被派生类继承呢?从C++语义上讲,不允许这个函数被派生类继承并没有充足的理由,一个类对象实例完全可以被任何一个其他类对象实例所赋值!比如一个颜色对象实例可以被一个整数赋值,甚至可以被一个小白兔实例所赋值。赋值运算符既然允许重载,就应该允许被继承,就像其他的运算符重载之后都可以被派生类继承一样。微软的解释并没有说明为什么“赋值运算符重载函数”不能被继承的幕后原因。

我又查找了C++ Primer和其他一些重量级的C++经典,可是这些书籍对此问题都避而不谈,抑或是语焉不详,我都没有找到答案。于是,我只好反身求诸己,从C++类对象的构造开始分析,结果找到了我认为是正确的答案:


1,每一个类对象实例在创建的时候,如果用户没有定义“赋值运算符重载函数”,那么,编译器会自动生成一个隐含和默认的“赋值运算符重载函数”。所以,B1的实际上的声明应该类似于下面这种情况:


class A1{public:         int perator=(int a)         {                 return 8;         }         int operator+(int a)         {                 return 9;         }};class B1 : public A1{public:         B1& operator =(const B1& robj); // 注意这一行是编译器添加的         int operator-(int a)         {                 return 7;         }};


2,C++标准规定:如果派生类中声明的成员与基类的成员同名,那么,基类的成员会被覆盖,哪怕基类的成员与派生类的成员的数据类型和参数个数都完全不同。显然,B1中的赋值运算符函数名operator =和基类A1中的operator =同名,所以,A1中的赋值运算符函数int perator=(int a);被B1中的隐含的赋值运算符函数B1& perator =(const B1& robj);所覆盖。 A1中的int perator=(int a);函数无法被B1对象访问。


3,程序中语句v = 2实际上相当于v.operator =(2);,但是A1中的int perator=(int a);已经被覆盖,无法访问。而B1中默认的B1& perator =(const B1& robj);函数又与参数2的整数类型不相符,无法调用。


4,为了确认B1中默认的B1& perator =(const B1& robj);函数的存在性,可以用以下代码验证:


B1 b;B1 v;v = b; // OK, 相当于调用v.operator =(b);


5,所以,“赋值运算符重载函数”不是不能被派生类继承,而是被派生类的默认“赋值运算符重载函数”给覆盖了。


这就是C++赋值运算符重载函数不能被派生类继承的真实原因!


C++程序员的必读经典《Effective C++》这么说:

条款45: 弄清C++在幕后为你所写、所调用的函数

一个空类什么时候不是空类? ---- 当C++编译器通过它的时候。如果你没有声明下列函数,体贴的编译器会声明它自己的版本。这些函数是:一个拷贝构造函数,一个赋值运算符,一个析构函数,一对取址运算符。另外,如果你没有声明任何构造函数,它也将为你声明一个缺省构造函数。所有这些函数都是公有的。换句话说,如果你这么写:

class Empty{};

和你这么写是一样的:

class Empty {public:   Empty();                         // 缺省构造函数   Empty(const Empty& rhs);         // 拷贝构造函数   ~Empty();                        // 析构函数 ---- 是否                                   // 为虚函数看下文说明   Empty&   perator=(const Empty& rhs);     // 赋值运算符   Empty* operator&();              // 取址运算符   const Empty* operator&() const;};

0 0