面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。
继承的格式为:class derived-class: access-specifier base-class
其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。例如 class Line:public Point,是Line类公有继承了Point。若不屑继承方式public,则默认为private。私有继承是将基类(base-class)的成员作为继承类的私有成员。若想一个类或者方法不允许继承,可以在它的前面加final字段。
继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
- 公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
- 保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
- 私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
访问控制和继承
生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
我们可以根据访问权限总结出不同的访问类型,如下所示
一个派生类继承了所有的基类方法,但下列情况除外:
- 基类的构造函数、析构函数和拷贝构造函数。
- 基类的重载运算符。
- 基类的友元函数
多继承
多继承即一个子类可以有多个父类,它继承了多个父类的特性。继承格式如下:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,… { <派生类类体> };
例如:class Line:public Point1,public Point2,protected Point3,private Point4{};
#include<stdio.h>
#include<stdlib.h>
#include"iostream"
using namespace std;
class Point{
public:
int x, y;
void sayPoint() {
cout << "画了一个点" << endl;
}
};
class Line:Point{ //没有继承声明,默认为私有继承。私有继承会让Line所有成员都为Point私有
public :
void sayLine() {
cout << "画了一条线" << endl;
}
void sayPo() {
sayPoint(); //私有继承,sayPoint成为Line的私有方法。
}
};
class Line1:public Point {
public :
void sayLine() {
cout << "画了一条线1" << endl;
}
};
class Line2 :public Point {
public:
void sayLine() {
cout << "画了一条线2" << endl;
}
};
class Circle :public Line2, public Line1 { //不知道为什么没有出现虚表继承的问题 ,没有报错。
public:
void sayCircle() {
cout << "画了一个圆" << endl;
}
};
int main() {
Line line;
Line1 line1;
//line.sayPoint(); //私有继承,无法调用父类方法,实际是因为成为line私有的方法,外部无法调用
line1.sayPoint();
Circle circle;
//circle.sayPoint(); 会出现调用不明确编译错误
circle.sayCircle();
system("pause");
}
子类继承父类后方法的调用
当子类继承父类后,可能会出现方法的重载、重写、和多态。多态和虚表的概念暂时还未理解,以后补充。以下是子类对象重写,重载和父类方法的范例:
#include<stdlib.h>
#include<iostream>
using namespace std;
class Point {
public:
int x=1, y=2;
void sayPoint() {
cout << "父类" << endl;
};
void sayX() {
cout << "x:" << x << endl;
}
virtual void sayHello() {
cout << "父类 hello" << endl;
};
};
class Point1 :public Point {
public:
void sayPoint(int x, int y) {
cout << "子类 Point1" << x << ","<<y << endl;
}
void sayPoint() {
cout << "子类无参数" << endl;
}
void sayHello() {
cout << "子类 hello too" << endl;
}
};
int main() {
Point1 point1;
point1.sayPoint(1, 2); //调用子类的函数,函数的重载。
point1.sayPoint(); //调用的子类函数,虽然与父类的函数完全相同,调用的是子类的函数。
point1.Point::sayPoint(); //子类对象调用父类的方法。
point1.sayX(); //调用的是父类的函数,因为子类继承了父类的函数。
point1.sayHello();
cout<<point1.x<<endl; //因为继承了父类,所以可以直接访问父类的成员。
system("pause");
return 0;
}
结果:调用父类同名方法时,可以采用: 子类对象.父类名::父类方法();的方式调用父类的对象。