继承
1. 单继承
1.定义:被继承的为基类,继承者称为派生类
2.派生继承的三种方式:
- 公有继承public
- 保护继承protected
- 私有继承private
class Base//基类
{
}
class Derived//派生类
:public/protected/private Base
{
}
3.初始化的顺序
先初始化父类后初始化子类(说法:当创建派生类对象时,先调用基类构造函数,再调用派生类构造函数)
细品一下:要创建一个派生类对象,显然要调用的是派生类自己的构造函数,只是在调用派生类构造函数的过程中,需要在初始化表达式中,先调用基类的构造函数,完成基类成员的初始化之后,最后再执行派生类构造函数的函数执行体
因比,严格来说,上述说法是错误的,在派生类对象构造处讲到
#include<iostream>
using namespace std;
class father
{
public:
father()
{
cout << "i am father" << endl;
}
private:
};
class son
:public father
{
public:
son()
{
cout << "i am son" << endl;
}
private:
};
int main()
{
son s;
return 0;
}
结果
先调用父类的构造函数,后调用子类的构造函数
i am father
i am son
4.派生类生成的过程有是三个步骤:
- 吸收基类的成员
- 改造基类的成员
- 添加自己新的成员
#include<iostream>
using namespace std;
class father
{
public:
father()
{
cout << "i am father" << endl;
}
void print()
{
cout << "father-->print ";
cout << m_father << endl;
}
private:
const char* m_father = "i am father";
};
class son
:public father
{
public:
son()
{
cout << "i am son" << endl;
}
void print()
{
cout << "son-->print ";
cout << m_son << endl;
}
private:
const char* m_son = "i am son";
};
int main()
{
son s;
s.print();
return 0;
}
结果
吸收基类的函数,改造函数,添加自己的函数
i am father
i am son
son-->print i am son
5.派生方式对基类成员的访问权限控制
- 基类的私有成员,不管哪种继承方式,在派生类内部是无法访问的
- 基类的非私有成员,在派生类内部的都是可以访问的
公有继承时,在派生类内部的访问权限保持不变
保护继承时,在派生类内部的访问权限都是proteceted的
私有继承时,在派生类内部的访问权限都是private的
像是一种交集关系,继承方式和基类的内部权限取交集
6.不能继承的有:
- 构造函数
- 析构函数
- 赋值运算符函数(对象的复制有关)
- operator new/delete
- 友元关系
7.派生类对象的构造
- 派生类内部没有显式提供构造函数但编译器会自动为派生类提供一个默认构造函数该构造函数执行时,会初始化基类部分,进而调用基类的默认构造函数
- 派生类有显式定义构造函数,基类没有显式定义构造函数当创建派生类对象时,执行基类初始化时,也会自动调用
基类的默认构造函数 - 派生类有显式定义构造函数,基类也有显式定义构造函数当创建派生类对象时,执行基类初始化时,必须要显式在派生类构造函数的初始化列表中,显式调用基类的有参构造函数
#include<iostream>
using namespace std;
class father
{
public:
father(int num)
{
m_ifather = num;
cout << "i am father" << endl;
}
void print()
{
cout << "father-->print ";
cout << m_cfather << endl;
}
private:
const char* m_cfather = "i am father";
int m_ifather;
};
class son
:public father
{
public:
son()
:father(1)
{
cout << "i am son" << endl;
}
void print()
{
cout << "son-->print ";
cout << m_son << endl;
}
private:
const char* m_son = "i am son";
};
int main()
{
son s;
s.print();
return 0;
}
派生类对象的销毁(可以想象成入栈出栈的过程)
与创建的过程相反:
先调用派生类的析构函数,完成派生类的资源清理操作之后,自动调用基类析构函数,完成基类部分的资源清理操作
派生类对象的析构顺序:
1.派生类的析构函数
2.类对象成员的析构函数
3.基类的析构函数
#include<iostream>
using namespace std;
class father
{
public:
father(int num)
{
m_ifather = num;
cout << "i am father" << endl;
}
void print()
{
cout << "father-->print ";
cout << m_cfather << endl;
}
~father()
{
cout << " ~father()" << endl;
}
private:
const char* m_cfather = "i am father";
int m_ifather;
};
class son
:public father
{
public:
son()
:father(1)
{
cout << "i am son" << endl;
}
void print()
{
cout << "son-->print ";
cout << m_son << endl;
}
~son()
{
cout << "~son()" << endl;
}
private:
const char* m_son = "i am son";
};
int main()
{
son s;
s.print();
return 0;
}
结果(看析构的顺序)
i am father
i am son
son-->print i am son
~son()
~father()
2多重继承
1.初始化的顺序
多重继承时,记得要在每一个基类的前面加上访问权限,否则就是采用了私有继承
多重继承时,基类被初始化的顺序只与其被继承时的顺序有关,而与其在派生类构造函数的出是表达式中的顺序无关
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
private:
};
class B
{
public:
B()
{
cout << "B()" << endl;
}
private:
};
class C
:public A
,public B
{
public:
C()
:B()
,A()
{
cout << "C()" << endl;
}
private:
};
int main()
{
C c;
}
结果
A()
B()
C()
2.多重继承产生的问题
成员名冲突的二义性
#include<iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
}
void print()
{
cout << "m_A = " << m_A << endl;
}
private:
int m_A = 1;
};
class B
:public A
{
public:
B()
:A()
{
cout << "B()" << endl;
}
void printB()
{
cout << "m_B = " << m_B << endl;
}
private:
int m_B = 2;
};
class C
:public A
{
public:
C()
:A()
{
cout << "C()" << endl;
}
void printC()
{
cout << "m_C = " << m_C << endl;
}
private:
int m_C = 3;
};
class D
:public B
,public C
,public A
{
public:
D()
:B()
,C()
,A()
{
cout << "D()" << endl;
}
private:
int m_D = 4;
};
int main()
{
D d;
d.print();
}
此时编译会出问题,因为D中的print有B类,C类,A类的print的函数,编译器无法分辨
解决方法:
用类的作用域限定符
int main()
{
D d;
d.A::print();
d.B::print();
d.C::print();
d.C::A::print();
}
类大小,内存分布
1>class A size(4):
1> ±–
1> 0 | m_A
1> ±–
1>class B size(8):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | m_A
1> | ±–
1> 4 | m_B
1> ±–
1>class C size(8):
1> ±–
1> 0 | ±-- (base class A)
1> 0 | | m_A
1> | ±–
1> 4 | m_C
1> ±–
1>class D size(24):
1> ±–
1> 0 | ±-- (base class B)
1> 0 | | ±-- (base class A)
1> 0 | | | m_A
1> | | ±–
1> 4 | | m_B
1> | ±–
1> 8 | ±-- (base class C)
1> 8 | | ±-- (base class A)
1> 8 | | | m_A
1> | | ±–
1>12 | | m_C
1> | ±–
1>16 | ±-- (base class A)
1>16 | | m_A
1> | ±–
1>20 | m_D
1> ±–
分析
A size为4,因为有一个int类型的成员
B size为8,因为自身有一个int类型的成员,又继承了A的成员
C size为8,和B同理
D size为24,继承了类A,B,C,一共是20,加上自身有一个int类型的成员,所以是24
虚拟继承能解决多继承发生的二义性问题,在其他文章再写.