赋值兼容 与 虚函数

赋值兼容是指在需要父类对象的任何地方都可以,使用public子类(private和protected不可以)的对象来替代。赋值兼容是一种默认行为,不需要任何的显示的转化步骤

----赋值兼容规则:(父类:shape  子类:circle)
子类的对象可以赋值给父类对象。                shape s= c;
子类的对象可以初始化父类的引用。            shape &s =c;
子类对象的地址可以赋给指向父类的指针。 shape *s = &c;
在替代之后,就可以作为父类的对象使用,但只能使用从父类继承的成员,也就是说s只能使用circle从shape中继承的成员

----静多态和动多态
精多态:函数重载就是一种静多态 void func() void func(int a),在编译阶段就决定了调用情况,在编译时就可以确定函数地址
动多态:通过继承重写基类的虚函数实现的多态,动多态是在运行时反生的,运行时在虚函数表中寻找调用函数的的地址

 

----动多态形成的条件

1.父类中有虚函数,virtual 函数声明
2.子类中覆写(override)父类的虚函数,最好加上virtual,不加也可以,覆写的函数也是虚函数
3.用已被子类对象地址赋值的父类指针被子类对象赋值的父类引用,调用虚函数,则会产生多态,如果使用被子类对象赋值的父类对象则产生不了多态。

class A
{
public:
	int a = 10;
	virtual void foo()
	{
		cout << "A::foo" << endl;
	}
};

class B :virtual public A
{
public:
	int b = 20;
	virtual void foo() //覆写
	{
		cout << "B::foo" << endl;
	}
};

int main()
{
	B b;
	A a = b; //拷贝构造 发生拷贝时不会把子类的虚函数表拷贝过去,虚函数表不会发生变化
	a.foo(); //未实现多态 虚函数表为父类的虚函数表(不确定这个说法是否正确)
	A a1;
	a1 = b;  //运算符重载
	a1.foo();//未实现多态

	A* p = &b;
	p->foo(); //实现多态

	A& ra = b;
	ra.foo(); //实现多态
}

不能用对象实现多态的原因

当使用子类对象给父类对象赋值的时候,无论是调用拷贝构造还是运算符重载,只会处理成员变量,不会将虚函数指针、虚函数表赋值过来一个类对象里面的vptr永远不会变,永远都会指向所属类型的虚函数表,因此通过调用虚函数时,就没有必要进行动态解析了,白白增加了间接性,浪费性能,编译器直接在编译时就可以确认具体调用哪个函数了,因此没有所谓的多态。而是用引用或者指针本身还是原来的子类对象,所以虚函数指针和虚函数表都是子类的,所以可而已实现多态。

其他文章:
https://www.cnblogs.com/yinheyi/p/10525543.html
https://blog.csdn.net/weixin_43519514/article/details/106474326

----虚函数格式:
class 类名
{
    virtual 函数声明;
}

----虚函数小结:
1,在基类中用 virual 声明成员函数为虚函数。类外实现虚函数时,不必再加 virtual.
2,在派生类中重新定义此函数称为覆写,要求函数名,返值类型,函数参数个数及类型全部匹配。并根据派生类的需要重新定义函数体。
3,当一个成员函数被声明为虚函数后,其子类中完全相同的函数(显示的写出)也为虚函数。 可以在其前加 virtual 以示清晰。
4,定义一个指父类对象的指针,并使其指向其子类的对象,通过该指针调用虚函数,此时调用的就是指针变量指向对象的同名函数。Shape* ps = &c; ps->draw();
5,子类中的覆写的函数,可以为任意访问类型,依子类需求决定
6,父类中的虚函数,如果子类不覆写,仍然是虚函数 孙子类中仍然可以继续覆写

声明型 virtual static friend  声明时加关键字 如果分开实现则不可以加关键字
实现型 const  声明和实现时都需要加关键字

#include "stdafx.h"
#include <iostream>
using namespace std;

//对外提供公共接口 以后增加新的绘图,不希望改变shape
class Shape
{
public:
	Shape(int x, int y)
		:_x(x), _y(y){}

	virtual void draw() //虚函数
	{
		cout << "draw shape start from" <<
			"(" << _x <<"," << _y << ")" << endl;
	}

protected: //继承后可在类内直接访问
	int _x, _y;
};


class Circle :public Shape //实现赋值兼容 必须为public的继承方式
{
public:
	Circle(int x, int y, int r)
		:Shape(x, y), radius(r){}

	virtual void draw()  //覆写后的虚函数
	{
		cout << "draw Circle start from" << "(" << _x << "," << _y << ")"
			<< " radius=" << radius << endl;
	}

private:
	int radius;
};


class Rect :public Shape
{
public:
	Rect(int x, int y, int l, int w)
		:Shape(x, y), _len(l), _wid(w){}

	virtual void draw()
	{
		cout << "draw Rect start from" << "(" << _x << "," << _y << ")"
			<< " len&&wid: " << _len << " " << _wid << endl;
	}

private:
	int _len;
	int _wid;
};



int _tmain(int argc, _TCHAR* argv[])
{
	Circle c(9, 8, 7);
	c.draw();

	Rect r(5, 6, 7, 8);

	//Shape s(1,2);
	//s = c;
	//Shape &rs = c; //赋给引用

	//c = s;  赋值不兼容(父类给子类赋值)

	Shape* ps = &c; //赋值兼容 子类地址赋给父类指针

	ps->draw();
	ps = &r;
	ps->draw();

	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值