C++总结(6)————C++中的多态

这些知识点很多前辈都总结的非常好了,我汇总起来便于查阅。
参考了https://lyy-0217.blog.csdn.net/article/details/79428643很多内容,表示感谢

1.认识多态

多态是指同样的消息被不同类型的对象接受时导致不同的行为,所谓的消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数。在程序设计中多态性使用最简单的例子就是运算符,比如“+”,就可以实现整数之间,浮点数之间,双精度浮点数之间以及它们相互的加法运算。同样的消息–相加,被不同类型的对象-变量接受后,不同类型的变量采用不同的方式进行加法运算。如果是不同类型的变量相加,例如浮点数和整数,则先要将浮点数转化成整数,然后再进行加法运算。
面向对象的多态性可以分为4类,重载多态、强制多态、包含多态和参数多态。
重载多态–普通函数及类的成员函数的重载,运算符重载等。
强制多态–比如浮点数和整数相加,首先进行类型强制转化,把整数转化成浮点数再相加。
包含多态–定义于不同类中的同名函数的多态行为,主要通过虚函数来实现。
参数多态–和类模板相关联。

2.类型兼容原则

在深入了解多态前,先认识下类型兼容原则。
类型兼容原则是指在需要基类对象的地方,都可以用公有派生类来替代。在替代之后,派生类对象就可以作为基类的对象来使用,但只能使用从基类继承得成员。
类型兼容规则的引入,对于基类及其公有派生类的对象,我们可以使用相同的函数同一进行处理(因为当函数的形参为基类的对象时,实参可以是派生类的对象),而没有必要为每一个类设计单独的模块,大大提高程序的效率。
兼容包括以下情况:

  1. 子类对象可以直接赋值给父类对象
  2. 子类对象可以直接初始化父类对象
  3. 父类指针可以直接指向子类对象
  4. 父类引用可以直接引用子类对象

下面是一个例子:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
    public:
    int mi;
    void add(int i)
    {
        mi += i;
    }
    void add(int a, int b)
    {
        mi += (a + b);
    }
};

class Child : public Parent
  {
    public:
    int mv;
    void add(int x, int y, int z)
       {
          mv += (x + y + z);
       }
  };

int main()
{
    Parent p;   //父类
    Child c;    //子类
    c.mv=0;  //不初始化下面p=c会出错
	c.mi=0;
    p = c;     //子类对父类赋值
    Parent p1(c);  //用子类来初始化父类
    Parent& rp = c;  //父类引用直接引用子类
    Parent* pp = &c; //父类指针直接指向子类

    rp.mi = 100;
    rp.add(5);             // 只能使用从父类继承的成员
    rp.add(10, 10);        //只能使用从父类继承的成员

    /* 为什么编译不过? */
    // pp->mv = 1000;      // pp是父类指针,指向子类c,那么c对象就退化为父类对象,而mv是子类对象,不能使用
    // pp->add(1, 10, 100); // pp只能访问父类对象,并且可以访问父类与子类同名的对象
                            // 而不用指定作用域。
    return 0;
}

当使用父类指针(引用)指向子类对象时:

  1. 子类对象退化为父类对象
  2. 只能访问父类中定义的成员
  3. 可以直接访问被子类覆盖的同名成员而不用指定作用域
3.函数的重写

(1)子类中可以重定义父类中已经存在的成员函数。
(2)这种重定义发生在继承中,叫做函数重写。
(3)函数重写是函数同名覆盖(隐藏)的特殊情况。
那么当函数重写遇上赋值兼容会发生什么?
先来看一个简单的例子:

#include <iostream>
#include <string>

using namespace std;

class Parent
{
    public:
    void print()
    {
        cout << "I'm Parent." << endl;
    }
};

class Child : public Parent
{
    public:
    void print()
    {
        cout << "I'm Child." << endl;
    }
};

void how_to_print(Parent* p)
{
    p->print();
}

int main()
{
    Parent p;
    Child c;

    how_to_print(&p);    // Expected to print: I'm Parent.
    how_to_print(&c);    // Expected to print: I'm Child.

    return 0;
}

运行结果为:
I’m Parent.
I’m Parent.

很显然,这个结果并不是我们想要的,我们想要的是执行 how_to_print(&c); 后打印I’m Child.
如果要解决上面的问题,一种方法是 可以通过作用域分辨符(::)来访问父类中的函数

Child c;
Parent* p=&c;

c.Parent::print();
c.print();

另一种方法是使用多态,使我们可以根据实际的对象类型调用具体的成员重写函数。
当父类指针(引用)指向
(1)父类对象,则调用父类中定义的函数
(2)子类对象,则调用子类中定义的重写函数
C++语言直接支持多态的概念,可以通过使用virtual关键字对多态进行支持,被virtual声明的函数被重写后具有多态性,被virtual声明的函数叫做虚函数.
修改上一个程序:

#include <iostream>
#include <string>

using namespace std;

class Parent
{

    virtual void print()
    {
        cout << "I'm Parent." << endl;
    }

};

class Child : public Parent
{
public:
    void print()
    {
        cout << "I'm Child." << endl;
    }
};

void how_to_print(Parent* p)
{
    p->print();
}

int main()
{   
    Parent p;
    Child c;

    how_to_print(&p);
    how_to_print(&c);


    return 0;
}

运行结果:
I’m Parent.
I’m Child.

父类包含了虚函数,派生类中的重写函数即使没有virtual关键字修饰,派生类的派生类的重写函数也是虚函数.
例子:

#include "stdafx.h"
#include<iostream>
#include<stdio.h>

class father
{
  public:
      virtual void print()
                {
                   printf("I'm father");
                 }
};

class son:public father
{
  public:
      void print()
              {
                printf("I'm son");

              }
};

class grandson:public son
{
   public:  
   void print()
           {
             printf("I'm grandson");
           }
};

int _tmain(int argc, _TCHAR* argv[])
{
	son * s;//派生类的指针
    grandson gs;//派生类的派生类对象
    s=&gs;//派生类的指针指向其子类的地址
    s->print();//函数调用

	system("pause");
	return 0;
}

在这里插入图片描述
结果调用了派生类的派生类函数

在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数
纯虚函数是一种特殊的虚函数,它的一般格式如下:
class <类名>
{
virtual <类型><函数名>(<参数表>)=0;

};

在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。除非在派生类中完全实现基类中所有的的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。
一般而言纯虚函数的函数体是缺省的,但是也可以给出纯虚函数的函数体(此时纯虚函数变为虚函数),这一点经常被人们忽视,调用纯虚函数的方法为baseclass::virtual function.

4、多态在理论中的概念与意义

理论中的概念:

  1. 静态联编
    *在函数编译期间就能确定具体的函数调用
    -如.函数重载
  2. 动态联编
    *在程序实际运行后才能确定函数的具体调用
    -如.函数重写
    给个例子说明:
#include <iostream>
#include <string>

using namespace std;

class Parent
{
public:
    virtual void func()  //虚函数,还存在重载
    {
        cout << "void func()" << endl;
    }

    virtual void func(int i)
    {
        cout << "void func(int i) : " << i << endl;
    }

    virtual void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << "(" << i << ", " << j << ")" << endl;
    }
};

class Child : public Parent
{
public:
    void func(int i, int j)
    {
        cout << "void func(int i, int j) : " << i + j << endl;
    }

    void func(int i, int j, int k)
    {
        cout << "void func(int i, int j, int k) : " << i + j + k << endl;
    }
};

void run(Parent* p)
{
    p->func(1, 2);     // 展现多态的特性
                       // 动态联编
}


int main()
{
    Parent p;

    p.func();         // 静态联编
    p.func(1);        // 静态联编
    p.func(1, 2);     // 静态联编

    cout << endl;

    Child c;

    c.func(1, 2);     // 静态联编

    cout << endl;

    run(&p); 
    run(&c);//虚函数,调用了子类的函数

    return 0;
}

上述程序的运行结果为:
void func()
void func(int i) : 1
void func(int i, int j) : (1, 2)

void func(int i, int j) : 3

void func(int i, int j) : (1, 2)
void func(int i, int j) : 3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值