C++ 运算符重载、组合与继承、虚函数与多态 知识点总结

本学期对于《C++面向对象程序设计》一书的学习已基本完成,我感觉自己对C++有了更深入的了解。虽然我所掌握的知识只能算是C++的冰山一角,但是我有足够的信心和热情去探索更多的知识,充实自己的学习。
下面对近期学过的知识进行一下总结:
一、运算符重载
我认为,运算重载是在C++系统里面一个常见的、重要的内容。有了运算符重载,代码可以变得更加简洁、条理,减少出错率。
1.运算符重载不会改变内置类型表达式中的运算符含义,只有在至少一个操作数是用户自定义类型的对象时,才有可能调用该类中重载的运算符。
2.运算符函数的参数个数取决于两个因素:
1)运算符的操作数个数,即是一元运算符还是二元运算符。
2)运算符函数是成员函数还是非成员函数。
类的成员运算符函数:
this指向的对象被作为运算符的第一个操作数(左)
一元运算符函数不需要提供函数
二元运算符提供一个参数作为右操作数。
非成员运算符函数:
一元运算符要提供一个类类型的参数。
二元运算符需要提供两个参数分别作为左、右操作数,其中至少一个参数必须是类类型的。
通常声明为类的友元,以便访问私有数据成员。
3.运算符重载的限制:
1)不可以重载的运算符:
作用域解析符(::)、成员选择符(.)成员指针间接引用符(.*)、条件运算符(?:)。
2)不能定义C++中没有的运算符。
3)不建议重载的运算符:
逻辑与(&&)、逻辑或(||)、逗号运算符(,)、取地址运算符(&)。
4)逻辑运算符和关系运算符应该返回bool值,算术运算符应该返回操作数类型的值,赋值运算符和复合赋值运算符返回左操作数对象的引用。
5)赋值(=)、下标([ ])、函数调用(())和成员函数访问箭头(->)运算符必须是成员函数。
4.常用运算符的重载
1)输入运算符的函数原型:
istream& operator>>(istream &in,type &t);//表示用in读入,type表示用户自定义类型,t是其对象。
假设定义了一个Time类,类中有year、month、day成员,把运算符重载函数定义成Time类的友元:
friend istream&operator>>(istream &is,Time &d);
在类外:
istream&operator>>(istream &is,Time &d)
{
is>>d.year>>d.month>>d.day;
return is;
}
这样一来,在主函数里想要输入Time成员的时候,直接cin>>Time;就可以了,可以节省很多时间,同时也可以防止代码写得太多而遗漏某些成员。
2)输出运算符的重载函数与输入运算符的类似。
二、组合与继承

  • 组合
    将一个类的对象作为另一个类的成员,被称作组合或包含。
    其实在最初写成绩单系统的时候,我们就用到了组合。但是我认为最典型的组合是在图书管理系统中。先写了一个Time类,里面有成员year,month,day这样的成员,之后在写Book类、Reader类的时候,可以分别设一个Time类的对象,分别作为这两个类的成员,作为图书出版时间和用户注册时间。
    总结一下相关知识点:
    1.创建包含对象成员的组合对象时,会执行成员类的构造函数初始化对象成员。例如:
class Book{
 	Time t;
 	string name;
 	public:
 	Book(){}
 	Book(Time tt,string n):t(tt),name(n){}
 };

析构函数的执行次序和构造函数相反。
2.如果没有在初始化列表中对成员对象进行显式的初始化,编译器会执行成员对象的默认构造函数,如果成员对象所属的类不存在默认的构造函数,会引起编译错误。

  • 继承
    继承是在已有类的基础上继承得到新类型,这个新类型自动拥有已有类的特征,并可以修改继承到的特性或者增加自己的新特性。
    继承是在已有类的基础上创建新类的过程。
    1.被继承的已有类成为基类。
    继承得到的新类成为派生类。
    派生类可以再被继承,这样构成的层次结构成为继承层次。
    2.比如,在我之前写的图书管理系统代码中,管理端和用户端的代码中都包含查询的功能,即按照不同的功能多样化查询书籍、借书记录。这两段代码的部分是重复的,所以为了让代码更加简洁,可以单独写一个查找基类,专门用来执行查找功能,让另外两个类分别继承即可。
    3.类继承关系的语法形式
    class 派生类名:基类名表
    {
    数据成员和成员函数声明
    };
    基类名表的构成:
    访问控制 基类名,访问控制 基类名,…,访问控制 基类名
    访问控制 表示派生类对基类的继承方式,使用关键字:
    public 公有继承
    private 私有继承
    protected 保护继承
    4.下面举一个继承的例子:
class A{
	string a;
	string b;
public
	void print(){//用来输出a和b的值
	cout<<a<<" "<<b<<endl;
	}
};

class B:public A{
	string c;
	string d;
public:
	void set(){
	print();//调用继承于基类的成员函数访问继承于基类的私有数据成员
	cout<<c<<" "<<d<<endl;
}
};

//关于B的写法,还可以写成下面这种形式:
class B:public A{
	string c;
	string d;
public:
	void print(){//隐藏了基类中的同名成员
		A::print();//调用继承于基类的成员函数访问继承于基类的数据成员
		cout<<c<<" "<<d<<endl;
	}
};

派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽的基类的同名成员。在派生类中的同名成员,显式地使用类名限定符: 类名::成员
5.基类的初始化
在创建派生类对象的时候用指定参数调用基类的构造函数来初始化派生类继承基类的数据。
派生类构造函数声明为:
派生类构造函数(变元表):基类(变元表),对象成员1(变元表)…对象成员n(变元表);
构造函数执行顺序:基类->对象成员->派生类
6.注意:
基类的构造函数和析构函数不能被继承。
如果基类没有定义构造函数或有无参的构造函数,派生类也可不用定义构造函数。
如果没有无参的构造函数,派生类也可以不用定义构造函数。
三、虚函数与多态
1.冠以关键字virtual的成员函数就是虚函数。虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。实现运行时多态的关键首先是要说明虚函数,而且必须用基类指针调用派生类的不同实现版本。
2.一个虚函数,在派生类层界面相同的重载函数都保持虚特性。
虚函数必须是基类的成员函数。(将来会被派生类覆盖,所以基类这个虚函数没有用处)
虚函数可以是另一个类的友元。
3.一个基类,若干派生类,以基类为指针调用。
无继承就无多态,否则就叫做重载。
4.多态性是指一个名字,多种语义;或界面相同,多种实现。就像在模拟图书管理的系统里面,图书管理员与用户面对同样的登录界面,登陆进去之后实现不同的功能——管理员管理图书和用户信息,用户进行借书和查询等。
重载函数是多态性的一种简单形式。
5.在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同。(也就是说,要完完全全覆盖才行)
如果仅仅是返回类型不同,C++认为是错误重载。
如果函数原型不同,仅函数名相同,丢失虚特性。
如:

class base
{
public:
	virtual void vf1();
	virtual void vf2();
	virtual void vf3();
	void f();	
};
class derived:public base
{
public:
	void vf1();//在派生类中重写了基类继承的函数。虚函数,覆盖了vf1,可以多态
	void vf2(int);//重载,参数不同,虚特性丢失。与base的vf2之间无任何联系
	char vf3();//错误,仅有返回类型不同
	void f();//非虚函数重载,正常覆盖,但不是多态。此无虚函数机制
};

6.实现多态:
1)派生类应该从它的基类公有派生。
2)必须首先在基类中定义虚函数。
3)派生类对基类中声明虚函数重新定义时(派生类改造同名函数进行覆盖),关键字virtual可以不写。
4)一般通过基类指针访问虚函数时才能体现多态性。(基类对象形式访问派生类对象:基类指针指向派生类对象、基类对象引用派生类对象)
7.
1)一个虚函数无论被继承多少次,保持其虚函数特性。
2)虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数。
3)构造函数、内联成员函数、静态成员函数不能是虚函数。
4)构造函数不可以是虚函数。而析构函数可以是虚函数,通常声明为虚函数。
8.纯虚函数和抽象类(含有纯虚函数的类):
1)纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
2)纯虚函数是一个在基类中说明的虚函数,在基类中没有定义, 要求任何派生类都定义自己的版本。
基类只定义原型,无实现代码(在派生类中具体去写)。
3)纯虚函数为各派生类提供一个公共界面。
4)纯虚函数说明形式:
virtual 类型 函数名(参数表)= 0 ;
=0表示将来不写实现代码,之后重写才有代码。
5)一个具有纯虚函数的基类称为抽象类。
如:

class  shape// 抽象类
{
  public :
  virtual  void  rotate(int)=0 ; // 纯虚函数
  virtual  void  draw()=0 ;	// 纯虚函数
} ;
      …...
shape  x ;//错误,抽象类不能建立对象
shape  *p ;// 正确,可以声明抽象类的指针
shape  f () ;//正确,抽象类不能作为函数返回类型
void  g ( shape ) ;// 错误,抽象类不能作为传值参数类型,不具备多态性质(:基类指针、基类对象引用派生类对象)
shape  & h ( shape &) ;// 正确,可以声明抽象类的引用

版权声明:本文为AMWICD原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。