继承和多态1

类的种类
1.组合类,一个类是另一个类的组成部分
2.代理类,一个类的接口是另一个类接口的子集
3.继承类,一个类是另一个类的一种

对于类中成员的访问能力进行了测试

//继承:就是将父类中除了private成员,其他的都那一份到子类中进行使用
//继承过程之后子类中父类成员的权限不可能大于父类中的成员
//继承之后,子类中父类成员的权限不可能大于继承权限
class A
{
public:
	A(int a)
	{
		_a = a;
	}
	int _a;
	void funa()
	{
		funb();
		cout << "funa" << endl;
	}
protected://子类和自身可以访问
	int _b;
	void funb()
	{
		cout << "funb" << endl;
	}

private://自身可以访问
	int _c;
	void func()
	{
		cout << "func" << endl;
	}
};
//构造子类的时候,父类对象一定会在初始化列表中进行构造
//如果父类没有默认构造,就必须自己手动在初始化列表中加上父类的构造
class B :public A//继承权限  不加继承权限默认是私有继承
{
public:
	B(int a,int b):A(a)
	{
		_b = b;
	}
	int _b;
	void fun()
	{
		funa();
		funb();

		//func();//父类的私有成员,子类不可以访问
	}
};
/*
类中的编译顺序:先编译类名,在编译成员名,在编译成员方法体

组合类的构造/析构顺序:先构造成员对象,在构造自身对象
					 :先析构自身对象,在析构成员对象

继承类的构造/析构顺序:先构造父类对象,在构造自身对象
					:先析构自身对象,在析构父类对象

*/
int main()
{
	A _a(1);
	_a.funa();
	//_a.funb();//protected 成员 外界不可访问
	//_a.func();//私有成员

	B _b(12,34);
	_b.funa();
	//_b.funb();子类继承父类的protected权限成员,继承下来后,权限不可能比protected更放开

	return 0;
}

当父类和子类的成员函数名字相同时,涉及同名覆盖问题,请看以下代码

class A
{
public:
	void funa()
	{
		cout << "A::funa()" << endl;
	}
	void funa(int a)
	{
		cout << "A:: funa(int a)" << endl;
	}
};

class B :public A
{
public:
	void funb()
	{
		funa();
		cout << "B::funb()" << endl;
	}
	void funa()
	{
		cout << "B::funa()" << endl;
	}
};
/*
子类中的成员,会隐藏父类中同名的成员---同名隐藏
如果想要访问被子类隐藏的父类成员,只需要加上父类作用域即可
*/
int main()
{
	A a;
	a.funa();

	B b;
	b.funb();
	b.A::funa();//访问被子类同名隐藏的父类成员

	return 0;
}

多态
静多态—编译时期的多态
函数重载,模板

动多态—运行时期的多态
继承中的多态

虚函数
使用virtual关键字修饰的普通成员方法
子类继承父类时候,如果子类中的成员方法对应父类中相同(同函数名,同参数列表的)的方法是虚函数
则子类中对应的成员方法也是虚函数

vfptr(虚表指针)----vftable(虚表)存储虚函数的指针

动多态的实现要素
1.调用的必须是虚函数
2.必须是指针或者引用调用
3.对象必须完整–构造函数调用完成 && 析构函数没有开始调用

动多态产生原理:
1.类中的虚函数指针会存在vftable中
2.继承过程中,子类中相同的成员方法会覆盖vftable中已有的虚函数指针

子类中的成员方法会在vftable中覆盖父类相同的虚方法

动多态调用过程:
当时用指针调用虚方法时候
1.判断为虚方法
2.通过vfptr找到vftable
3.在vftable中找到对应的成员方法指针
4.调用那个找到的成员方法指针

class A
{
public:
	int* _a;
	A()
	{
		this->fun();//没有产生动多态  对象必须完整
		cout << "A()" << endl;
		_a = new int();
	}
	virtual void fun()//虚函数
	{
		cout << "A::fun()" << endl;
	}
	void fun(int a)
	{
		cout << "A::fun(int a)" << endl;
	}
	virtual ~A()
	{
		cout << "~A()" << endl;
		delete _a;
	}
};
//父子类的成员方法之间存在两种关系:同名隐藏,相同虚函数覆盖
class B :public A
{
public:
	B()
	{
		cout << "B(int a,int b)" << endl;
		_b = new int();
	}
	int* _b;
	void fun()
	{
		cout << "B::fun()" << endl;
	}
	void fun(int b)
	{
		cout << "B::fun(int b)" << endl;
	}
	~B()
	{
		cout << "~B()" << endl;
	}
};


int main()
{
	A a;
	a.fun();

	B b;
	b.fun();


	A* p = new A();
	p->fun();//A::fun()

	//父类指针指向子类对象
	A* p1 = new B();
	p1->fun();//当未写虚函数时,调用A::fun(),当写入虚函数时调用B::fun()
	//简单根据类型去调用
	delete p1;//当A的析构函数不加virtual时,打印结果为 A() B(int a,int b) B::fun() ~A
	//当A的析构函数加上virtual时,打印结果为 A() B(int a,int b) B::fun()  ~B()~A()
	//先析构子类对象,在析构父类对象
	return 0;
}

面试题:什么情况下析构函数必须写成虚函数?
存在父类指针指向堆上的子类对象时候,必须将父类的析构函数写成虚函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值