- 该系列文章大部分摘自博主白鳯《C++面向对象程序设计》✍千处细节、万字总结(建议收藏)
http://t.csdn.cn/GxZ6U- 如有不好的影响请联系删除
类的继承是新的类从已有类那里得到已有的特性。从另一个角度来看这个问题,从已有类产生新类的过程就是类的派生。类的继承和派生机制较好地解决了代码重用的问题。
关于基类和派生类的关系,可以表述为:派生类是基类的具体化,而基类则是派生类的抽象。
#include
#include
using namespace std;
class Person{
private:
string name;
string id_number;
int age;
public:
Person(string name1, string id_number1, int age1) {
name = name1;
id_number = id_number1;
age = age1;
}
~Person() {
}
void show() {
cout << "姓名: " << name << " 身份证号: " << id_number << " 年龄: " << age << endl;
}
};
class Student:public Person{
private:
int credit;
public:
Student(string name1, string id_number1, int age1, int credit1):Person(name1, id_number1, credit1) {
credit = credit1;
}
~Student() {
}
void show() {
Person::show();
cout << "学分: " << credit << endl;
}
};
int main() {
Student stu("白", "110103**********23", 12, 123);
stu.show();
return 0;
}
从已有类派生出新类时,可以在派生类内完成以下几种功能:
派生类可以继承基类中除了构造函数与析构函数之外的成员,但是这些成员的访问属性在派生过程中是可以调整的。从基类继承来的成员在派生类中的访问属性也有所不同。
|基类中的成员 |继承方式 |基类在派生类中的访问属性|
|private |public、protected、private| 不可直接访问|
|public |public、protected、private| public、protected、private|
|protected |public、protected、private| protected、protected、private|
基类的成员可以有public、protected、private3中访问属性,基类的成员函数可以访问基类中其他成员,但是在类外通过基类的对象,就只能访问该基类的公有成员。同样,派生类的成员也可以有public、protected、private3种访问属性,派生类的成员函数可以访问派生类中自己增加的成员,但是在派生类外通过派生类的对象,就只能访问该派生类的公有成员。
派生类对基类成员的访问形式主要有以下两种:
构造函数的主要作用是对数据进行初始化。在派生类中,如果对派生类新增的成员进行初始化,就需要加入派生类的构造函数。与此同时,对所有从基类继承下来的成员的初始化工作,还是由基类的构造函数完成,但是基类的构造函数和析构函数不能被继承,因此必须在派生类的构造函数中对基类的构造函数所需要的参数进行设置。同样,对撤销派生类对象的扫尾、清理工作也需要加入新的析构函数来完成。
调用顺序
#include
#include
using namespace std;
class A{
public:
A() {
cout << "A类对象构造中..." << endl;
}
~A() {
cout << "析构A类对象..." << endl;
}
};
class B : public A{
public:
B() {
cout << "B类对象构造中..." << endl;
}
~B(){
cout << "析构B类对象..." << endl;
}
};
int main() {
B b;
return 0;
}
代码运行结果如下
A类对象构造中...
B类对象构造中...
析构B类对象...
析构A类对象...
可见:构造函数的调用严格地按照先调用基类的构造函数,后调用派生类的构造函数的顺序执行。析构函数的调用顺序与构造函数的调用顺序正好相反,先调用派生类的析构函数,后调用基类的析构函数。
派生类构造函数的一般格式为:
派生类名(参数总表):基类名(参数表) {
派生类新增数据成员的初始化语句
}
-----------------------------------------------------------------
含有子对象的派生类的构造函数:
派生类名(参数总表):基类名(参数表0),子对象名1(参数表1),...,子对象名n(参数表n)
{
派生类新增成员的初始化语句
}
在定义派生类对象时,构造函数的调用顺序如下:
调用基类的构造函数,对基类数据成员初始化。
调用子对象的构造函数,对子对象的数据成员初始化。
调用派生类的构造函数体,对派生类的数据成员初始化。
说明:
派生类可以声明与基类成员同名的成员。在没有虚函数的情况下,如果在派生类中定义了与基类成员同名的成员,则称派生类成员覆盖了基类的同名成员,在派生类中使用这个名字意味着访问在派生类中声明的成员。为了在派生类中使用与基类同名的成员,必须在该成员名之前加上基类名和作用域标识符“::”,即
基类名::成员名
访问声明
访问声明的方法就是把基类的保护成员或共有成员直接写在私有派生类定义式中的同名段中,同时给成员名前冠以基类名和作用域标识符“::”。利用这种方法,该成员就成为派生类的保护成员或共有成员了。
class B:private A{
private:
int y;
public:
B(int x1, int y1) : A(x1) {
y = y1;
}
A::show; //访问声明
};
注意:
声明多继承派生类的一般形式如下:
class 派生类名:继承方式1 基类名1,...,继承方式n 基类名n {
派生类新增的数据成员和成员函数
};
默认的继承方式是private
多继承派生类的构造函数与析构函数:
与单继承派生类构造函数相同,多重继承派生类构造函数必须同时负责该派生类所有基类构造函数的调用。
多继承构造函数的调用顺序与单继承构造函数的调用顺序相同,也是遵循先调用基类的构造函数,再调用对象成员的构造函数,最后调用派生类构造函数的原则。析构函数的调用与之相反。
虚基类的作用:如果一个类有多个直接基类,而这些直接基类又有一个共同的基类,则在最低层的派生类中会保留这个间接的共同基类数据成员的多份同名成员。在访问这些同名成员时,必须在派生类对象名后增加直接基类名,使其唯一地标识一个成员,以免产生二义性。
#include
#include
using namespace std;
class Base{
protected:
int a;
public:
Base(){
a = 5;
cout << "Base a = " << a << endl;
}
};
class Base1: public Base{
public:
Base1() {
a = a + 10;
cout << "Base1 a = " << a << endl;
}
};
class Base2: public Base{
public:
Base2() {
a = a + 20;
cout << "Base2 a = " << a << endl;
}
};
class Derived: public Base1, public Base2{
public:
Derived() {
cout << "Base1::a = " << Base1::a << endl;
cout << "Base2::a = " << Base2::a << endl;
}
};
int main() {
Derived obj;
return 0;
}
代码执行结果如下
Base a = 5
Base1 a = 15
Base a = 5
Base2 a = 25
Base1::a = 15
Base2::a = 25
虚基类的初始化
虚基类的初始化与一般的多继承的初始化在语法上是一样的,但构造函数的调用顺序不同。在使用虚基类机制时应该注意以下几点:
在一定条件下,不同类型的数据之间可以进行类型转换,如可以将整型数据赋值给双精度型变量。在赋值之前,先把整型数据转换成双精度数据,然后再把它赋给双精度变量。这种不同数据类型之间的自动转换和赋值,称为赋值兼容。在基类和派生类对象之间也存有赋值兼容关系,基类和派生类对象之间的赋值兼容规则是指在需要基类对象的任何地方,都可以用子类的对象代替。
class Base{
·····
};
class Derived: public Base{
·····
};
根据赋值兼容规则,在基类Base的对象可以使用的任何地方,都可以使用派生类Derived的对象来代替,但只能使用从基类继承来的成员。具体的表现在以下几个方面:
Base b;
Derived d;
b = d;
Derived d;
Base &br = d;
Derived d;
Base *bp = &d;