多态 polymorphism
1、父类中有虚函数
2、子类中覆写(override)父类的虚函数
3、子类对象的地址赋给了父类的指针,并通过该指针用虚函数
#include <iostream>
using namespace std;
class A
{
public:
virtual void func()
cout<<"class A"<<endl;
};
class B
{
private:
void func()
cout<<"class B"<<endl;
};
int main()
{
A *pa = new B; // 输出class B
pa->func();
return 0;
}
赋值兼容只发生在公有派生的父子关系
子类对象赋给父类的对象
子类对象赋给父类的引用
子类对象的地址赋给父类的指针
#include <iostream>
using namespace std;
class Shape
{
public:
Shape(int x, int y)
:_x(x), _y(y)
{}
void draw()
{
cout<<"draw from "<<"("<<_x<<","<<_y<<")"<<endl;
}
protected:
int _x;
int _y;
};
class Circle:public Shape
{
public:
Circle(int x, int y, int r)
:Shape(x, y), _radius(r)
{}
void draw()
{
cout<<"draw from "<<"("<<_x<<","<<_y<<")"<<endl;
cout<<"radius = "<<_radius<<endl;
}
private:
int _radius;
};
int main()
{
Shape s(1, 2);
s.draw();
Circle c(3, 4, 5);
c.draw();
c.func();
s = c;
s.draw();
return 0;
}
静多态:编译阶段决定的
动多态:在运行时决定的
发生多态
1、父类中有虚函数,声明虚函数virtual
2、子类中覆写(override)了父类的虚函数
3、将子类对象地址赋值给父类的指针,并发生虚函数的调用
#include <iostream>
using namespace std;
class Shape
{
public:
Shape(int x, int y)
:_x(x), _y(y)
{}
virtual void draw();
protected:
int _x;
int _y;
};
class Circle:public Shape
{
public:
Circle(int x, int y, int r)
:Shape(x, y), _radius(r)
{}
virtual void draw()
{
cout<<"draw from "<<"("<<_x<<","<<_y<<")"<<endl;
cout<<"radius = "<<_radius<<endl;
}
private:
int _radius;
};
void Shape::draw
{
cout<<"draw from "<<"("<<_x<<","<<_y<<")"<<endl;
}
class Rect:public Shape
{
public:
Rect(int x, int y, int len, int wid)
:Shape(x, y), _len(len), _wid(wid)
{}
virtual void draw()
{
cout<<"start from "<<"("<<x<<","<<y<<")"<<endl;
cout<<" len = "<<_len<<" wid = "<<_wid<<endl;
}
private:
int _len;
int _wid;
};
int main()
{
Circle c(3, 4, 5);
c.draw();
Shape *ps;
//Shape *p = &c;
// p->draw();
// p = new Rect(3, 4, 5, 6);
// p->draw();
int choice;
while(1)
{
scanf("%d", &choice);
switch(choice)
{
case 1:
ps = &c;
ps->draw();
break;
case 2:
ps = &r;
ps->draw();
break;
}
}
return 0;
}
声明虚函数的方法 virtual void func(); 声明型关键字
覆写override
overload 重载:同一作用域,函数名相同,参数列表不同
shadow :发生在父子类中的同名成员
override : 发生在父子类中,父类中函数有virtual声明的函数
子类中,同参数、同名称、同返回函数之间构成覆写
纯虚函数
格式: virtual void draw() = 0;
纯虚函数没有实现体,含有纯虚函数的类,称为抽象基类
抽象基类不能实例化
java 中叫 interface接口,作用是给族类提供接口用的。
设计模式
高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象
高层业务逻辑==抽象层==低层业务逻辑
#include <iostream>
using namespace std;
class IReader
{
public:
virtual void content();
};
class Story:public IReader
{
public:
void content()
{
cout<<"story one"<<endl;
}
};
class Newspaper:public IReader
{
public:
void content()
{
cout<<"newspaper"<<endl;
}
};
class Mother
{
public:
void read_content(IReader * i)
{
i->content();
}
};
int main()
{
Mother mo;
Story s;
Newspaper ns;
mo.read_content(&s);
mo.read_content(&ns);
return 0;
}
虚函数表
C++的多态是通过一张虚函数表(Virtual Table)来实现,简称为V-Table。
表中有虚函数的地址表,解决继承、覆写的问题,保证其真实反映实际的函数
这个表被分配在实例的内存中
静态代码
Base *b = new Derive();
b->f();
首先明确b的类型;然后通过指针虚函数表的指针vptr和偏移量,匹配虚函数的入口;根据入口地址调用虚函数
多态实现了动态绑定,但牺牲了一些空间和效率。
运行时类型信息(RTTI)
run time type identification
typeid dynamic_cast 只用于含有虚函数的父子类转化
Manager *t = dynamic_cast<Manager*>(emp[i]);
int main()
{
B b;
A * pa = &b;
B * pb = dynamic_cast<B*>(pa);
cout<<pb<<endl; // 输出地址
C * pc = dynamic_cast<C*>(pa);
cout<<pc<<endl; // 输出0
return 0;
}
typeid
#include <iostream>
#include <typeinfo>
using namespace std;
typedef void (*PF)(int);
int main()
{
//检查类型信息
cout<<typeid(int).name()<<endl; // i
cout<<typeid(char).name()<<endl; // c
cout<<typeid(double).name()<<endl; // d
cout<<typeid(float).name()<<endl; // f
cout<<typeid(long).name()<<endl; // l
cout<<typeid(PF).name()<<endl; //PFviE
return 0;
}
B、C 均继承A
D是单独的类
int main()
{
B b;
A * pa = &b;
B * pb = static_cast<B*>(pa); // 成功
cout<<pb<<endl;
C * pc = static_cast<C*>(pa); // 成功 不安全
cout<<pc<<endl;
D * pd = static_cast<D*>(pa); // 不成功
cout<<pd<<endl;
B * pb = reinterpret_cast<B*>(pa); // 成功 不安全
cout<<pb<<endl;
C * pc = reinterpret_cast<C*>(pa); // 成功 不安全
cout<<pc<<endl;
D * pd = reinterpret_cast<D*>(pa); // 成功 不安全
cout<<pd<<endl;
return 0;
}