继承
子类也称为派生类,父类也称为基类
语法
class 子类:继承方式 父类
公共继承、保护继承、私有继承
父类的private成员无论用哪种继承,子类都不可以访问
公共继承:父类中是公共属性的,子类继承后也是公共的;父类中是保护属性的,子类继承后仍是保护属性的。
保护继承:父类中公共和保护属性的,子类继承后均为保护权限。
私有继承:父类中公共和保护属性的,子类继承后均为私有权限。
继承中的对象模型
父类中的所有非静态成员属性都会被子类继承下去,private属性的是被编译器隐藏了,因此访问不到,但确实是被继承下去了。
#include<iostream>
#include<string>
using namespace std;
class Base1
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class Base2
{
public:
double m_a;
protected:
double m_b;
private:
double m_c;
};
class Son1 :public Base1
{
int m_d;
};
class Son2 : private Base2
{
int m_d;
};
void test()
{
Son1 s1;
Son2 s2;
cout << "s1的大小" << sizeof(s1) << endl;
cout << "s2的大小" << sizeof(s2) << endl;
}
int main()
{
test();
system("pause");
}
我试了一下,s1的16包括父类的三个整型数据加子类中一个整型数据
而s2的32我不太明白,父类中三个double是24,子类中有个整型不应该是24+4=28吗。
深入检查:
利用开发人员命令提示工具查看对象模型:
开始----visual studio 文件夹里有个开发人员命令提示工具
跳转盘符: F:(我是F盘)–>跳转文件路径:cd 具体路径–>查看目录:dir–>命令行:
cl /d1 reportSingleClassLayout类名 文件名
第一个l是L的小写,第二个1是数字1,文件名可以手打也可以通过Tab键选择。
回车后:
但看这个也是24+4=28,不太明白
继承中构造和析构顺序
class Base1
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
public:
Base1()
{
cout << "Base1的构造" << endl;
}
~Base1()
{
cout << "Base1的析构" << endl;
}
};
class Son1 :public Base1
{
public:
int m_d;
Son1()
{
cout << "Son1的构造" << endl;
}
~Son1()
{
cout << "Son1的析构" << endl;
}
};
void test()
{
Son1 s1;
}
可以看出,父类先构造,子类后构造
子类先析构,父类后析构。
继承中同名成员处理方式
当子类与父类出现同名成员时:
访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域。
class Base
{
public:
string m_a;
Base()
{
m_a = "父类";
}
};
class Son :public Base
{
public:
string m_a;
Son()
{
m_a = "子类";
}
void func()
{
cout << m_a << endl
<< Base::m_a << endl;
}
};
void test()
{
Son s1;
s1.func();
cout << s1.m_a<<endl
<<s1.Base::m_a<<endl;
}
遇到同名成员函数也一样,
遇到子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数,想访问父类的同名函数就要加作用域。
继承同名静态成员处理方式
静态和非静态成员出现同名,处理方式一致。
class Base
{
public:
static string m_a;
};
string Base::m_a = "父类";
class Son :public Base
{
public:
static string m_a;
};
string Son::m_a = "子类";
void test()
{
Son s1;
cout << "通过对象访问:"<<endl
<< s1.m_a<< endl
<< s1.Base::m_a <<endl
<< "通过类名访问:"<<endl
<< Base::m_a <<endl
<< Son::m_a <<endl
<< Son::Base::m_a << endl ;
}
多继承语法
允许一个类继承多个类
class 子类:继承方式 父类1,继承方式 父类2.。。
遇到同名成员时,要加作用域。
菱形继承(钻石继承)
两个派生类A、B继承同一个基类O,又有某个类C同时继承两个派生类。
可能会出现二义性:
A、B中都继承有O的属性,C访问该数据时,会出现二义性,可以加作用域解决,其次某项属性C会继承两份,利用虚继承。
在继承最大的基类时加上virtual变为虚继承,最大的基类变成虚基类
class Base
{
public:
int m_age;
};
class Son1 :public Base{};
class Son2 : public Base{};
class Grandson :public Son1, public Son2{};
void test()
{
Grandson g1;
g1.Son1::m_age = 1;
g1.Son2::m_age = 2;
cout << "g1.Son1::m_age =" << g1.Son1::m_age << endl
<< "g1.Son2::m_age =" << g1.Son2::m_age << endl;
}
使用开发者命令行工具
可以看到继承了两个m_age
解决方法:虚继承
class Son1 :virtual public Base{};
class Son2 :virtual public Base{};
利用开发者命令行工具
从Son1和Son2继承下的是(vbptr)虚基类指针
指向的是vbtable:虚基类表,从上图中vbtable中的8和4是偏移量,继承Son1时就是0(起始)+8(偏移量)=8,继承Son2时就是4(起始)+4(偏移量)=8
起始值从左边那列看