03c++继承与多态

目录:

  • 继承的本质和原理
  • 派生类的构造过程
  • 重载覆盖 隐藏
  • 静态绑定和动态绑定
  • 多态 vfptr和vftable
  • 抽象类的设计原理
  • 多重继承以及问题
  • 虚基类 vbptr和vbtable
  • RTTI
  • c++四种类强转
  • 继承多态常见笔试面试题目分享

1、继承的本质和原理:

继承方式: 基类的访问限定 派生类的访问限定 main外部的访问限定
三种:私有全私有
保护二级私有
公有一级私有
class A
{
public:
	int ma;
protected:
	int mb;
private:
	int mc;
};


class B :public A
{
public:
	void func()
	{
		cout << "func: " << func << endl;
	}
	int md;
protected:
	int me;
private:
	int mf;
};

class C :public B
{
	//在c里面,请问ma的访问限定的是什么?
	//私有的

};

2、派生的构造过程

* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
#if 0
/*
* 派生类如何初始化基类继承来的成员变量?
* 通过调用基类相应的构造函数
*
* 派生类的构造函数和析构函数负责初始化和清理派生类部分
*/
class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }

protected:
	int ma;
};

class Derive :public Base
{
public:
	Derive(int data = 20) :mb(data), Base(data)
	{
		cout << "Derive()" << endl;
	}
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;

};

int main()
{
	Derive v;
	/*
	输出:
		Base()
		Derive()
		~Derive()
		~Base()
	先调用派生类的析构函数
	*/
	return 0;
}
#endif

3、重载 隐藏 覆盖

图像分析:
在这里插入图片描述
在这里插入图片描述
从图中可以看出来有危险。

/*重载 隐藏 覆盖
1、重载关系
必须处在同一个作用域中,函数名字相同,参数列表不同
2、隐藏:作用域的隐藏
*/

class Base
{
public:
	Base(int data = 10) :ma(data) {}
	void show() { cout << "Base::show()" << endl; }
	void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; }


private:
	int mb;
};

int main()
{
#if 0
	Derive d;
	d.show();
	d.Base::show(); //这样子才能调用基类的
	//d.show(10); 优先查看派生类自己的作用域成员,没有的话才去基类里面找
	d.Base::show(10);
#endif 
	Base b(10);
	Derive c(20);
	//基类到派生类的转换
	b = c; //类型从下到上的转化

	//d = b;//类型从上到下的转换 error 

	//基类指针 引用 <-派生类对象 默认派生类到基类的转换
	Base* pb = &c; //只能访问派生基类部分的成员
	pb->show();
	pb->show(10);
	/*Base::show()
	Base::show(int)
	*/

	((Derive*)pb)->show(); //这样子就调用了派生类的show

	//在继承结构中进行上下的类型的转换,默认支持从下到上的转换
	Derive* pd = (Derive*)&b; //派生类的show方法 没有派生类对象 
	//b是基类哇 不安全涉及内存的非法访问
	pd->show();
	return 0;
}

4、虚函数、静态绑定、动态绑定

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


/*
* 虚函数 ,静态绑定 和动态绑定
* 总结一:如果类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生了一个唯一的vftable虚函数表,
*虚函数表中的主要存储的内容就是RTTI指针和虚函数的地址 ,程序运行中,每一张虚函数表都会记载到内存的.rodata区
*
* 总结二:
* 一个类里面定义了函数,那么这个类定义的对象运行的时候,内存中开始 部分,多存储了vfptr虚函数指针,指向相应类型 的虚函数表vftable
* 一个类型定义的n个对象,他们的vfptr指向都是同一张虚函数表
*
* 总结三:
* 一个类里卖弄虚函数的个数,不影响对象内存大小(vfptr),影响的是虚函数表的大小
*
* 总结四:
* 如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
* 而且基类的方法都是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
*/

class Base
{
public:
	Base(int data = 10) :ma(data) {}
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};

int main()
{
	Derive d(50);
	Base* pb = &d;
	pb->show(); //静态(编译时期)的绑定 (函数的调用)
	//如果发现是虚函数,就进行动态绑定了
	pb->show(10);
	/*
	E8 0B EE FF FF       call        Base::show (07FF6900A13F7h)
	BA 0A 00 00 00       mov         edx,0Ah
	48 8B 4D 28          mov         rcx,qword ptr [pb]
	E8 E2 EA FF FF       call        Base::show (07FF6900A10DCh)
	*/

	cout << sizeof(Base) << endl;
	cout << sizeof(Derive) << endl;

	cout << typeid(pb).name() << endl;

	/*如果Base没有虚函数,*pb识别的就是编译时期的 如果有虚寒申诉就是运行时期的*/
	cout << typeid(*pb).name() << endl;
	/*
	Derive::show
	Base::show(int)
	16
	24
	class Base * __ptr64
	class Derive
	| vptr (8 字节) | ma (4 字节) | 填充 (4 字节) |

	*/
	return 0;
}
/*
* Base::show()
Base::show(int)
4
8
class Base * __ptr64
class Base

*/

5、多态

/*
解释多态:
静态编译时期:函数重载
编译阶段就已经确定好的;
动态编译时期:
在继承中,基类指针引用指向派生类的对象,通过该指针引用调用同名的覆盖方法(虚函数)基类指针指向哪个派生类对象,就会调用哪个派生类对象同名覆盖的方法,称为多态。
访问谁的vfptr=>继续访问谁的vftable;调用对应的派生类对象的方法
*/

class Animal
{
public:
	Animal(string name) :_name(name) {}
	virtual void  bark()
	{

	}
protected:
	string _name;
};

class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << "猫叫" << endl; }
};

class Dog :public Animal
{
public:
	Dog(string name) :Animal(name) {}
	void bark() { cout << "狗叫" << endl; }
};

void bark(Animal* p)
{
	p->bark();
}
int main()
{
	Cat a("wocao");
	Dog b("shabi");
	bark(&a);
	bark(&b);
	return 0;
}

6、理解抽象类

//汽车的基类
class Car
{
public:
	Car(string name, double oil) :name(name), oil(oil) {}
	double getLiftMiles()
	{
		return oil * getMilesPersonGallon();
	}
public:
	string name;
	double oil;
	virtual double getMilesPersonGallon() = 0; //纯虚函数

};

class Bnze :public Car
{
public:
	Bnze(string name, double oil) :Car(name, oil) {}
	double getMilesPersonGallon() { return 20.0; }
};

class Audi :public Car
{
public:
	Audi(string name, double oil) :Car(name, oil) {}
	double getMilesPersonGallon() { return 18.0; }
};

void  showCarLeftMiles(Car& car)
{
	cout << "Car name:" << car.name << endl << car.getLiftMiles() << endl;

}
int main()
{
	Audi car("1", 12);
	Bnze car1("2", 12);
	showCarLeftMiles(car);
	showCarLeftMiles(car1);
	return 0;
}

7、再谈动态绑定

/*虚函数和动态绑定 问题:是不是虚函数的调用一定就是动态绑定? 不是
类的构造函数中,调用虚函数不会发生动态绑定
*/


class Base
{
public:
	Base(int data = 10) :ma(data) {}
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) {}
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};
int main()
{
	Base b;
	Derive d;
	/*
	E8 6F E9 FF FF       call        Base::Base (0121118h)
	6A 14                push        14h
	8D 4D DC             lea         ecx,[d]
	E8 24 E9 FF FF       call        Derive::Derive (01210D7h)
	对象本身调用虚函数是静态绑定
	*/
	b.show();
	d.show();

	//动态绑定
	Base* pb1 = &b;
	pb1->show();
	Base* pb2 = &d;
	pb2->show();

	//动态绑定
	Base& rb1 = b;
	rb1.show();
	Base& rb2 = d;
	rb2.show();

	//动态绑定
	Derive* p = (Derive*)&b;
	p->show();
	//如果不是引用或者指针调用则是静态调用
	return 0;
}

8、虚析构函数

/*

/*
1、哪些函数不能实现虚函数
虚函数依赖:
	1虚函数能产生地址,存储在虚函数表vftable中
	2、对象必须存在(vfptr->vftable->虚函数地址)

构造函数:
1、virtual + 构造函数
2、构造函数中华调用的任何函数都是静态绑定的调用虚函数,也不会产生静态绑定派生类的对象构造过程。
1、先调用的是基类的构造函数2、才调用派生类的构造函数

static静态成员方法 No virtual + static

问题二:
虚析构函数 析构函数调用的时候存在,对象是存在的!
什么时候吧基类的析构函数必须实现成虚函数?
基类的指针(引用)指向堆上new出来的派生类对象的时候,delete pb(基类的指针)
,它调用析构函数的时候必须发生动态绑定,否则会导致派生类的析构函数无法调用
*/


class Base
{
public:
	Base(int data = 10) :ma(data) { cout << "Base()" << endl; }
	virtual ~Base() { cout << "~Base()" << endl; }
	//虚函数
	virtual void show() { cout << "Base::show()" << endl; }
	//虚函数
	virtual void show(int) { cout << "Base::show(int)" << endl; }

protected:
	int ma;
};
class Derive :public Base
{
public:
	Derive(int data = 20) :Base(data), mb(data) { cout << "Derive()" << endl; }
	//基类的析构函数是虚函数,派生类的析构函数默认成为虚函数
	~Derive() { cout << "~Derive()" << endl; }
	void show() { cout << "Derive::show" << endl; } //不写virtual也是虚函数


private:
	int mb;
};

int main()
{
	Base* pb = new Derive(10);
	pb->show();//动态绑定
	delete pb;//派生类的析构函数,没有呗调用
	/*
	pb->Base Base::~Base 对于析构函数的调用就是静态绑定了

	发现是虚析构函数就是动态绑定了
	call Base::~Base
	*/
	return 0;
}

在这里插入图片描述
在这里插入图片描述

9、题目分享

class Animal
{
public:
	Animal(string name) :_name(name) {}
	virtual void  bark() = 0;
protected:
	string _name;
};

class Cat :public Animal
{
public:
	Cat(string name) :Animal(name) {}
	void bark() { cout << "猫叫" << endl; }
};

class Dog :public Animal
{
public:
	Dog(string name) :Animal(name) {}
	void bark() { cout << "狗叫" << endl; }
};

int main()
{
	Animal* p1 = new Cat("加菲猫");
	Animal* p2 = new Dog("二哈");

	int* p11 = (int*)p1;

	int* p22 = (int*)p2;
	int tmp = p11[0]; //cat的前四个字节 vfptr -> Dog vftable
	p11[0] = p22[0];  //dog的前四个字节 vfprt ->Cat vftable
	p22[0] = tmp;

	p1->bark(); //cat vfptr -> dog vfptr->dog vftable
	p2->bark();
	delete p1;
	delete p2;
	return 0;
}
class Base
{
public:
	virtual void show(int i = 10)
	{
		cout << "call Base::show i" << i << endl;
	}
};

class Derive :public Base
{
public:
	void show(int i = 20)
	{
		cout << "call Derive ::show i " << i << endl;
	}
};

int main()
{
	Base* p = new Derive();//虚析构函数

	/*
	* push 0Ah =》函数调用参数压栈的在编译时期就确定好的 是基类的10
	* mov eax,dword ptr[p];
	* mov ecx,dword ptr[eax]
	* call ecx;
	*/
	p->show();
	delete p; //动态绑定 p->Derive vfptr ->Derive vftable

	return 0;
}
class Base
{
public:
	virtual void show()
	{
		cout << "call Base::show " << endl;
	}
};

class Derive :public Base
{
private: //改成私有还能调用吗?
	void show()
	{
		cout << "call Derive ::show " << endl;
	}
};

int main()
{
	Base* p = new Derive();
	/*
	* 成员方法能不能调用就是说方法的访问权限是不是public,实在编译时期就需要确定的
	* 只能看到Base::show
	* 基类:call Base::show静态
	* call Derive::show动态
	*/
	p->show(); //最终调用是在运行时期才确定的 这个调用的call Derive show()
	delete p;
	return 0;
}

class Base
{
public:
	Base()
	{
		/*
		push ebp
		mov ebp,esp
		sub esp,4Ch
		rep  stos esp<->ebp
		vfptr <= &Base::vftable
		*/
		cout << "call Base()" << endl;
		clear();
	}
	void clear() { memset(this, 0, sizeof(*this)); }

	virtual void show()
	{
		cout << "call Base::show()" << endl;
	}
};

class Derive :public Base
{
public:
	/*
	push ebp
	mov ebp,esp
	sub esp,4Ch
	rep  stos esp<->ebp
	vfptr <= &Derivev::vftable
	*/
	Derive()
	{
		cout << "call Derive ()" << endl;
	}

	void show()
	{
		cout << "call Derive::show()" << endl;
	}

};

int main()
{
	//Base* p = new Base();
	//p->show();//动态绑定
	//delete p;

	Base* p1 = new Derive();
	p1->show(); //动态绑定
	delete p1;
	return 0;
}
  • 14
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值