C++ 继承和派生

继承和派生

一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。
从父类产生一个子类,称为派生。
基类与派生类的关系:派生类是基类的具体化,而基类是派生类的抽象。

定义基类和派生类

定义基类

例:一个矩形类。

/*矩形类*/
class Rectangle
{
protected:/*被保护成员*/
	double L = 0;
	double W = 0;
public:   /*共有成员*/
	Rectangle() = default;
	Rectangle(double a, double b) : L{ a }, W{ b } {}
	double GetArea() const	/*矩形面积*/
	{
		return L * W;
	}
	double GetGirth() const	/*矩形周长*/
	{
		return  2 * (L + W);
	}
};
基类成员的访问属性
  • 共有成员(public member)可以在程序的任何地方被访问。
  • 私有成员(private member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用。
  • 被保护成员(protected member)可以被类的成员函数(或友元函数和友元类)使用,类外不能使用,但可以被派生类的成员函数使用。

如果省略成员访问限定符,则系统默成员都是私有的。
public、private、protected 关键字被称为成员访问限定符。

定义派生类

派生类必须通过使用类派生列表(class derivation list)明确指出它是从哪个(或哪些)基类继承而来的。
类派生列表的形式是:

class  派生类名:[继承方式]  基类名
{
    派生类新增加的成员
};

首先是一个冒号,后面紧跟以逗号分隔的基类列表。
其中每个基类前面可以有以下三种访问说明符中的一个:public、protected 或者 private。

派生类的构造函数

派生类可以接收基类全部的成员,但是不能继承构造函数和析构函数。
派生类必须使用基类的构造函数来初始化它的基类部分。
例:

/*长方体类*/
class Cuboid : public Rectangle
{
private:
	double H = 0;
public:
	Cuboid() = default;
	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
	// Rectangle{ a,b }是基类的构造函数
};

执行派生类构造函数的顺序:
先调用基类构造函数,对基类数据成员初始化;
然后执行派生类构造函数本身,对派生类数据成员初始化。

派生类的拷贝构造和移动构造函数

在默认情况下,基类默认构造函数初始化派生类对象的基类部分。
如果我们想拷贝((或移动)基类部分,则必须在派生类的构造函数初始值列表中显式地使用基类的拷贝(或移动)构造函数。

合成的拷贝、赋值、移动操作

某些定义基类的方式也可能导致有的派生类成员成为被删除的函数:

  • 如果基类中的默认构造函数、拷贝构造函数、拷贝赋值运算符或析构函数是被删除的函数或者不可访问,则派生类中对应的成员将是被删除的。
  • 如果在基类中有一个不可访问或删除掉的析构函数,则派生类中合成的默认和拷贝构造函数将是被删除的。
  • 如果基类的移动操作是删除的或不可访问的,则派生类的移动操作也将是被删除的。
  • 如果基类的析构函数是删除的或不可访问的,则派生类的移动构造函数也将是被删除的。
继承的构造函数

C++11标准允许派生类继承基类构造函数的方式是提供一条注明了(直接)基类名的 using 声明语句。

/*正方形类*/
class Square
{
protected:
	double L;
public:
	Square() :L{ 4 } {};
	Square(double a) :L{ a } {}
};
/*立方体类*/
class Cube : public Square
{
public:
	Square::Square; 	
/*
等价于下列构造函数:
Cube() :Square() {};
Cube(double a) :Square{ a } {}
*/
};

一个构造函数的 using 声明不会改变该构造函数的访问级别。
一个 using 声明语句不能指定 explicit 或 constexpr。

继承方式

通过指定继承方式,可以改变基类成员在派生类中的访问属性。
公用继承
公用继承(public inheritance)基类的共有成员和被保护成员在派生类中保持原有访问属性。
派生类可以访问基类的共有成员和被保护的成员。

class Cuboid : public Rectangle
{
private:
	double H = 0;
public:
	Cuboid() = default;
	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
	double GetVolume() const           /*长方体体积*/
	{//访问基类被保护成员L、W
		double V = L * W * H;  //底面积乘高
		return V;
	}
	double GetSurfaceArea() const   /*长方体表面积*/
	{//访问基类共有成员GetArea(),GetGirth()
		double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积
		return S;
	}
};

派生类的对象可以访问基类的共有成员和本类的共有成员。

	Cuboid Cu{ 3,3,3 };
	cout << "底面积:" << Cu.GetArea() << endl;
	cout << "体积:" << Cu.GetVolume() << endl;
	cout << "底周长:" << Cu.GetGirth() << endl;
	cout << "表面积:" << Cu.GetSurfaceArea() << endl;

私有继承和受保护的继承
受保护的继承(protected inheritance)基类的共有成员和被保护成员在派生类中成了被保护成员。
私有继承(private inheritance)基类的共有成员和被保护成员在派生类中成了私有成员。
派生类可以访问基类的共有成员和被保护的成员。

class Cuboid : protected Rectangle
{
private:
	double H = 0;
public:
	Cuboid() = default;
	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
	double GetVolume() const   /*长方体体积*/
	{//访问基类被保护成员L、W
		double V = L * W * H;  //底面积乘高
		return V;
	}
	double GetSurfaceArea() const   /*长方体表面积*/
	{//访问基类共有成员GetArea(),GetGirth()
		double S = 2 * GetArea() + H * GetGirth(); //底面积 + 侧面积
		return S;
	}
};

如果基类的共有成员在派生类中成了被保护或私有成员,则派生类的对象只可以访问本类的共有成员。

	Cuboid Cu{ 3,3,3 };
	cout << "体积:" << Cu.GetVolume() << endl;
	cout << "表面积:" << Cu.GetSurfaceArea() << endl;
改变个别成员的可访问性

有时我们需要改变派生类继承的某个名字的访问级别,通过使用 using 声明可以达到这一目的:

/*长方体类*/
class Cuboid : public Rectangle
{
private:
	double H = 0;
public:
	using Rectangle::W; //受保护的成员变成共有成员
	Cuboid() = default;
	Cuboid(double a, double b, double c) : Rectangle{ a,b }, H{ c } {}
};

派生类到基类的隐式类型转换

  1. 派生类对象能向基类对象赋值。(派生类增加的部分会被切掉。)
  2. 派生类对象能向基类对象的引用进行赋值或初始化。
  3. 派生类对象的地址能赋给指向基类对象的指针变量。
  4. 如果函数参数是基类的对象或基类对象的引用,相应的实参能用派生类对象。

通过基类的对象、基类对象的引用或指向基类的指针,只能访问派生类中基类部分的共有成员,而不能访问派生类增加的成员。
不存在基类向派生类的隐式类型转换。

防止继承的发生

有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑它是否适合作为一个基类。C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字 final。

/*矩形类*/
class Rectangle final  //不允许被继承
{
protected:/*被保护成员*/
	double L = 0;
	double W = 0;
public:   /*共有成员*/
	Rectangle() = default;
	Rectangle(double a, double b) : L{ a }, W{ b } {}
};

你可能感兴趣的:(C++,c++,开发语言,继承)