继承概述
继承是所有面向对象的高级语言的重要基本概念。继承,即依据另一个类来定义一个类,这样做实现了代码功能和执行效率的提高。
当使用继承创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类(或父类),新建的类称为派生类(或子类)。
形象化理解继承
继承的概念可以用下图简单地呈现:
从图中可以很明显的看出,继承就是按照继承中的访问修饰符 (下文解释)将基类中的多有成员复制到派生类中。
以下是定义一个基类的派生类的代码框架:
class father
{
成员;
};
class son : VS father
{
额外定义的成员;
};
其中,father
是基类,son
是派生类,VS
是继承中的访问修饰符。
注意:基类中private
状态的成员是不能被继承的(准确地说是派生类不能直接访问基类的private
成员),但可以通过调用基类的成员函数间接地调用它们。如
class father
{
private:
int fa;
};
class son : public father
{
public:
son()
{
this->fa = 0;
}
};
是不被允许的,编译器报错:
成员 “father::fa” (已声明 所在行数:4)不可访问
以下是一个实例,定义了一个基类animal
和一个派生类dog
:
#include <string>
using namespace std;
class animal
{
public:
bool gender;
int age;
string name;
animal() {};
~animal() {};
void eat() {};
void sleep() {};
};
class dog : public animal
{
public:
void bark() {};
dog() {};
~dog() {};
};
请注意:基类中的友元函数声明、重载运算符(某些编译器中仅operater =
)不会被继承。
关于继承中构造函数的调用问题,见文末的总结/小注。
继承中的访问修饰符
继承中的访问修饰符与常规的访问修饰符相同,分为public
、private
和protected
三类。继承中的访问修饰符的作用是规定基类的成员在继承后的可访问状态的变换规则。听起来有些拗口,下面举一个例子:
类father
中有一个可访问状态为public
的成员变量fa
,类son
以public
状态继承类father
,则该成员变量fa
在son
中的可访问状态仍为public
。
更具体地:
- 当使用公有继承(public)时,派生类中继承的成员与基类中成员的可访问状态保持一致。
- 当使用私有继承(private)时,派生类中继承的所有成员的可访问状态均变为
private
。 - 当使用保护继承(protected)时,派生类中继承的所有成员的可访问状态均变为
protected
。
因此我们几乎只会使用public
进行继承。
多继承
一个派生类可以继承自多个基类。语法框架如下:
class son : VS1 father1, VS2 father2, ...
{
成员;
};
其中,VS1
、VS2
…是继承中的访问修饰符,father1
、father2
是基类名。
以下是一个多继承的实例:
#include <iostream>
using namespace std;
class shape
{
protected:
int width;
int height;
public:
void setWidth(int w)
{
this->width = w;
return;
}
void setHeight(int h)
{
this->height = h;
return;
}
};
class cost
{
private:
int price;
public:
int getCost(int area)
{
return area * price;
}
void setPrice(int w)
{
this->price = w;
return;
}
};
class rectangle : public shape, public cost
{
public:
int getarea()
{
return this->width * this->height;
}
};
int main()
{
rectangle rec;
rec.setWidth(5);
rec.setHeight(8);
rec.setPrice(30);
cout << rec.getarea() << ' ' << rec.getCost(rec.getarea()) << endl;
return 0;
}
以上程序运行后,将会输出以下结果:
40 1300
总结/小注
继承中构造函数/析构函数的调用规则如下:
构造函数
如果派生类并没有定义属于自己的构造函数,则按照参数调用基类的构造函数;
如果派生类定义了无参构造函数,则调用时先调用派生类的无参构造函数,再调用基类的无参构造函数(若不存在则报错);
如果派生类定义了含参构造函数,则调用时先调用派生类的含参构造函数,再调用基类的无参构造函数(若不存在则报错,若希望调用基类含参构造函数则需要显式声明,见代码);
class father
{
public:
father(int a) {};
};
class son : public father
{
public:
son(int a) : father(a) {};
};
析构函数
先调用基类析构函数,再调用派生类析构函数。