Classes in C++ can be extended, creating new classes which retain characteristics of the base class. This process, known as inheritance, involves a base class and a derived class: The derived class inherits the members of the base class, on top of which it can add its own members.
C++的类可以被扩展,创建新的类的同时保持原有类的特性。这个过程,称为继承。该过程涉及基类和派生类。派生类继承(inherit)了基类的成员,在派生类中,可以添加自己的成员。
想像一下两个这样的多边形:矩形(Rectangle)和三角形(Triangle)。
这两个多边形有着某些共同的属性(common properties),比如:需要用来计算它们的面积的值,这些值都可以简单地用宽(width,或者底base)和高(height)来表示。
如果一个多边形类(Polygon)派生出矩形类和三角形类。那么这个多边形就可以表示世界上所有的矩形和三角形。如下图所示:
在这个例子中,多边形(Polygon)类包含两种类型(矩形和三角形)多边形的共同属性:宽、高。Rectangle和Triangle是Polygon类的派生类,但是具体到特定的细节,这两个派生类又是不一样的。
派生自其他类的类继承了基类的所有可访问成员(accessible members)。这意味着,如果基类包含一个成员A,从基类中派生出另一个类,并且这个类包含另一个成员B。那么这个派生类将同时拥有成员A和成员B。
两个类的继承关系可以在派生类中声明,派生类的定义可以用如下语法:
class derived_class_name: public base_class_name
{ /*...*/ };
derived_class_name是派生类的类名,base_class_name是基类类名。这两个类中间用分号连接,表示派生关系。冒号后面的public访问限定符(access specifier)也可以用protected或者private来替换。一般情况下,使用public还是较为常见(Since public is the most accessible level, by specifying this keyword the derived class will inherit all the members with the same levels they had in the base class.)。
这样,我们就可以写出这样的类:
// derived classes
#include <iostream>
using namespace std;
class Polygon {
protected:
int width, height;
public:
void set_values (int x, int y)
{ width=x; height=y;}
};
class Rectangle: public Polygon {
public:
int area ()
{ return width * height; }
};
class Triangle: public Polygon {
public:
int area ()
{ return width * height / 2; }
};
int main () {
Rectangle rect;
Triangle trgl;
rect.set_values (4,5);
trgl.set_values (4,5);
cout << rect.area() << '\n';
cout << trgl.area() << '\n';
return 0;
}
在上面的例子中,我们有一个基类(base class)──Polygon(多边形)类。有两个类派生自多边形类,分别为:矩形类(Rectangle)和三角形类(Triangle)。
使用如下语法形式进行派生:
class Rectangle: public Polygon{ ... };
class Triangle: public Polygon { ... };
注意每个类后面的分号,继承方式为public继承。
在基类中,有两个保护(protected)的属性:宽(width)和高(height)。还有一个方法:set_values。
Rectangle类和Triangle类派生自Polygon类,那么他们就拥有基类中的这些成员(width,height, setvalues()),虽然在派生类中没有写出这些成员,但是他们还是有的。
然后再两个派生类中,分别有各自的计算面积的方法:
int area (){ return width * height; }
int area (){ return width * height / 2; }
分别用来计算矩形的面积(宽×高);计算三角形的面积( 12(底×高) )。
在main函数中,计算矩形的面积分别用各自类对象调用各自的计算面积的方法:
cout << rect.area() << '\n'; // 计算矩形的面积
cout << trgl.area() << '\n' // 计算三角形的面积
也许你会注意到基类(Polygon)中的访问限定符──protected。它和private有些相似。唯一不同的是,当它们用在继承上:
When a class inherits another one, the members of the derived class can access the protected members inherited from the base class, but not its private members.
当一个类派生自另一个类时,派生类的成员可以访问基类中的protected成员,但是不能访问private成员。
在上面的继承关系中
基类的数据成员:
protected:
int width, height;
在派生类中,访问了基类的protected数据成员(width和height):
public:
int area (){ return width * height; }
如果基类的数据成员是private类型的会怎么样?
上面一行将会出错,因为私有成员只能在类声明内部访问(或者它的友元)。如:通过调用该类的public类型的函数间接访问。但是,如果把protected改为public又会怎么样?任何地方都可以访问。
下面是一张访问权限表:
Access | public | protected | private |
---|---|---|---|
members of the same class | yes | yes | yes |
members of derived class | yes | yes | no |
not members(任何地方都可以访问) | yes | no | no |
如果在继承中忘记了写public关键字会怎么样?
If no access level is specified for the inheritance, the compiler assumes private for classes declared with keyword class and public for those declared with struct.
Actually, most use cases of inheritance in C++ should use public inheritance. When other access levels are needed for base classes, they can usually be better represented as member variables instead.
如果你是用的类(class)关键字,那么编译器会假定是private继承;但是,如果你使用的struct关键字,那么编译器认为是public继承。
原则上讲,public继承,继承了基类的所有成员,除了:
它的构造函数和析构函数
它的赋值操作符成员(operator=)
它的友元(注意,友元是不能被继承的)
它的私有成员
尽管子类可以访问父类的构造函数和析构函数,但是子类不能继承父类的构造函数和析构函数。它们会被子类的构造函数和析构函数自动调用。
除非另有说明,派生类的构造函数调用基类的默认构造函数(i.e.,无参构造函数)。当然,调用基类的不同构造函数也是有可能的──使用初始化列表,调用特定参数的基类构造函数。
// 初始化列表调用基类特定类型的构造函数
#include < iostream >
using namespace std;class Circle { // 计算圆柱的底面积
double radius;
public:
Circle(double r) : radius(r) { }
double area() {return >radius*radius*3.14159265;}
};class Cylinder {
Circle base;
double height;
public:
Cylinder(double r, double h) : base (r), height(h) {}
double volume() {return base.area() * height;} // 体积=底面积×高
};int main () {
Cylinder foo (10,20);cout << “foo’s volume: ” << foo.volume() << >’\n’;
return 0;
}
在上面的例子中,Circle类只有一个构造函数,且参数个数为1个。在Cylinder类中,有个成员base,Circle类型的base。此时通过初始化列表的方式,调用Circle类的一个参数的构造函数,对base进行初始化。
如果将上面基类的set_values函数替换成有两个参数的构造函数,可以这样修改:
// derived classes
#include <iostream>
using namespace std;
class Polygon {
protected:
int width, height;
public:
Polygon (int x=0, int y=0):
width(x),height(y){} // 初始化列表
};
class Rectangle: public Polygon {
public:
Rectangle(int x, int y):
Polygon(x,y){}; // 显示调用基类Polygon的两个参数的构造函数
int area ()
{ return width * height; }
};
class Triangle: public Polygon {
public:
Triangle(int x, int y):
Polygon(x,y){};
int area ()
{ return width * height / 2; }
};
int main () {
Rectangle rect(4,5);
Triangle trgl(4,5);
cout << rect.area() << '\n';
cout << trgl.area() << '\n';
return 0;
}
如果你觉得这样不容易理解,set_values版的类可以辅助你理解。