2020-08-27

继承与派生:权限public>protected>private 如继承方式为A,其最高权限不超过A
private 始终不能用(通过指针可以突破继承方式的限制)
在这里插入图片描述

即使是pirvate的成员变量,也会被继承并且会占用内存(只是没有权限访问),并采用基类的构造函数。

继承时名字遮蔽问题:
名字相同时,派生类的函数会遮蔽基类的函数(不构成重载),如果要使用基类的函数,需要加上域名解析符
(int可以转为bool)

在这里插入图片描述
C类对象如果调用func函数,从内到外一层层找(只找函数名),可以使用A和B类的函数和变量(加上域解析符)C对象大小(20字节),创建的时候,先调用A然后B的构造函数最后C ,销毁的时候CBA的析构函数。

多继承

问题:菱形继承,A基类派生B C ,D继承BC那么D中就会保存两份A的数据,出现二义性
解决:虚继承。

虚继承

由最终派生类直接调用基类,再按照出现的顺序调用其他的构造函数

内存模型:
虚基类的子对象始终位于派生类对象的最后面
将派生类分为固定部分和共享部分,并把共享部分放在最后,几乎所有的编译器都在这一点上达成了共识。
只要含有虚继承,内存中就会存入一个虚基类指针

向上转型

间派生类对象或指针赋值给基类对象或指针,赋值的本质是把内存中的数据拷贝存取到你一内存。
class A{
public:
	A(int a);
public:
	void display();
protected:
	int m_a;
};
A::A(int a) : m_a(a){ }
void A::display(){
	cout << "Class A: m_a=" << m_a << endl;
}
//中间派生类B
class B : public A{
public:
	B(int a, int b);
public:
	void display();
protected:
	int m_b;
};
B::B(int a, int b) : A(a), m_b(b){ }
void B::display(){
	cout << "Class B: m_a=" << m_a << ", m_b=" << m_b << endl;
}
//基类C
class C{
public:
	C(int c);
public:
	void display();
protected:
	int m_c;
};
C::C(int c) : m_c(c){ }
void C::display(){
	cout << "Class C: m_c=" << m_c << endl;
}
//最终派生类D
class D : public B, public C{
public:
	D(int a, int b, int c, int d);
public:
	void display();
private:
	int m_d;
};
D::D(int a, int b, int c, int d) : B(a, b), C(c), m_d(d){ }
void D::display(){
	cout << "Class D: m_a=" << m_a << ", m_b=" << m_b << ", m_c=" << m_c << ", m_d=" << m_d << endl;
}
int main(){
	A *pa = new A(1);
	B *pb = new B(2, 20);
	C *pc = new C(3);
	D *pd = new D(4, 40, 400, 4000);
	pa = pd;
	pa->display();
	pb = pd;
	pb->display();
	pc = pd;
	pc->display();
	cout << "-----------------------" << endl;
	cout << "pa=" << pa << endl;
	cout << "pb=" << pb << endl;
	cout << "pc=" << pc << endl;
	cout << "pd=" << pd << endl;

	system("pause");
	return 0;
}
pa=0x317fc
pb=0x317fc
pc=0x317f8
pd=0x317f8
指针的起始位置不一样,发生转型
类似于用int来接受double会发生截断一样。

多态:
基类指针可以指向派生类对象,并且可以使用派生类的成员变量,但是却只能使用基类的成员函数。
修改指针,起始就修改了函数中的this,this用来找对象,但是指针本身指向的函数不会改变。
解决方法:虚函数。

多态:父类指针可以指向子类对象并可以使用子类从父类继承来的成员函数。

1.只需要在函数声明处加上virtual,定义出可以不加
2.只需要将基类函数声明为虚函数,派生类中具有遮蔽关系的同名函数自动变成虚函数。。
3.派生类没有函数遮蔽,则使用基类虚函数
4.构造函数不能是虚函数,析构函数有时候必须是虚函数。

析构函数必须是虚函数:

如果用基类指针指向派生类对象,当delete时,只会调用基类的析构函数,造成内存泄漏
如果使用虚函数,则会调用派生类的析构函数(会自动调用基类析构函数)

纯虚函数:

virtual 返回值类型 函数名 (函数参数) = 0;
没有函数体,只有函数声明,末尾加上=0(标志纯虚函数)
含纯虚函数的类叫抽象类,因为无法被实例化,无法创建对象(纯虚函数没有函数体,无法调用,无法分配空间)
派生类必须实现所有纯虚函数后才能实例化。
1) 一个纯虚函数就可以使类成为抽象基类,但是抽象基类中除了包含纯虚函数外,还可以包含其它的成员函数
(虚函数或普通函数)和成员变量。

2) 只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数。如下例所示:

虚函数表:

如果一个类包含了虚函数,那么在创建该类的对象时就会额外地增加一个数组,数组中的每一个元素都是虚函数的
入口地址。不过数组和对象是分开存储的,为了将对象和数组关联起来,编译器还要在对象中安插一个指针,指
向数组的起始位置。这里的数组就是虚函数表(Virtual function table),对象的实际内存中只是多了一个
指向数组起始位置的指针。
C++ 的对象内存模型主要包含了以下几个方面的内容:
如果没有虚函数也没有虚继承,那么对象内存模型中只有成员变量。
如果类包含了虚函数,那么会额外添加一个虚函数表,并在对象内存中插入一个指针,指向这个虚函数表。
如果类包含了虚继承,那么会额外添加一个虚基类表,并在对象内存中插入一个指针,指向这个虚基类表
编译器会在虚函数表 vftable 的开头插入一个指针,指向当前类对应的 type_info 对象。当程序在运
行阶段获取类型信息时,可以通过对象指针 p 找到虚函数表指针 vfptr,再通过 vfptr 找到 type_info 
对象的指针,进而取得类型信息。
运行时类型识别(Run-Time Type Identification,RTTI)
函数绑定,函数调用本质是执行函数体中的代码,通过函数名找到其在内存中的地址。

静态绑定:编译链接期间即可找到函数的地址完成函数绑定
动态绑定:例如多态:只有程序运行后根据实际情况由用户来觉得绑定哪个函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值