C++之虚函数和多态

干货较多-需要自己深思理解:

C++支持两种多态性:
1.编译时多态性(静态绑定-早绑定)
在程序编译阶段即可以确定下来的多态性
通过使用 重载机制(重载函数)实现

(模板)http://blog.csdn.net/my_business/article/details/12194691

2.运行时多态性(动态绑定-晚绑定)
必须等到程序运行时才能确定的多态性
要通过 虚函数 来实现

http://blog.csdn.net/zp752963831/article/details/46635885

第一个父类中 把某个函数 定义成虚函数 ,那么在他的子类中 无论是否加 virtual 都是 虚函数。

例子:

class Base
{
public:
virtual void fun(int i)
{
OutputDebugString(_T("Base::fun(int i)"));
}
};

class BaseA :public Base
{
public:
virtual void fun(int i)
{
OutputDebugString(_T("BaseA::fun(int i)"));
}
};

其中BaseB 的fun函数 加与不加virtual函数都无所谓,其都是虚函数

但是如果他继续作为基类,并且要用到多态,则其应该加一个virtual

虚函数的访问权限:
派生类中所定义的虚函数的访问权限 不会影响对它的晚绑定
(即:只有在第一个父类中定义的public,private或protect才对 虚函数的访问 有控制功能)

晚绑定 和 实例化对象 的 class 中函数的访问权限有关

class base
{
public:
    base():strBase(_T("base"))
    {
        log();
    }
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("base"), _T("base"), MB_OK);
    }
public:
    param strBase;
    param* pStr;
};

class child : public base
{
public:
    child()
    {

    }
    /*child(const child& other)
    {
        pStr = new param(_T("child2"));
    }*/pritave:
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("child"), _T("child"), MB_OK);
    }
public:

};int main(){  base * pBase = new child();  pBase->log();//ok  child* pChild = static_cast<child*> pBase;  pChild->log();//error }

在成员函数中调用虚函数:
在一个基类或派生类的虚或非虚成员函数中可以直接调
用该类体系的虚函数或非虚函数

class base
{
public:
    base()
    {
        log();
    }
        void go()
    {
        log();
    }
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("base"), _T("base"), MB_OK);
    }
};

class child : public base
{
public:
    child()
    {

    }
    void go()
    {
        log();
    }
private:
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("child"), _T("child"), MB_OK);
    }

public:

};
int main()
{
        base * pBase = new child();
    pBase->go();//ok  调用父类的go,内部调用子类的log函数。
    child* pChild = static_cast<child*>(pBase);
    pChild->go(); //go 内部直接调用自己的
}        

在构造/析构函数中调用虚函数:
在构造函数或析构函数中调用虚函数时,采用的是早绑定。
也就是虚函数机制在构造函数中不工作。

class base
{
public:
    base()
    {
        log();
    }
    void go()
    {
        log();
    }
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("base"), _T("base"), MB_OK);
    }
};

class child : public base
{
public:
    child()
    {
        log();
    }
    void go()
    {
        log();
    }
private:
    virtual void log()
    {
        MessageBox(::GetTopWindow(NULL), _T("child"), _T("child"), MB_OK);
    }
};
int main()
{
        base * pBase = new child();//构造父类时,父类构造调用自己的            //    log    ,子类构造,则用子类的log函数。
}                    

虚函数和缺省实参:
运行的函数是派生来的函数,但使用的缺省实参是父类里的缺省实参
即:虚函数的缺省实参不支持晚绑定。

class base
{
public:
    base()
    {
    }
    virtual void log(int paramI = 1)
    {
        SStringT str = paramI;
        OutputDebugString(str);
    }
};

class child : public base
{
public:
    child()
    {
    }
    virtual void log(int paramI = 2)
    {
        SStringT str = paramI;
        OutputDebugString(str);
    }
};
int main()
{
        base * pBase = new child();
    pBase->log();//  输出 1
    child* pChild = static_cast<child*>(pBase);
    pChild->log();        //输出2
}    

纯虚函数:
定义:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。
在基类中实现纯虚函数的方法是在函数原型后加“ =0”:virtual void funtion1()=0
*引入原因:
1)为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
2)在很多情况下,基类本身生成对象是不合情理的。

3)实现接口

引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要
求在派生类中必须予以重载以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。

1  

覆盖:
在c++中,派生类继承了基类的全部特征,但有时为了更加准确地描述客观世界的对象,需要对从基类继承来的接口函数进行修改:
这可以通过在派生类中定义一个与基类同名,而返回值,参数类型,顺序或个数都可同可不同的函数,
这样,通过派生类对象调用该同类型的函数时,调用的是派生类中定义的函数。也就是说,
派生类修改了从基类继承来的接口行为,也可称为派生类的函数覆盖了基类中的同名函数。

重载:重载类成员函数与覆盖有点类似:相同的方法(函数名)完成不同的行为

覆盖和重载的 区别:
1.重载是与被重载函数同名,而参数不同(类型、顺序或者数量,至少三者之一不同),返回值可同可不同的另一个函数。
2.重载函数必须在同一个类中或不属于类成员的普通函数之间,不能分布在基类与派生类中。
3.覆盖是,在基类与派生类中出现的标示符同名的情况。

注:
1.虚函数是一组特殊的覆盖函数,同组虚函数间要求函数名、返回值、参数类型、数量、顺序都相同。
通过对象名调用虚函数时,与调用覆盖函数的规则一样-----采用早绑定。
通过对象的指针或引用调用虚函数时采用晚绑定,调用的是指针实际指向,或实际引用的对象对应的类中定义的虚函数

2.如果类外的两个同名函数,或同类中两个同名的成员函数参数相同,但返回值不同,编译器不认为是重载,而认为它们具有二义性,会给出错误提示。
3.如果一组虚函数中,两个虚函数仅返回值不同,但其参数和名字都相同,编译器也认为出错。

三者比较表格:

*=======================================================================================*
|函数名 |返回值 |参数 |绑定时间 |适用范围 |
*---------------+---------------+---------------+-----------------------+---------------*
|虚函数 |同 |同 |晚绑定(指针/引用调用)|基类与派生 |
| | | |早绑定(对象名调用) |类之间 |
*---------------+---------------+---------------+-----------------------+---------------*
|重载函数 |可同可不同 |必须不同 |早绑定 |类外函数或同 |
| | | | |一类中的函数 |
*---------------+---------------+---------------+-----------------------+---------------*
|覆盖函数 |可同可不同 |可同可不同 |早绑定(支配规则) |基类与派生类 |
| | | | |之间 |
*=======================================================================================*

时间: 2024-10-20 01:52:40

C++之虚函数和多态的相关文章

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数、抽象类、虚析构函数、动态创建对象

C++ Primer 学习笔记_34_面向对象编程(5)--虚函数与多态(二):纯虚函数.抽象类.虚析构函数.动态创建对象 一.纯虚函数 1.虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 2.如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 3.在基类中不能给出有意义的虚函数定义,这时可以把它声明成纯虚函数,把它的定义留给派生类来做 4.定义纯虚函数: class <类名> { virtual <类型> <函

C++ Primer 学习笔记33_面向对象编程(4)--虚函数与多态(一):多态、派生类重定义、虚函数的访问、 . 和-&gt;的区别、虚析构函数、object slicing与虚函数

C++ Primer学习笔记33_面向对象编程(4)--虚函数与多态(一):多态.派生类重定义.虚函数的访问. . 和->的区别.虚析构函数.object slicing与虚函数 一.多态 多态可以简单地概括为"一个接口,多种方法",前面讲过的重载就是一种简单的多态,一个函数名(调用接口)对应着几个不同的函数原型(方法). 更通俗的说,多态行是指同一个操作作用于不同的对象就会产生不同的响应.或者说,多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态行分

C++中虚函数和多态

1.C++中的虚函数 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态",这是一种泛型技术.所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法.比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议. 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Ta

你好,C++(37)上车的人请买票!6.3.3 用虚函数实现多态

6.3.3  用虚函数实现多态 在理解了面向对象的继承机制之后,我们知道了在大多数情况下派生类是基类的“一种”,就像“学生”是“人”类中的一种一样.既然“学生”是“人”的一种,那么在使用“人”这个概念的时候,这个“人”可以指的是“学生”,而“学生”也可以应用在“人”的场合.比如可以问“教室里有多少人”,实际上问的是“教室里有多少学生”.这种用基类指代派生类的关系反映到C++中,就是基类指针可以指向派生类的对象,而派生类的对象也可以当成基类对象使用.这样的解释对大家来说是不是很抽象呢?没关系,可以

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr)、C++对象模型

C++ Primer 学习笔记_35_面向对象编程(6)--虚函数与多态(三):虚函数表指针(vptr)及虚基类表指针(bptr).C++对象模型 一.虚函数表指针(vptr)及虚基类表指针(bptr) C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括: virtual function机制:用以支持一个有效率的"执行期绑定": virtual base class:用以实现多次在继承体系中的基类,有一个单一而被共享的实体. 1.虚函数表指针 C++中,有两种数据

c++ 内存分配 虚函数实现多态等

看到了很好的解释,先mark下,有空整理. 虚函数实现多态与多重继承乌索普的回答 很有收获,毕竟我觉得多态才是面向对象的灵魂. 这样的设计也让人得以理解为什么虚函数会像成员变量一样与对象绑定. C++成员函数在内存中的存储方式 原文地址:https://www.cnblogs.com/zsl96/p/8732662.html

&lt;C++&gt; 类(3):初始化列表 常函数和常量对象 虚函数与多态(包括纯虚函数)

一.初始化列表(初始化列表中必须有的两个内容) 1.类中const的成员变量: ①特点:不能修改 必须初始化 在构造函数后面加冒号 格式为:":变量名(值)" 也就是说 常量必须在初始化列表中初始化 ②执行顺序:构造函数先执行初始化列表 然后执行函数中的内容 1 #include<iostream> 2 using namespace std; 3 4 class CPerson 5 { 6 public: 7 const int a; 8 public: 9 CPerso

一道理解虚函数(多态)机制的题目

一道理解虚函数(多态)机制的题目(摘抄) 以下程序输出为 class Base { public: Base(int j): i(j) {} virtual~Base() {} void func1() { i *= 10; func2(); } int getValue() { return i; } protected: virtual void func2() { i++; } protected: int i; }; class Child: public Base { public:

虚函数和多态

虚函数和多态 - 虚函数 在类的定义中,前面有virtual类关键字的成员函数就是虚函数 class base{ virtual int get(); }; int base::get(){} virtual关键字只用在类定义里的函数声明,写函数体时不用 构造函数和静态成员函数不能是虚函数 虚函数可以参与多态,普通函数不能 - 多态的表现形式一 派生类的指针可以赋给基类指针 通过基类指针指向一个基类和派生类中的同名虚函数时 若该指针指向一个基类对象,那么被调用的是基类的虚函数 若该指针指向一个派

c++虚函数与多态实例分析

1 #include <iostream> 2 #include <complex> 3 using namespace std; 4 5 class Base 6 { 7 public: 8 Base() {cout<<"Base-ctor"<<endl;} 9 ~Base() {cout<<"Base-dtor"<<endl;} 10 virtual void f(int){cout<