多态性与虚函数

1、多态性
多态性在C++中表现为统一形式的函数调用,可能调用不同的函数实现

1.1 编译时的多态性
对于一些函数的调用,如果编译器在编译时就可以确定索要调用函数是哪一个具体实现,这种多态性称为编译时多态性,也成为静态多态性
C++可以通过重载(函数重载或运算符重载)来实现编译时的多态性。

1.2 运行时的多态性
当函数的调用在编译时无法得知所调用的函数是哪一个实现是,需要在运行时才能决定,这种多态性称为动态多态性。

示例1.1

// 多态性与虚函数.cpp : 定义控制台应用程序的入口点。
//

// Data类声明
class Data
{
public:
    Data(int x = 0, int y = 0); // 缺省构造函数

    void set_xy(int x, int y);
    int get_x() const;
    int get_y() const;

    long norm(void);

    ~Data()
    {
    }
    Data& operator += (Data& add) // 重载运算符+=
    {
        m_x += add.m_x;
        m_y += add.m_y;
        return *this;  // 返回当前对象
    }

protected:
    int m_x;
    int m_y;

};

// Data类成员函数定义
Data::Data(int x, int y):m_x(x), m_y(y){} // 构造函数初始对保护成员化

void Data::set_xy(int x, int y)
{
    m_x = x;
    m_y = y;
}

int Data::get_x(void) const
{
    return m_x;
}

int Data::get_y(void) const
{
    return m_y;
}

long Data::norm(void)
{
    return (m_x*m_x+m_y*m_y);
}

// T_Data类声明
class T_Data:public Data
{
public:
    T_Data(int x = 0, int y = 0, int z = 0);

    void set_xyz(int x, int y, int z);

    int get_z(void);

    long norm(void);

    T_Data& operator +=(T_Data& add)
    {
        m_x += add.m_x;
        m_y += add.m_y;
        m_z += add.m_z;

        return *this;
    }

protected:
    int m_z;
};// T_Data类成员函数定义
T_Data::T_Data(int x, int y, int z):Data(x, y), m_z(z){}

void T_Data::set_xyz(int x, int y, int z)
{
    m_x = x;
    m_y = y;
    m_z = z;
}

int T_Data::get_z(void)
{
    return m_z;
}

long T_Data::norm(void)
{
    return m_x*m_x+m_y*m_y+m_z*m_z;
}



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

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
    Data d1(10, 20);
    Data d2;
    T_Data d3(10, 20, 30);
    T_Data d4;

    d2.set_xy(20,40);
    d4.set_xyz(5, 10, 15);

    cout << "d1 = (" << d1.get_x() << "," << d1.get_y() << ")\n";
    cout << "d2 = (" << d2.get_x() << "," << d2.get_y() << ")\n"; 

    d2 += d1; // 定调用Data类重载运算符:+=
    cout << "d2 = (" << d2.get_x() << "," << d2.get_y() << ")\n";

    cout << "d3 = (" << d3.get_x() << "," << d3.get_y() << "," 
         << d3.get_z() << ")\n";

    cout << "d4 = (" << d4.get_x() << "," << d4.get_y() << "," 
        << d4.get_z() << ")\n";

    d4 += d3; // 定调用Data类重载运算符:+=

    cout << "d4 = (" << d4.get_x() << "," << d4.get_y() << "," 
        << d4.get_z() << ")\n";

    cout << "d1's norm is " << d1.norm() << endl;
    cout << "d4's norm is " << d4.norm() << endl;

    return 0;
}

结果:
这里写图片描述

示例1.2

int _tmain(int argc, _TCHAR* argv[])
{
    Data *p;
    T_Data td(10, 20, 30);

    cout << "td = (" << td.get_x() << "," << td.get_y() << "," 
        << td.get_z() << ")\n";

    p = &td; // 用指向基类指针指向派生类对象

    cout << "td's norm is (*p)" << p->norm() << endl; //实际上p是指向基类的Data的norm()
    cout << "td's norm is (td)" << td.norm() << endl;

    return 0;
}

结果:
这里写图片描述

2、虚函数
2.1 表现形式
从表现形式上看是指那些被virtual修饰的成员函数。当一个成员函数在基类中被定义为虚函数,那么只要同名函数出现在派生类中,如果在类型、参数等方面均保持相同,那么即使在派生类中的相同函数前没有关键字virtual,它也被默认看成是一个虚函数。

2.2 虚函数的作用
在示例1.2中,我们可以看到,通过指向派生累的基类指针来调用成员函数norm(),则实际调用的是基类的成员函数norm()。如果通过派生类指针来调用成员函数,则使用派生类对象的成员函数norm(),这是非多态行为。
其实只要在声明基类即Data类的成员函数norm改为虚函数,即

virtual long norm(void);

注意:
1、若虚函数在类外实现,实现时不需要加关键字virtual,否则会报语法错误
2、基类(Data类)声明norm()函数为虚函数后,其派生类的norm()也自动的变为虚函数,因为二者形式一致。

结果:
这里写图片描述

这时p->norm()调用的是我们希望的td对象(T_Data类)的norm()函数而不是基类(Data类)对象的norm()函数。对于p这个基类指针来说,它可以调用同一层次结构不同类对象的虚函数。对同一消息,不同对象(p指向的不同对象)有不同的响应方式,这就是多态性。

2.2.1 虚函数表
如果将一个基类的指针(本来是用来指向基类对象的)指向派生类对象,则进行指针类型转换,将派生类对象的指针转换为基类指针,因此基类指针指向的是派生类对象的基类部分。

其实把类的一个函数声明为虚函数时,编译器就在类结构里加上一个指针,该指针被称为虚指针,它指向的是一个虚函数表(Vtable),该表包含了类中所有虚函数的地址,也包含其基类的虚函数的地址。这样,对于声明了虚函数的类对象,系统在得到对象的指针后会查找虚函数表,找到该对象的虚函数的函数指针后调用该函数,如果此虚函数未被派生类实现那么系统会调用其基类的虚函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值