目录
一、多态的概念
1、问题的引出
基类的指针指向派生类对象,在访问函数(基类与派生类的同名函数)时,只能调用基类的函数。
如何才能通过基类指针调用派生类函数?
#include <iostream>
using namespace std;
class Parent
{
public:
void show()
{
cout<<"this is Parent"<<endl;
}
};
class Child:public Parent
{
public:
void show()
{
cout<<"this is child"<<endl;
}
};
int main(int argc, char const *argv[])
{
Child c;
Parent p;
p = c;
Parent *p1 = new Child; //基类指针指向派生类对象
p1->show(); //静态联编,编译器根据p1的类型(parent *)调用Parent里面的show函数
return 0; //却不能按我们期望的那样,调用派生类函数
}
2、多态的概念
在基类指针指向基类对象的时候,按照基类的方法做事,在指向派生类对象的时候,就按照派生类对象的方法做事,换句话说,基类指针可以根据指向的不同,既可以按照基类的方法做事 也可以按照派生类方式做事,它有多种形态,或者多种表现方式,这种现象称之为多态。
多态产生的条件:
1.要有继承
2.要有虚函数重写(发生在不同的作用域中,函数原型相同)
3.基类指针指向派生类对象
#include <iostream>
using namespace std;
class Parent
{
public:
virtual void show() //被virtual修饰的函数称为虚函数
{
cout<<"this is Parent"<<endl;
}
};
class Child:public Parent //1、要有继承
{
public:
virtual void show() //子类中的virtual可以写,也可以不写 2.虚函数重写
{
cout<<"this is child"<<endl;
}
};
int main(int argc, char const *argv[])
{
Parent *p1 = new Child; //3.基类指针指向派生类对象
p1->show(); //动态联编:运行的时候才知道p1指向什么对象
delete p1;
p1 = new Parent;
p1->show();
/*Child c;
Parent *p2 = &c;
p2->show();*/
return 0;
}
3、多态的原理
#include <iostream>
using namespace std;
class Parent
{
public:
int a;
virtual void show() //被virtual修饰的函数称为虚函数
{
cout<<"this is Parent"<<endl;
}
};
class Child:public Parent //1、要有继承
{
public:
virtual void show() //子类中的virtual可以写,也可以不写 2.虚函数重写
{
cout<<"this is child"<<endl;
}
};
int main(int argc, char const *argv[])
{
Parent p;
Child c;
cout<<sizeof(c)<<endl;
cout<<sizeof(p)<<endl;
cout<<"Parent的起始地址:"<<&p<<endl;
cout<<"a的起始地址:"<<&p.a<<endl;
Parent *p1 = new Child;
p1->show(); //通过指针找到对象,通过对下个前8个字节找到虚函数表,
//通过虚函数表来找对应的函数(效率低),不要将所有的函数都声明为虚函数
return 0;
}
4、虚析构函数
根据析构的规则,只能从当前基类往上调用析构函数,并不能调用子类的析构函数,通过将基类中的析构函数声明为虚析构,使得其能按照正确的顺序进行动态的释放对象。
#include <iostream>
using namespace std;
class Parent
{
public:
int a;
Parent()
{
cout<<" Parent的构造函数"<<endl;
}
virtual void show() //被virtual修饰的函数称为虚函数
{
cout<<"this is Parent"<<endl;
}
virtual ~Parent() //虚析构函数,为了使得其能正确的析构
{
cout<<" Parent的析构函数"<<endl;
}
};
class Child:public Parent //1、要有继承
{
public:
Child()
{
cout<<"child的构造函数"<<endl;
}
virtual void show() //子类中的virtual可以写,也可以不写 2.虚函数重写
{
cout<<"this is child"<<endl;
}
~Child()
{
cout<<"child的析构函数"<<endl;
}
};
int main(int argc, char const *argv[])
{
Parent *p = new Child;
p->show();
delete p;
return 0;
}
二、动态类型识别
1、问题的引出
(1)新的关键词:dynamic_cast
(2)dynamic_cast用于基类和派生类之间的转换
(3)dynamic_cast要求使用的目标类型是多态的
所以说: 要求至少有一个虚函数
用于指针转换时,转换失败返回NULL指针
用于引用转换时,转换失败引发bad_cast异常
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
enum {ID = 0};
virtual int GetID()
{
return ID;
}
};
class Child:public Parent
{
public:
enum {ID = 1};
virtual int GetID()
{
return ID;
}
};
void f(Parent *p)
{
if(p->GetID() == Child::ID) //如果成立,说明指向派生类对象,没有任何转化过程
{
Child *c = (Child *)p; //进一步将p指针转化为c型指针
cout<<"可以转换"<<endl;
}
else
{
cout<<"不能转换"<<endl;
}
}
int main(int argc, char const *argv[])
{
//Parent *p = new Parent;
Parent *p = new Child;
f(p);
return 0;
}
2、dynamic_cast关键字用法
该关键字主要用于基类与派生类之间的类型转换。
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
virtual void show(){};
};
class Child:public Parent
{
public:
enum {ID = 1};
virtual void show(){};
};
void f(Parent *p)
{
Child *c = dynamic_cast<Child *>(p); //可用dynamic_cast直接转换类型
if(NULL == c) //转换失败,会返回NULL
{
cout<<"转换失败"<<endl;
}
else
{
cout<<"转换成功"<<endl;
}
}
int main(int argc, char const *argv[])
{
//Parent *p = new Child; //可以转换
Parent *p = new Parent; //不能转换
f(p);
return 0;
}
3、typeid关键字
typeid运算符用来获取一个表达式的类型信息
typeid的操作对象可以是表达式,也可以是数据类型
typeid(expression) 或者 typeid(datatype)
#include <typeinfo>
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
virtual void show(){};
};
class Child:public Parent
{
public:
enum {ID = 1};
virtual void show(){};
};
void f(Parent *p)
{
if(typeid(*p) == typeid(Child)) //说明指针p指向Child。可以进行类型转换
{
cout<<"转换成功"<<endl;
Child *c = (Child *)p;
}
else if(typeid(*p) == typeid(Parent))
{
cout<<"转换失败"<<endl;
}
}
int main(int argc, char const *argv[])
{
int a;
char ch;
Parent p1;
Child c1;
const type_info &pa = typeid(a); //将typeid获取的类型信息保存到一个type_info类型的对象
const type_info &pb = typeid(ch); //中
const type_info &pc = typeid(p1);
const type_info &pd = typeid(c1);
cout<<pa.name()<<endl; //name()是type_info的一个成员函数,表示返回类型的名称
cout<<pb.name()<<endl;
cout<<pc.name()<<endl;
cout<<pd.name()<<endl;
//Parent *p = new Parent;
Parent *p = new Child;
f(p);
return 0;
}
三、纯虚函数
格式:virtual 返回值类型 函数名 (函数参数) = 0;
含有纯虚函数的类称为抽象类,抽象类不能定义实例对象。
#include <iostream>
using namespace std;
class Parent
{
private:
int a;
public:
virtual void show() = 0; //纯虚函数,没有函数体
};
class Child:public Parent
{
public:
virtual void show()
{
cout<<"this is child!"<<endl;
}
};
int main(int argc, char const *argv[])
{
//Parent *p = new Parent; //含有纯虚函数的类称为抽象类,抽象类不能定义实例对象
Parent *p = new Child;
p->show();
return 0;
}