一、继承的基本概念
? 继承:子类继承父类的属性和行为
? 作用:代码复用
继承分类:
1. 按访问属性分为public、private、protected三类
1)public: 父类属性无更改,pubic, private, protected 仍是自己本身(子类成员函数可以访问父类的public和protected,子类对象可以访问public)
2)private: 父类属性全变为privates(子类不能访问父类属性)
3)protected: 父类public变为protected,其他不变(子类成员函数可以访问父类的public和protected,子类对象不能访问)
2. 按继承父类的个数分为单继承和多继承
类的成员函数由所有对象共享,但是每个对象有单独的成员变量,所以利用sizeof(对象时),字节数为所有成员变量的大小
普通继承:子类继承父类即继承父类的所有属性及行为,当多继承时,有父类的父类的两份拷贝
虚继承:菱形继承,共享一个虚基类
二、类与类的关系
1. 父类和子类
普通继承:先执行父类构造函数,再执行子类构造函数;先执行子类析构函数,再执行父类析构函数
1)当子类中没有构造函数或析构函数,父类却需要构造函数和析构函数时,编译器会为子类提供默认的构造函数与析构函数以调用父类的构造和析构函数
2)子类的内存结构:子类继承父类,类似在子类中定义了父类的对象,如此当产生Derive类的对象时,会先产生成员对象base,这需要调用其构造函数
? 当Derive类没有构造函数时,为了能够在Derive类对象产生时调用成员对象的构造函数,编译器同样会提供默认的构造函数,以实现成员构造函数的调用
class Base{...};
class Derive {
public:
Base base; //原来的父类Base 成为成员对象
int derive; // 原来的子类派生数据
};
3)子类内存中的数据排列:先安排父类的数据,后安排子类新定义的数据
注意:当子类中有构造函数,父类无构造函数,不会给父类提供默认的构造函数
普通子类继承父类 c++ 代码示例:
#include <stdio.h>
class Base { //基类定义
public:
Base() {
printf("Base\n");
}
~Base() {
printf("~Base\n");
}
void setNumber(int n) {
base = n;
}
int getNumber() {
return base;
}
public:
int base;
};
class Derive : public Base { //派生类定义
public: void showNumber(int n) {
setNumber (n);
derive = n + 1;
printf("%d\n", getNumber());
printf("%d\n", derive);
}
public:
int derive;
};
int main(int argc, char* argv[]) {
Derive derive;
derive.showNumber(argc);
return 0;
}
汇编标识:
00401000 push ebp
00401001 mov ebp, esp
00401003 sub esp, 0Ch
00401006 lea ecx, [ebp-0Ch] ;获取对象首地址作为this指针
00401009 call sub_401050 ;调用类Derive的默认构造函数 ①
0040100E mov eax, [ebp+8]
00401011 push eax ;参数2:argc
00401012 lea ecx, [ebp-0Ch] ;参数1:传入this指针
00401015 call sub_4010E0 ;调用成员函数showNumber ②
0040101A mov dword ptr [ebp-4], 0
00401021 lea ecx, [ebp-0Ch] ;传入this指针
00401024 call sub_401090 ;调用类Derive的默认析构函数 ③
00401029 mov eax, [ebp-4]
0040102C mov esp, ebp
0040102E pop ebp
0040102F retn
00401050 push ebp ;子类Derive的默认构造函数分析
00401051 mov ebp, esp
00401053 push ecx
00401054 mov [ebp-4], ecx
00401057 mov ecx, [ebp-4] ;以子类对象首地址作为父类的this指针 ①
0040105A call sub_401030 ;调用父类构造函数
0040105F mov eax, [ebp-4]
00401062 mov esp, ebp
00401064 pop ebp
00401065 retn
00401090 push ebp ;子类Derive的默认析构函数分析
00401091 mov ebp, esp
00401093 push ecx
00401094 mov [ebp-4], ecx
00401097 mov ecx, [ebp-4] ;以子类对象首地址作为父类的this指针 ①
0040109A call sub_401070 ;调用父类析构函数
0040109F mov esp, ebp
004010A1 pop ebp
004010A2 retn
? 子类中定义了其他对象作为成员,并在初始化列表中指定了某个成员的初始化值时:先构造父类,然后按声明顺序构造成员对象和初始化列表中指定的成员,最后构造自己
类中定义了其他对象作为成员,并在初始化列表中指定了某个成员的初始化值时的 c++ 示例代码:
class Member{
public:
Member() {
member = 0;
}
int member;
};
class Derive : public Base {
public:
Derive():derive(1) {
printf("使用初始化列表\n");
}
public:
Member member; //类中定义其他对象作为成员
int derive;
};
int main(int argc, char* argv[]) {
Derive derive;
return 0;
}
汇编标识:
00401000 push ebp
00401001 mov ebp, esp
00401003 sub esp, 10h
00401006 lea ecx, [ebp-10h] ;传递this指针
00401009 call sub_401050 ;调用Derive的构造函数 ①
0040100E mov dword ptr [ebp-4], 0
00401015 lea ecx, [ebp-10h] ;传递this指针
00401018 call sub_4010D0 ;调用Derive的析构函数 ⑥
0040101D mov eax, [ebp-4]
00401020 mov esp, ebp
00401022 pop ebp
00401023 retn
00401050 push ebp ; Derive构造函数
00401051 mov ebp, esp
00401053 push ecx
00401054 mov [e