C++基础(5)——类继承

1. 从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。

class TableTennisPlayer//一个简单的基类
{
 private:
 	string firstname;
	string lastname;
	bool hasTable;
public:
	TableTennisPlayer(const string & fn = “none”,const string & ln = “none”,bool ht = false);
        void Name() const;
        bool HasTable() const {return hasTable;};
        void ResetTable(bool v){hastable = v;};
};
//将RatedPlayer类声明为从TableTennisPlayer类派生而来
class RatedPlayer : public TableTennisPlayer
{
};
//冒号指出RatedPlayer类的基类是TableTennisPlayer类。
  • RatedPlayer对象将具有以下特征:
    • 派生类对象存储了基类的数据成员;
    • 派生类对象可以使用基类的方法;
  • 上述特殊的声明头表明TableTennisPlayer是一个公有基类,这被称为公有派生。派生类对象包含基类对象。使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。

2. 需要在继承特性中添加

  • 派生类需要自己的构造函数
  • 派生类可以根据需要添加额外的数据成员和成员函数
class RatedPlayer : public TableTennisPlayer
{
private:
	unsigned int rating;//增加一个数据成员
public:
	RatedPlayer(unsigned int r=0,const string & fn =“none”, const string & ln = “none”, bool ht = false);
	RatedPlayer(unsigned int r, const TableTennisPlayer &tp);
	unsigned int Rating() const {return rating;};
	void ResetRating(unsigned int r){rating = r;};
};
  • 构造函数必须给新成员(if have)和继承的成员提供数据。在第一个RatedPlayer构造函数中,每个成员对应一个形参;而第二个RatedPlayer构造函数使用一个TableTennisPalyer参数,该参数包括firstname、lastname和hasTable。

3. 构造函数:访问权限的考虑

  • 派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。派生类构造函数必须使用基类构造函数。
  • 创建派生类对象时,程序首先创建基类对象。从概念上说,这意味着基类对象应当在程序进入派生类构造函数之前被创建。
  • 第一个RatedPlayer构造函数的代码
RatedPlayer::RatedPlayer(unsigned int r,const string & fn, const string & ln, bool ht):TableTennisPlayer(fn,ln,ht)
{
rating = r;
}
  • 第二个RatedPlayer构造函数的代码
RatedPlayer::RatedPlayer(unsigned int r,const TableTennisPlayer & tp)
:TableTennisPlayer(tp)
{
rating = r;
}
  • 派生类构造函数的要点
    • 首先创建基类对象;
    • 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
    • 派生类构造函数应初始化派生类新增的数据成员。

4. 继承:is-a关系

  • C++有3种继承方式:公有继承、保护继承和私有继承。
  • 公有继承建立的关系称为is-a-kind-of(是一种)关系,通常用术语is-a,即派生类对象也是一个基类。
  • 公有继承不建立has-a关系,不能建立is-like-a关系,不建立is-implemented-as-a(作为……来实现)关系,不建立uses-a关系。

5. 多态公有继承

  • 有两种重要的机制可用于实现多态公有继承:在派生类中重新定义基类的方法;使用虚方法。
  • 非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法。
void BrassPlus::ViewAcct() const
{
…
	Brass::ViewAcct();
	cout<<”Maxmum loan : $”<<maxloan<<endl;
	cout<<”Owed to bank :	$”<<owesBank<<endl;
	cout<<”Loan Rate:”<<100*rate<<”%\n”;
…
}
/*
BrassPlus:: ViewAcct()显示新增的BrassPlus数据成员,
并调用基类的Brass::ViewAcct()来显示基类数据成员。
在派生类方法中,标准技术是使用作用域解析运算符来调用基类方法。
*/

6. 静态联编和动态联编

  • 将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编(binding)。在编译过程中进行联编被称为静态联编(static binding)。编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic binding)。
  • 将派生类引用或指针转换为基类引用或指针被称为向上强制转换(upcasting),这时公有继承不需要进行显式类型转换。将基类指针或引用转换为派生类指针或引用称为向下强制转换(downcasting),如果不使用显式类型转换,则向下强制转换是不被允许的。

7. 虚函数

  • 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生出来的类)中是虚的。
  • 如果要在派生类中重新定义基类的方法,则将它设置为虚方法;否则,设置为非虚方法。
  • 构造函数不能是虚函数。创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数,这种顺序不同于继承机制。派生类不继承基类的构造函数,将类构造函数声明为虚的没什么意义。
  • 析构函数应当是虚函数,除非类不用做基类。

8. 访问控制:protected

  • private和protected之间的区别只有在基类派生的类中才会表现出来,派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。
  • 最好对类数据成员采用私有访问控制,不要使用保护访问控制;同时通过基类方法使派生类能够访问基类数据。
  • 然而,对于成员函数来说,保护访问控制很有用,它让派生类能够访问公众不能使用的内部函数。

9. 编译器生成的成员函数

  • 默认构造函数。默认构造函数要么没有参数,要么所有的参数都有默认值,提供构造函数的动机之一是确保对象总能被正确的初始化,因此,最好提供一个显式默认构造函数,将所有的类数据成员都初始化为合理的值。
  • 复制构造函数接受其所属类的对象作为参数。在下述四个情况下,将使用复制构造函数:将新对象初始化为一个同类对象;按值将对象传递给函数;函数按值返回对象;编译器生成临时对象。
  • 默认的赋值运算符用于处理同类对象之间的赋值;如果语句创建新的对象,则使用初始化;如果语句修改已有对象的值,则是赋值。

猜你喜欢

转载自blog.csdn.net/nininicrystal/article/details/86218815