继承:
#include<stdio.h>
#include<iostream>
class base
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class son:public base
{
public:
int m_d;
};
void test01()
{
std::cout<<"sizeof of son="<<sizeof(son)<<std::endl;
}
int main()
{
test01();
return 0;
}
执行上面代码得到的结论:
父类中所有非静态成员属性都会被继承下去
父类中私有成员属性,是被编译器给隐藏了,因此是访问不到的,但是确实是被继承下去了
利用开发人员命令提示工具查看对象模型
跳转盘符 (你的文件所在的盘符)比如C:
跳转文件路径 cd 具体路径下
查看命名cl /d1 reportSingleClassLayout类名 文件名
这样也能看到!!
继承中构造和析构顺序
#include<stdio.h>
#include<iostream>
class base
{
public:
base()
{
std::cout << "base构造" << std::endl;
}
~base ()
{
std::cout << "~base析构" << std::endl;
}
};
class son :public base
{
public:
son()
{
std::cout << "son构造" << std::endl;
}
~son()
{
std::cout << "~son析构" << std::endl;
}
};
void test01()
{
base fa;
son so;
}
int main()
{
test01();
return 0;
}
结论:先构造父类,在构造子类,析构的顺序与构造的顺序相反
继承中同名的成员处理方式
问题:当子类与父类出现同名的成员,如何让通过子类对象,访问到子类或父类中同名的数据呢?
访问子类同名成员 直接访问即可
访问父类同名成员 需要加作用域
#include<stdio.h>
#include<iostream>
class base
{
public:
base()
{
m_a = 100;
}
int m_a;
};
class son:public base
{
public:
son()
{
m_a = 200;
}
int m_a;
};
//同名成员属性的处理方式
void test01()
{
base b;
son s;
std::cout << "son下的 m_a= " << s.m_a << std::endl;
//如果通过子类对象访问到父类中的同名成员,需要加作用域
std::cout << "base下的m_a= " << s.base::m_a << std::endl;
}
//同名成员是属性的处理方式
int main()
{
test01();
return 0;
}
总结:子类对象可以直接访问到子类的同名成员
子类对象加作用域可以访问到父类的同名成员
当子类和父类拥有同名的成员函数,子类会隐藏父类中同名的成员函数,加作用域可以访问到父类中同名的成员函数
继承中同名的静态成员的处理方法
问题:继承中同名的静态成员在子类对象上面如何进行访问?
静态成员和非静态成员出现同名,处理方式一致
访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域
#include<stdio.h>
#include<iostream>
class base
{
public:
static void func()
{
std::cout << "base静态" << std::endl;
}
static int m_a;
};
int base::m_a = 100;
class son:public base
{
public:
static void func()
{
std::cout << "son 静态" << std::endl;
}
static int m_a;
};
int son::m_a = 200;
void test01()
{
son s;
//通过对象来访问数据
std::cout << "son 下面的s.m_a=" << s.m_a << std::endl;
std::cout << "base下面的s.m_a=" << s.base::m_a << std::endl;
//通过类名来访问数据
std::cout << "通过类名访问" << std::endl;
std::cout << "son下m_a=" << son::m_a << std::endl;
std::cout << "son下m_a=" << son::base::m_a << std::endl;
//第一个双冒号代表通过类名的方式访问
//第二个双冒号代表访问父类作用域下的
}
void test02()
{
//通过对象访问
son s;
s.func();
s.base::func();
//通过类名访问
son::func();
son::base::func();
}
int main()
{
test01();
test02();
return 0;
}
子类出现和父类同名静态成员函数,也会隐藏父类中所有同名成员函数
如果想访问父类中被隐藏同名成员,需要加作用域
总结:同名静态成员吃力方式和非静态吃力方式一样,只不过有两种访问的方式(通过类名或者通过对象)
多继承语法
c++允许一个类继承多个类
语法:class 子类:继承方式 父类1,继承方式 父类2......
多继承中可能会引发父类中有同名成员 出现,需要加作用域区分
比如一个子类继承了父类1,和父类2,这两个父类中有同名的成员,通过总类访问不同父类中的数据,应该加上作用域来区分
c++实际不建议多继承
菱形继承(钻石继承)
菱形继承的概念:
- 两个派生类继承同一个基类
- 又有某个类同时继承这两个派生类
- 这种继承被称为菱形继承。或者钻石继承
菱形继承问题:
- 1.羊继承了动物的数据,驼同样继承了动物的数据,当羊驼使用数据时就会产生二义性(到底是羊的,还是 驼的)
- 2.羊驼继承动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以
#include<iostream>
using namespace std;
//动物类
class Animal
{
public:
int m_age;
};
//羊类
class sheep :public Animal
{
};
//驼类
class tuo:public Animal
{
};
//羊驼类
class sheeptuo :public sheep,public tuo
{
};
void test01()
{
sheeptuo st;
//st.m_age =18;//产生了 二义性 加上作用域就可以访问
//但是这份数据我们只需要一份就可以,菱形继承导致了数据有两份导致了资源浪费
st.sheep::m_age =19;
st.tuo::m_age =28;
cout<<"st.sheep::m_age="<<st.sheep::m_age<<endl;
cout<<"st.tuo::m_age="<<st.tuo::m_age<<endl;
}
int main()
{
test01();
return 0;
}
解决的:利用虚继承解决菱形继承的问题 继承之前加上关键字virtual 变为虚继承 Animal 类变为虚基类
#include<iostream>
using namespace std;
//动物类
class Animal
{
public:
int m_age;
};
//羊类
class sheep :virtual public Animal
{
};
//驼类
class tuo:virtual public Animal
{
};
//羊驼类
class sheeptuo :public sheep,public tuo
{
};
void test01()
{
sheeptuo st;
st.m_age =18;
st.sheep::m_age =19;
st.tuo::m_age =28;
cout<<"st.m_age="<<st.m_age<<endl;//不会出现不明确的情况
cout<<"st.sheep::m_age="<<st.sheep::m_age<<endl;
cout<<"st.tuo::m_age="<<st.tuo::m_age<<endl;
}
int main()
{
test01();
return 0;
}
虚继承的内部剖析:
简述C++中虚继承的作用及底层实现原理?
- 每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)
- 虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了
- 当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
- 实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
- 我的理解是通过偏移地址,找到虚基类成员,如果两个父类的父类是一个类,类似于B的父类是A,C的父类是A,B,C父类相同,那么我们不需要两份相同的继承。