关于继承和多态

/*
1.继承的本质和原理
继承的本质:a.代码的复用
类与类之间的关系
组合:a part of ... ...一部分的关系
继承:a kind of ... ...一种的关系

继承方式	基类的访问限定	派生类的访问限定	(main)外部的访问限定
public
			public			public					Y		
			protected		protected				N
			private			不可见					N
protected(基类的成员的访问限定,在派生类里面是不可能超过继承方式的)
			public			protected				N
			protected		protected				N
			private			不可见					N
private
			public			private					N
			protected		private					N
			private			不可见					N

总结:
1.外部只能访问对象public的成员,protected和private的成员无法直接访问
2.在继承结构中,派生类从基类可以继承过来private的成员,但是派生类却无法直接访问
3.protected和private的区别?在基类中定义的成员,想被派生类访问,但是不想被外部访问,
那么在基类中,把相关成员定义成protected保护的;如果派生类和外部都不打算访问,那么
在基类中,就把相关成员定义成private私有的。

默认的继承方式:
要看派生类是用class定义的,还是struct定义的?
class定义派生类,默认继承方式就是private私有的
struct定义派生类,默认继承方式就是public
*/

class A
{
public:
	int ma;
protected:
	int mb;
private:
	int mc; //自己或者友元能访问私有的成员
};

//继承 A基类/父类  B派生类/子类
class B : public A 
{
public:
	int md;
protected:
	int me;
private:
	int mf;
};
/*
1.派生类从继承可以继承来所有的成员(变量和方法),除过构造函数和析构函数
派生类怎么初始化从基类继承来的成员变量呢?
解答:通过调用基类相应的构造函数来初始化

派生类的构造函数和析构函数,负责初始化和清理派生类部分
派生类从基类继承来的成员,的初始化和清理由谁来负责呢?是由基类的构造和析构函数来负责

》》》派生类对象构造和析构的过程是:
1.派生类调用基类的构造函数,初始化从基类继承来的成员
2.调用派生类自己的构造函数,初始化派生类自己特有的成员
....派生类对象的作用域到期了
3.调用派生类的析构函数,释放派生类成员可能占用的外部资源(堆内存,文件)
4.调用基类的析构函数,释放派生类内存中,从基类继承来的成员可能占用的外部资源(堆内存,文件)
*/
class Base
{
public:
	Base(int data) : ma(data) { cout << "Base()" << endl; }
	~Base() { cout << "~Base()" << endl; }
protected:
	int ma;
};

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

int main()
{
	Derive d(20);
	return 0;
}
/*
重载、隐藏、覆盖
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()
{
	Derive d;
	d.show();
	d.Base::show();
	d.Base::show(10);

	return 0;
/*
重载、隐藏、覆盖
1.重载关系
	一组函数要重载,必须处在同一个作用域当中;而且函数名字相同,参数列表不同
2.隐藏(作用域的隐藏)的关系
	在继承结构当中,派生类的同名成员,把基类的同名成员给隐藏掉了


1.把继承结构,也说成从上(基类)到下(派生类)的结构
2.
基类对象 -> 派生类对象
派生类对象 -> 基类对象

基类指针(引用)-> 派生类对象
派生类指针(引用)-> 基类对象

//在继承结构中,进行上下的类型转换,默认只支持从下到上的类型的转换 OK

*/
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()
{
	Base b(10);
	Derive d(20);

	//基类对象b <- 派生类对象d 类型从下到上的转换 Y
	b = d;

	//派生类对象d <- 基类对象b 类型从上到下的转换 N
	//d = b; //不可以

	//基类指针(引用)<- 派生类对象 类型从下到上的转换 Y
	Base *pb = &d;
	pb->show();
	((Derive*)pb)->show();
	pb->show(10);

	//派生类指针(引用) <- 基类对象 类型从上到下的转换 N
	//Derive *pb = &b;
	Derive *pd = (Derive*)&b; //不安全,涉及了内存的非法访问
	pd->show();
}
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()
{
	Derive d(50);
	Base *pb = &d;
	//静态(编译时期)的绑定(函数的调用)call Base::show
	pb->show();
	//静态绑定call Base::show
	pb->show(10);

	cout << sizeof(Base) << endl;//4
	cout << sizeof(Derive) << endl;//8

	cout << typeid(pb).name() << endl; //class Base*
	cout << typeid(*pb).name() << endl; //class Base

	return 0;
}
/*
虚函数,静态绑定和动态绑定

覆盖:
基类和派生类的方法,返回值,函数名以及参数列表都相同,而且基类的方法是虚函数
那么派生类的方法就自动处理成虚函数,它们之间成为覆盖关系

一个类添加了虚函数,对这个类有什么影响
总结一:
一个类里面定义了虚函数,那么编译阶段,编译器给这个类类型产生一个唯一的vftable虚函数表,
虚函数表中主要存储的内容就是RTTI指针和虚函数的地址。当程序运行时,每一张虚函数表都会
加载到内存的.rodata区

总结二:
一个类里面定义了虚函数,那么这个类定义的对象,其运行时,内存中开始部分,多存储一个vfptr虚函数
指针,指向相应类型的虚函数表vftable。一个类型定义的n个对象,他们的vfptr指向的都是同一张虚函数表

总结三:
一个类里面虚函数的个数,不影响对象内存大小(vfptr),影响的是虚函数表的大小
*/
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) {}
	/*
	总结四:
	如果派生类中的方法,和基类继承来的某个方法,返回值、函数名、参数列表都相同,
	而且基类的方法是virtual虚函数,那么派生类的这个方法,自动处理成虚函数
	*/
	void show() { cout << "Derive::show()" << endl; }
private:
	int mb;
};

int main()
{
	Derive d(50);
	Base *pb = &d;

	/*
	pb->Base Base::show 如果发现show是普通函数,就进行静态绑定 call Base::show
	pb->Base Base::show 如果发现show是虚函数,就进行动态绑定
	mov eax, dword ptr[pb]
	mov ecx, dword ptr[eax]
	call ecx (虚函数的地址) 动态(运行时期)绑定(函数的调用)
	*/

	//静态(编译时期)的绑定(函数的调用)call Base::show
	pb->show();
	//静态绑定call Base::show
	pb->show(10);

	cout << sizeof(Base) << endl;//4 8(多了虚函数指针)
	cout << sizeof(Derive) << endl;//8 12

	cout << typeid(pb).name() << endl; //class Base*
	/*
	pb的类型:Base -> 有没有虚函数
	如果Base没有虚函数,*pb识别的就是编译时期的类型 *pb <=> 类型
	如果Base有虚函数,*pb识别的就是运行时期的类型 RTTI类型
	*/
	cout << typeid(*pb).name() << endl; //class Base class Derive

	/*
	Base的两个show函数都是普通的函数
	Base::show()
	Base::show(int)
	4
	8
	class Base *
	class Base
	*/

	/*
	给Base的两个show方法定义成virtual虚函数了
	Derive::show()
	Base::show(int)
	8
	12
	class Base*
	class Derive
	*/

	return 0;
}
/*
问题一:哪些函数不能实现为虚函数

虚函数依赖:
1.虚函数能产生地址,存储在vftable当中
2.对象必须存在(vfptr -> vftable -> 虚函数地址)

构造函数
1.virtual + 构造函数 NO!
2.构造函数中(调用的任何函数,都是静态绑定的)调用虚函数,也不会发生静态绑定
派生类对象构造过程 1.先调用的是基类的构造函数 2.才调用派生类的构造函数

static静态成员方法 NO! virtual + static
问题二:虚析构函数 析构函数调用的时候,对象是存在的
*/
class Base
{
public:
	Base(int data) :ma(data) 
	{
		cout << "Base()" << endl;
	}
	~Base()
	{
		cout << "~Base()" << endl;
	}
	void show() {
		cout << "call Base::show()" << endl;
	}
protected:
	int ma;
};

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


int main()
{
	Base *pb = new Derive(10);
	pb->show();
	delete pb; //派生类的析构函数没有被调用到
	/*
	pb->Base Base::~Base 对于析构函数的调用,就是静态绑定
	*/
	return 0;
}
class Base
{
public:
	Base(int data) :ma(data) 
	{
		cout << "Base()" << endl;
	}
	//虚析构函数
	virtual ~Base()
	{
		cout << "~Base()" << endl;
	}
	virtual void show() {
		cout << "call Base::show()" << endl;
	}
protected:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int data)
		: Base(data), mb(data) 
	{
		cout << "Derive()" << endl;
	}
	//基类的析构函数是virtual虚函数,那么派生类的析构函数自动成为虚函数
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;
};


int main()
{
	Base *pb = new Derive(10);
	pb->show();
	delete pb; 
	/*
	pb->Base virtual Base::~Base 对于析构函数的调用,就是动态绑定
	*/
	return 0;
}
/*
问题一:哪些函数不能实现为虚函数

虚函数依赖:
1.虚函数能产生地址,存储在vftable当中
2.对象必须存在(vfptr -> vftable -> 虚函数地址)

构造函数
1.virtual + 构造函数 NO!
2.构造函数中(调用的任何函数,都是静态绑定的)调用虚函数,也不会发生静态绑定
派生类对象构造过程 1.先调用的是基类的构造函数 2.才调用派生类的构造函数

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

Derive d(50);
Base *pb = &d;
栈上的对象会自动正确释放内存
*/
class Base
{
public:
	Base(int data) :ma(data) 
	{
		cout << "Base()" << endl;
	}
	//虚析构函数
	virtual ~Base()
	{
		cout << "~Base()" << endl;
	}
	virtual void show() {
		cout << "call Base::show()" << endl;
	}
protected:
	int ma;
};

class Derive : public Base
{
public:
	Derive(int data)
		: Base(data), mb(data) 
	{
		cout << "Derive()" << endl;
	}
	//基类的析构函数是virtual虚函数,那么派生类的析构函数自动成为虚函数
	~Derive()
	{
		cout << "~Derive()" << endl;
	}
private:
	int mb;
};


int main()
{
	Base *pb = new Derive(10);
	pb->show();
	delete pb; 

	Derive d(50);
	Base *pb = &d;

	//delete pb;
	/*
	pb->Base virtual Base::~Base 对于析构函数的调用,就是动态绑定
	*/
	return 0;
}
/*
虚函数和动态绑定	问题:是不是虚函数的调用一定就是动态绑定?肯定不是
在类的构造函数当中,调用虚函数,也是静态绑定(构造函数中调用其他函数(虚),不会发生动态绑定)
如果不是通过指针或者引用变量来调用虚函数,那就是静态绑定
*/
class Base
{
public:
	Base(int data = 0) :ma(data) {}
	virtual void show() { cout << "Base::show()" << endl; }
protected:
	int ma;
};

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

int main()
{
	Base b;
	Derive d;

	//静态绑定 用对象本身调用虚函数,是静态绑定
	b.show(); //虚函数 call Base::show
	d.show(); //虚函数 call Derive::show

	//动态绑定(必须由指针调用虚函数)
	Base *pb1 = &b;
	pb1->show();
	Base *pb2 = &d;
	pb2->show();

	//动态绑定(必须由引用变量调用虚函数)
	Base &rb1 = b;
	rb1.show();
	Base &rb2 = d;
	rb2.show();

	//动态绑定(虚函数通过指针或引用变量调用,才发生动态绑定)
	Derive *pd1 = &d;
	pd1->show();
	Derive &rd1 = d;
	rd1.show();

	//Derive *pd2 = &b; //不行
	return 0;
}
/*
抽象类和普通类有什么区别?
一般把什么类设计成抽象类?
//动物的基类 泛指 类->抽象一个实体的类型

定义Animal的初衷,并不是让Animal抽象某个实体的类型
1.string _name; 让所有的动物实体类通过继承Animal直接复用该属性
2.给所有的派生类保留统一的覆盖/重写接口

拥有纯虚函数的类,叫做抽象类
抽象类不能再实例化对象了,但是可以定义指针和引用变量

*/

//汽车的基类
class Car
{
public:
	Car(string name, double oil) 
		: _name(name)
		, _oil(oil) 
	{

	}
	//获取汽车剩余油量还能跑的公里数
	double getLeftMiles()
	{
		return _oil * getMilesPerGallon();
	}
	string getName() const { return _name; }
protected:
	string _name;
	double _oil;
	virtual double getMilesPerGallon() = 0; //纯虚函数
};

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

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

class BMW : public Car
{
public:
	BMW(string name, double oil) : Car(name, oil) {}
	double getMilesPerGallon() { return 19.0; }
};

//给外部提供一个统一的获取汽车剩余路程数的API
void showCarLeftMiles(Car &car)
{
	cout <<  car.getName() << ":" << car.getLeftMiles() << endl;
}

int main()
{
	Bnze b1("奔驰", 20.0);
	Audi a("奥迪", 20.0);
	BMW b2("宝马", 20.0);

	showCarLeftMiles(b1);
	showCarLeftMiles(a);
	showCarLeftMiles(b2);

	return 0;
}
/*
如何解释多态?
静态(编译时期)的多态:函数重载、模板(函数模板和类模板)
bool compare(int, int){}
bool compare(double, double){}

compare(10, 20); call compare_int_int 在编译阶段就确定好调用的函数版本
compare(10.5, 20.5); call compare_double_double 在编译阶段就确定好调用的函数版本

template<typename T>
bool compare(T a, T b) {}

compare<int>(10, 20); => int实例化一个 compare<int>
compare(10.5, 20.5); => double实例化一个compare<double>

动态(运行时期)的多态:
在继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名覆盖方法(虚函数)
,基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名覆盖方法,称为多态

多态底层是通过动态绑定来实现的,pbase-》访问谁的vfptr-》继续访问谁的vftable->
									->当然调用的是对应的派生类对象的方法了

继承的好处是什么?
1.可以做代码的复用
2.在基类中给所有派生类提供统一的虚函数接口,让派生类进行重写,然后就可以使用多态了

*/
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高二的笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值