1.继承和派生相关概念
一个==新类==从==旧类==中得到已有的特性,如:数据成员、成员函数等,即称类的继承。
从==旧类==产生一个==新类==,即称为类的派生
这样的旧类被称为基类或父类,这样的新类被称为派生类或子类。
单继承:一个派生类从==一个基类==继承;多继承:一个派生类从==多个基类==继承。
派生类也可以派生出新的派生类。
从基类到派生类有三种继承方式:public(公有继承)、private(私有继承)、protected(保护继承)
派生类对基类的访问权限如下表:
基类成员\继承方式 public(公有继承) private(私有继承) protected(保护继承) 私有成员(private) 否 否 否 公有成员(public) 是(公) 是(私) 是(保) 保护成员(protected) 是(保) 是(私) 是(保)
- 派生类把基类除了构造函数和析构函数之外全部成员按上表规则接受过来
- 由上表可知,无论是哪种继承方式,派生类都无权访问基类的私有成员
- 由上表可知,公有继承不更改基类成员权限属性,私有继承和保护继承都改为相应的权限属性
定义为:
单继承:
class <派生类名> : <继承方式> <基类名>{
<派生类定义的新成员>
};
多继承:
class <派生类名> : <继承方式1> <基类名1>, <继承方式2> <基类名2>, <继承方式3> <基类名3>·······{
<派生类定义的新成员>
}
例4.1:
#include<iostream>
using namespace std;
class A {
private:
int a_1;
int a_2;
public:
int a_3;
A(int x, int y, int z) :a_1(x), a_2(y), a_3(z) {
}
void A_1() {
cout << a_1 << endl;
}
};
class B :public A {
private:
int b_1;
public:
B(int x, int y, int p, int q) :A(x, y, p), b_1(q) {//派生类的构造函数
}
void B_1() {
cout << b_1 << endl;
//cout << a_1 << endl; //错误,无权访问基类私有成员
}
};
void main() {
B BB(1, 2, 3, 4);
BB.A_1();
BB.B_1();
}
输出为:
1
4
2.派生类的构造/析构函数
对于派生类的构造函数,不仅要考虑派生类所增加的数据成员的初始化,还应当考虑基类的数据成员初始化,所以派生类的构造函数需要调用基类的构造函数。
参照例4.1
派生类构造函数的执行顺序为:
- 调用基类的构造函数
- 调用成员对象的构造函数
- 执行派生类构造函数体中的内容,完成对派生类数据成员的初始化
派生类析构函数的执行顺序为:(恰好相反)
- 对派生类普通成员清理
- 调用成员对象析构函数,对派生类新增的成员对象进行清理
- 调用基类析构函数,对基类进行清理
3.多继承之二义性
3.1二义性
在多继承中,当两个或两个以上的基类存在同名的成员,派生类的对象或成员访问时便会产生二义性。
这时可以通过作用域运算符来明确访问的函数,如下:
<对象名>.<基类名> :: <基类具体成员/对象>
class A {
public:
void show() {
cout << "1" << endl;
}
};
class B {
public:
void show() {
cout << "2" << endl;
}
};
class C: public A, public B{};
void main() {
C cc;
cc.show();//因为C的基类A和B都有show()这个函数,所以这里调用不明确
cc.A::show;//这样即可区分
}
上述是一种会产生二义性的情况,该情况二义性来源是两个,所以可以很直接的用作用域运算符(::)区分
但介于派生类也可派生,二义性的来源可以只来自一个基类,例如
这时,标明公共的基类(基类的基类)是错误的,要标明基类即可。
class A {
public:
void show() {
cout << "1" << endl;
}
};
class B: public A{};
class C: public A{};
class D: public B, public C{};
void main() {
D dd;
dd.A::show;//A为公基类,这样的限制方式是错的
dd.B::show;//这样即可区分
}
由于二义性,一个类不能从同一个类中直接继承一次以上。
3.2虚基性
为了解决二义性,最有效的办法是引进虚基类。 格式如下:
class <类名> : virtual <继承方式> <基类名>
或
class <类名> : <继承方式> virtual <基类名>
虚基类是在声明派生类时声明的,对于虚基类声明的派生类中,保留的是指向子对象的==指针==,这样就不会出现两个子对象的情况了。
继承虚基类的各层派生类中,建立对象的类被称为最派生类,只有在最派生类中,对虚基类的构造函数才会被调用,最派生类的所有基类中列出对虚基的构造函数都会被忽略
构造函数的调用顺序:
- 虚基类的构造函数。
- 非虚基类的构造函数。
- 派生类中成员对象的构造函数。
- 派生类的构造函数体。
4.虚函数
C++中的多态性:==使用一套基本语义为不同对象服务==。c++中存在编译时的多态性和运行时的多态性。
编译时的多态性就比如:==函数重载==和==模板==。先声明一套基础语句,根据具体情况定义为具体实例。运行时的多态性略。
虚函数即是在类中实现多态性的机制,使用到了关键字:virtual。
虚基类:在类派生时加上virtual
虚函数:在成员函数声明的前面加上virtual
class A{
public:
virtual void a();
};
class B : public A{
public:
virtual void a();// 重写了基类函数
};
A a;
B b;
A *a1 = &a;
A *a2 = &a;
// 当派生类“重写”了基类的虚方法,调用该方法时
// 程序根据 指针或引用 指向的 “对象的类型”来选择使用哪个方法
a1->a();// A::a();
a2->a();// B::a();
上例中为多态调用,即通过动态绑定(绑定在指针上)调用,通过指针的指向来调用相对应的函数。还有非多态调用,用运算符.来调用。