赋值兼容是指在需要父类对象的任何地方都可以,使用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;
}