C++分析研究  
C++
日历
<2013年3月>
242526272812
3456789
10111213141516
17181920212223
24252627282930
31123456
统计
  • 随笔 - 92
  • 文章 - 4
  • 评论 - 4
  • 引用 - 0

导航

常用链接

留言簿

随笔档案

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

 
  在C++中为什么输入输出流不能重载为类成员函数?

  假如说operator 《 ()这是一个成员函数,mealtime是一个类Time的私有成员,那么如我们看到的,如下一个输出语句:cout《mealtime;将被编译器处理为发送一条消息到cout,cout.operator《(mealtime);这表示operator 《必须是类ostream的一个成员,而不是类Time的一个成员。然而,即使能将这个新函数添加到标准ostream中,仍然不希望破坏标准的库,因此operator 《必然是一个普通函数,这表示如下输出语句:cout《mealtime将被编译器看作函数调用operator《(cout,mealtime);这样operator《()必须具有两个形参,第一个是ostream类,第二个是Timer类。

  大部份的标准库实现中,对ostream,istream类体系采用了构造函数保护继承的方式……致使即使以继承的方式来扩展流类,也会在对象实例化时遭遇阻碍……

  另一方面,标准库中的流类,其插入符函数没有声明为虚函数,因此子类不能对其实现进行覆盖,所以也使成员函数重载遭遇到实质的困难……

  总的来说,C++标准I/O库非常繁杂且难,其实现思想很多都与常规的OOP有所出入……在使用的时候要谨慎,并最好遵从惯例……

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

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

  class A1

  {

  public:

  int operator=(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 operator=(int a);被B1中的隐含的赋值运算符函数B1& operator =(const B1& robj);所覆盖。 A1中的int operator=(int a);函数无法被B1对象访问。 www.liuhebao.com

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

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

  B1 b;

  B1 v;

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

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

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

  关于本帖问题正确性的解释

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

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

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

  class Empty{};

  和你这么写是一样的:

  class Empty {

  public:

  Empty(); // 缺省构造函数

  Empty(const Empty& rhs); // 拷贝构造函数

  ~Empty(); // 析构函数 ---- 是否

  // 为虚函数看下文说明

  Empty&

  operator=(const Empty& rhs); // 赋值运算符

  Empty* operator&(); // 取址运算符

  const Empty* operator&() const;

  };

  但是Effective C++依然不能作为最后的判决。让我们从C++的“宪法”中寻找答案…

  ISO/IEC 14882是C++的国际标准。该标准于1998年9月1日通过并且定案。当然,这个标准已经不是最新标准了,但这个标准却是目前最被广泛支持的C++标准。所以,我一向称之为C++的“宪法”。

  C++“宪法”第12章 Special Member Functions (第185页)开宗明义:

  The default constructor, copy constructor and copy assignment operator, and destructor are special member functions. The implementation will implicitly declare these member functions for a class type when the program does not explicitly declare them, except as noted in 12.1. The implementation will implicitly define then if they are used, as specified in 12.1, 12.4 and 12.8. Programs shall not define implicitly-declared special member functions. Programs may explicitly refer to implicitly declared special member functions.

  译文:

  缺省构造函数,拷贝构造函数,拷贝赋值函数,以及析构函数这四种成员函数被称作特殊的成员函数。如果用户程序没有显式地声明这些特殊的成员函数,那么编译器实现将隐式地声明它们。12.1中有特别解释的例外。如果用户程序调用了这些特殊的成员函数,那么编译器就会定义这些特殊的成员函数,在12.1,12.4,12.8中分别规定了编译器对这些函数的定义方式。用户程序不能定义隐式声明的特殊成员函数。用户程序可以显式地调用隐式声明的特殊成员函数。

  译注:

  根据C++标准的规定:

  声明(Declare)代表头文件中的那部分代码,比如下面就是一个声明:

  class A

  {

  public:

  A();

  }

  定义(Define)代表源文件中的代码,比如下面就是一个定义:

  A::A()

  {}

  综上所述,可知,第一个说法是正确的。

  关于第二个说法的正确性,可参见C++“宪法”3.3.7 Name Hiding (第28页)(由于我手上的C++“宪法”是扫描版,无法直接拷贝文字,且文字较多,懒得输入了。)

  第3,4,5点说法都是常识性的知识,可以直接验证。
posted on 2012-11-26 18:14 HAOSOLA 阅读(236) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


 
Copyright © HAOSOLA Powered by: 博客园 模板提供:沪江博客
PK10开奖 PK10开奖