(C++)多态

目录

一、多态的概念

1、问题的引出

2、多态的概念

3、多态的原理

4、虚析构函数

二、动态类型识别

1、问题的引出

2、dynamic_cast关键字用法

3、typeid关键字

三、纯虚函数


一、多态的概念

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值