精华内容
下载资源
问答
  • 基类虚函数

    2014-10-09 21:57:25
    基类虚函数基类的概念 在C++语言中,一类不能被次说明为一派生类的直接基类,但可以不止一次地成为间接基类。这就导致了一些问题。为了方便 说明,先介绍继承的“类格”表示法。
  • 题目 定义一车(vehicle)基类,有Run、Stop等成员函数...观察虚函数的作用。 C++代码如下: #include<iostream> using namespace std; class vehicle { public: void Run(){cout<<"Vehicle is runnin

    题目

    定义一个车(vehicle)基类,有Run、Stop等成员函数,由此派生出自行车(bicycle)类、汽车(motorcar)类,从bicycle和motorcar派生出摩托车(motorcycle)类,它们都有Run、Stop等成员函数。观察虚函数的作用。

    C++代码如下:

    #include<iostream>
    using namespace std;
    
    class vehicle
    {
    public:
    	 virtual void Run(){cout<<"Vehicle is running."<<endl;}
    	 virtual void Stop(){cout<<"Vehicle is stopping."<<endl;}
    };
    class bicycle: public vehicle
    {
    public:
    	virtual void Run(){cout<<"Bicycle is running."<<endl;}
    	virtual void Stop(){cout<<"Bicycle is stopping."<<endl;}
    };
    class motorcar: public vehicle
    {
    public:
    	virtual void Run(){cout<<"Motorcar is running."<<endl;}
    	virtual void Stop(){cout<<"Motorcar is stopping."<<endl;}
    };
    class motorcycle:public bicycle,public motorcar
    {
    public:
    	virtual void Run(){cout<<"Motorcycle is running."<<endl;}
    	virtual void Stop(){cout<<"Motorcycle is stopping."<<endl;}
    };
    int main()
    {
    	vehicle v;
    	bicycle b;
    	motorcar mcar;
    	motorcycle mcycle;
    	
    	v.Run();
    	v.Stop();
    	b.Run();
    	b.Stop();
    	mcar.Run();
    	mcar.Stop();
    	mcycle.Run();
    	mcycle.Stop(); 
    	
    	cout<<"指针调用"<<endl;
    	vehicle* p;
    	p=&v;
    	p->Run() ;
    	p->Stop() ; 
    	p=&b;
    	p->Run() ;
    	p->Stop() ; 
    	p=&mcar;
    	p->Run() ;
    	p->Stop() ; 
    
    	return 0;
    }
    

    在这里插入图片描述

    展开全文
  • 本文重点:应该为多态基类声明虚析构器。一旦一类包含虚函数,它就应该包含一虚析构器。如果一类不用作基类或者不需具有态性,便不应该为它声明虚析构器。
  • 在C++继承体系当中,在派生类中可以重写不同基类中的虚函数。下面就是一例子:
  • 继承是一种机制,类通过继承指出它希望共享其虚基类的状态,在继承给定虚基类无论该类在派生层次中作为虚基类出现几次,只继承一共享的基类的对象。共享的虚基类子对象就是虚基类。 例如: class ...

    对于这些虚的,之前有很多歧义和不解,现在进行一下学习总结吧!

    1.多重继承可能会导致二义性,而虚继承可以解决这个问题。虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态,在虚继承下给定虚基类无论该类在派生层次中作为虚基类出现几次,只继承一个共享的基类的对象。共享的虚基类子对象就是虚基类。

    例如:

    class istream:public virtual ios{};

    class ostream:virtual public ios{};

    2.虚函数和纯虚函数的区别

    虚函数是为了重载和多态的需要,在基类中是一定要定义的

    纯虚函数在基类中只能声明,不能实现,而是在派生类中进行实现。

    virtual void foo1();//虚函数

    virtual void foo2()=0;//纯虚函数

    虚函数在类中可以重载也可以补充在,但是纯虚函数必须在子类中实现。

    3.含有纯虚函数的类并不一定是虚基类,虚基类也不一定含有纯虚函数,这两个是没有关系的。

    4.

    1)构造对象的时候必须知道对象实际类型,而虚函数行为是在运行期间确定实际类型的额,而在构造一个对象时,由于对象还未构造成功,编译器无法知道对象的实际类型,是该类本身还是该类的派生类,或是深层次的派生类。

    2)每一个拥有虚成员函数的类都有一个指向虚函数表的指针。对象通过虚函数表里存储的虚函数地址来调用虚函数。那虚函数表指针是构造函数初始化的。当我们通过new来创建一个对象的时候,第一步是申请需要的内存,第二步就是调用构造函数。如果构造函数是虚函数,那必然需要通过vtbl来找到虚构造函数的入口地址,显然,我们申请的内存还没有做任何初始化,不可能有vtbl的。因此,构造函数不能是虚函数。


    展开全文
  • 1.虚函数的引入 2.虚函数作用 3.关于虚函数的几点说明 4.纯虚函数 5.抽象类 6.虚基类 1.虚函数的引入 先看如下程序,程序后有进一步的解释。如果读者对程序不懂请先复习基础知识。 // // VirtualFun.cpp //...

    目录

    1.虚函数的引入

    2.虚函数作用

    3.关于虚函数的几点说明

    4.纯虚函数

    5.抽象类

    6.虚基类


    1.虚函数的引入

    先看如下程序,程序后有进一步的解释。如果读者对程序不懂请先复习基础知识。

    //
    //  VirtualFun.cpp
    //  virtual
    //
    //  Created by 刘一丁 on 2019/8/26.
    //  Copyright © 2019年 LYD. All rights reserved.
    //
    
    
    //file1.cpp
    #include <iostream>
    using namespace std;
    
    class Base                                  //基类 Base
    {
    public:
        Base(int x, int y)
        { a = x; b = y; }
        //virtual void show()                   //采用虚函数解决同名函数调用问题
        void show()                             //基类成员函数 show()
        {
            cout << "base++++++" << endl;
            cout << a << "  " << b << endl;
        }
        
    private:
        int a, b;
    };
    
    class Derived:public Base                    //派生类 Derived
    {
    public:
        Derived(int x, int y, int z):Base(x, y)
        { c = z; }
        void show()                              //派生类成员函数 show()
        {
            cout << "Derived+++++++"  << endl;
            cout << " c= " << c << endl;
        }
        
    private:
        int c;
    };
    
    int main()
    {
        Base mb(60, 60), *pc;                    //定义基类对象及指向基类的指针(简称基类指针)
        Derived mc(10, 20, 30);                  //定义派生类对象
        pc = &mb;                                //基类指针指向基类对象 mb
        pc-> show();
        pc = &mc;                                //基类指针指向派生类对象 mc
        pc-> show();
        
        return 0;
    }
    
    //output:
    //base++++++++
    //60  60
    //base++++++++
    //10  20
    
    

    注:

    C++中规定:基类的对象指针可以指向它公有派生的对象,但是当其指向公有派生类对象时,它只能访问派生类中从基类继承来的成员,而不能访问公有派生类中定义的成员,解决这个问题的方法就是引入虚函数。

    虚函数通常和指针连用,来体现动态的多态性。使用指针的目的是为了表达一种动态的特性,即当指针指向不同对象(基类或派生类对象)时,分别调用不同类的成员函数。

    2.虚函数作用

    在上文中虽然基类指针 pc 已经指向了派生类对象 mc,但是它所调用的成员函数 show() 仍然是其基类对象的 show()。解决方法有三种。

    ①mc.show();

    ②((Derived*)pc)-> show();

    ③将基类中的同名函数声明为虚函数,即在对应成员函数前加上关键字 virtual,并在派生类中被重载。

    虚函数更该方法即是代码中注释的部分,virtual void show();函数输出结果如下:

    base++++++++
    60  60
    Derived+++++
    c = 30

    3.关于虚函数的几点说明

    (1)C++规定,如果在派生类中,没有用 virtual 显式地给出虚函数声明,这是系统就会遵循以下规则来判断一个成员函数是否是虚函数。

    ①该函数与基类的虚函数有相同的名称。

    ②该函数与基类的虚函数有相同的参数个数及相同的对应参数类型。

    ③该函数与基类的虚函数有相同的返回类型或者满足赋值兼容规则的指针、引用型的返回类型。

    (2)通过虚函数来使用多态性机制,派生类必须从他的基类公有派生(赋值兼容规则)。

    (3)虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数,因为虚函数调用哪个要靠特定的对象来决定该激活哪个函数。

    (4)构造函数不能是虚函数,通常把析构函数声明为虚函数(请读者自行百度)。

    4.纯虚函数

    基类往往表示一种抽象的概念,它并不与具体的事物相联系。这时在基类中将某一成员函数定义为虚函数,并不是基类本身的要求,而是考虑到派生类的需要,在基类中预留了一个函数名,具体功能留给派生类根据需要去定义。

    纯虚函数是在声明虚函数时被“初始化”为 0 的函数。例如

    virtual void show() = 0;

    纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对他进行重新定义。纯虚函数没有函数体,它最后面的“ = 0” 并不表示函数的返回值为 0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。纯虚函数不具备函数的功能,不能被调用。

    //
    //  VirtualFun.cpp
    //  Virtual
    //
    //  Created by 刘一丁 on 2019/8/26.
    //  Copyright © 2019年 LYD. All rights reserved.
    //
    
    
    //file1.cpp
    #include <iostream>
    using namespace std;
    
    class Circle
    {
    public:
        void set(int x)
        { r = x; }
        virtual void show() = 0;   //定义纯虚函数
    protected:
        int r;
    };
    
    class Area:public Circle
    {
    public:
        void show()
        { cout << "Area is " << 3.14 * r * r << endl; }
    };
    
    class Perimeter:public Circle
    {
    public:
        void show()
        { cout << "Primeter is " << 2 * 3.14 * r << endl; }
    };
    
    int main()
    {
        Circle *ptr;
        Area ob1;
        Perimeter ob2;
        
        ob1.set(10);
        ob2.set(10);
        
        ptr = &ob1;
        ptr-> show();
        ptr = &ob2;
        ptr-> show();
        
        return 0;
    }
    
    //output:
    //Area is 314
    //Primeter is 62.8
    

    5.抽象类

    如果一个类至少有一个纯虚函数,那么就称该类为抽象类。上述程序中定义的类 circle 就是一个抽象类。

    ①抽象类只能作为其他类的基类来使用,不能建立抽象类的对象。

    ②不允许从具体类派生出抽象类。

    ③抽象类不能用作函数的参数类型、函数的返回类型或显示转换的类型。

    6.虚基类

    虚基类是对多继承来说的。如果一个类有多个直接基类,而这些直接基类又有一个共同的基类,则在最底层的派生类中会保留这个间接的共同基类数据成员的多份同名成员。

    //
    //  VirtualClass.cpp
    //  VirtualClass
    //
    //  Created by 刘一丁 on 2019/8/31.
    //  Copyright © 2019年 LYD. All rights reserved.
    //
    
    
    //file1.cpp
    #include <iostream>
    using namespace std;
    
    class base
    {
    public:
        base()
        {
            a = 5;
            cout << "base a = " << a << endl;
        }
    protected:
        int a;
    };
    
    class base1:public base
    {
    public:
        base1()
        {
            a = a + 10;
            cout << "base1 a = " << a << endl;
        }
    };
    
    class base2:public base
    {
    public:
        base2()
        {
            a = a + 20;
            cout << "base2 a = " << a << endl;
        }
    };
    
    class derived:public base1, public base2
    {
    public:
        derived()
        {
            cout << "base1::a = " << base1::a << endl;  //明确指出调用 base1 的数据成员 a
            cout << "base2::a = " << base2::a << endl;  //明确指出调用 base2 的数据成员 a 
        }
    };
    
    int main()
    {
        derived obj;
        
        return 0;
    }
    
    //output:
    //1.base a = 5
    //2.base1 a = 15
    //3.base a = 5
    //4.base2 a = 25
    //5.base1::a = 15
    //6.base2::a = 25
    
    //注:在访问这些同名成员时,必须在派生类对象名后增加直接基类名
    //使其唯一地标识一个成员,以免产生二义性。

    上述程序中,在定义 derived 对象 obj 时,首先调用 base1 的构造函数,在调用 base2 的构造函数,最后调用 derived 构造函数。在调用 base1 构造函数时又会先调用 base 的构造函数,所以会输出 1-2 语句;在调用 base2 构造函数时又会先调用 base 的构造函数所以输出 3-4 语句。 虽然在类 base1 和 base2 中没有定义数据成员 a ,但是他们在调用基类 base 构造函数时会分别继承基类的数据成员 a。这样就在 base1 和 base2 中同时存在着同名的数据成员 a ,他们都是类 base 成员的拷贝。但是类 base1 和类 base2 中的数据成员 a 分别具有不同的存储单元,可以存放不同的数据(在这里可以这样理解,base1 公有继承了 base ,所以相当于 base1 中也有protected 类型的数据成员 a )。下图显示了其中的层次关系。

    由于在类derived 中同时存在着类 base1 和类base2 的数据成员 a ,因此在 derived 的构造函数中输出 a 的值,必须加上“类名::”,指出是那一个数据成员 a ,否则就会出现二义性。例如

    class derived:public base1, public base2
    {
    public:
        derived()
        { cout << "derived a = " << a << endl; } //错误,二义性。
    };

    要想解决这个问题就需要用到虚基类。即将共同的基类base 声明为虚基类。这就要求从 base 派生新类时,使用关键字 virtual 将类 base 声明为虚基类。声明形式如下:

    class 派生类名:virtual 继承方式 类名
    {
        ...
    }

    用虚基类重写上述代码如下:

    //
    //  VirtualClass.cpp
    //  VirtualClass
    //
    //  Created by 刘一丁 on 2019/8/31.
    //  Copyright © 2019年 LYD. All rights reserved.
    //
    
    
    //file1.cpp
    #include <iostream>
    using namespace std;
    
    class base
    {
    public:
        base()
        {
            a = 5;
            cout << "base a = " << a << endl;
        }
    protected:
        int a;
    };
    
    class base1:virtual public base
    {
    public:
        base1()
        {
            a = a + 10;
            cout << "base1 a = " << a << endl;
        }
    };
    
    class base2:virtual public base
    {
    public:
        base2()
        {
            a = a + 20;
            cout << "base2 a = " << a << endl;
        }
    };
    
    class derived:public base1, public base2
    {
    public:
        derived()
        { cout << "derived a = " << a << endl; }
    };
    
    int main()
    {
        derived obj;
        
        return 0;
    }
    
    //output:
    //base a = 5
    //base1 a = 15
    //base2 a = 35
    //derived a = 35
    
    

    在上述程序中,从类 base 派生出来 base1 和类 base2 时,使用了关键字 virtual,把类 base 声明为类 base1 和 base2 的虚基类。这样,从类 base1 和 base2 派生出的类 derived 只继承基类 base 一次,也就是说,基类 base 的数据成员 a 只保留一份。具体层次关系如下图。

     

     

    展开全文
  • 虚基类 在说明其作用前先看一段代码 class A { public: int iValue; }; class B:public A { public: void bPrintf(){cout<<"This is class B"<<
      
    

    虚基类

    在说明其作用前先看一段代码

    从代码中可以看出类B C都继承了类A的iValue成员,因此类B C都有一个成员变量iValue ,而类D又继承了B C,这样类D就有一个重名的成员 iValue(一个是从类B中继承过来的,一个是从类C中继承过来的).在主函数中调用d.iValue 因为类D有一个重名的成员iValue编译器不知道调用 从谁继承过来的iValue所以就产生的二义性的问题.正确的做法应该是加上作用域限定符 d.B::iValue 表示调用从B类继承过来的iValue。不过 类D的实例中就有多个iValue的实例,就会占用内存空间。所以C++中就引用了虚基类的概念,来解决这个问题。

    在继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例。例如iValue这个成员,从类 D这个角度上来看,它是从类B与类C继承过来的,而类B C又是从类A继承过来的,但它们只保留一个副本。因此在主函数中调用d.iValue时就不 会产生错误。

    虚函数
    还是先看代码

    大家以为这段代码的输出结果是什么?有的人可能会马上回答funPrint of class A 与 funPrint of class B 因为第一次输出是引用类A的实 例啊,第二次输出是引用类B的实例啊。那么我告诉你这样想就错啦,答案是funPrint of class A 与 funPrint of class A 至于为什么输出 这样的结果不在本文讨论的范围之内;你就记住,不管引用的实例是哪个类的当你调用的时候系统会调用左值那个对象所属类的方法。比如说 上面的代码类A B都有一个funPrint 函数,因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了。可是如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到 虚函数(也就是动态联篇)

    在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,而是动态决定将要调 用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。但派生类中的virtual关键字可以省略,也表 示这是一个虚函数。下面来解决一下代码,声明一个基类的指针(必须是基类,反之则不行)p,把p指向类A的实例a,调用funPrint函数,这 时系统会判断p所指向的实例的类型,如果是A类的实例就调用A类的funPrint函数,如果是B类的实例就调用B类的funPrint函数。

    纯虚函数
    与其叫纯虚函数还不如叫抽象类,它只是声明一个函数但不实现它,让派生类去实现它,其实这也很好理解。

      

    如上代码,定义了一个交通工具类(Vehicle),类中有一函数可打印出交通工具的轮胎个数,但交通工具很多轮胎个数自然也就不确定,所以 就把它定义为纯虚函数,也就是光定义函数名不去实现它,类Camion继承了Vehicle并实现了里面的代码,打印出有4个轮胎。Bike类也是一样。 有一点须要注意一下,纯虚函数不能实化化,但可以声明指针。


    总结

    虚基类
    1, 一个类可以在一个类族中既被用作虚基类,也被用作非虚基类。
    2, 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的子对象。
    3, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。
    4, 最派生类是指在继承结构中建立对象时所指定的类。
    5, 派生类的构造函数的成员初始化列表中必须列出对虚基类构造函数的调用;如果未列出,则表示使用该虚基类的缺省构造函数。
    6, 从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生 类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象 只初始化一次。
    7, 在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

    虚函数
    1, 虚函数是非静态的、非内联的成员函数,而不能是友元函数,但虚函数可以在另一个类中被声明为友元函数。
    2, 虚函数声明只能出现在类定义的函数原型声明中,而不能在成员函数的函数体实现的时候声明。
    3, 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。
    4, 若类中一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同的实现。当使用该成员函数操作指针或引用所标识的对象时 ,对该成员函数调用可采用动态联编。
    5, 定义了虚函数后,程序中声明的指向基类的指针就可以指向其派生类。在执行过程中,该函数可以不断改变它所指向的对象,调用不同 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。 纯虚函数 版本的成员函数,而且这些动作都是在运行时动态实现的。虚函数充分体现了面向对象程序设计的动态多态性。

    纯虚函数
    1, 当在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数,其实现留待派生类完成。
    2, 纯虚函数的作用是为派生类提供一个一致的接口。
    3, 纯虚函数不能实化化,但可以声明指针。

    展开全文
  • C++类的继承概念辨析虚函数虚函数表动态绑定虚函数应用示例虚函数的实现:虚函数虚函数的意义抽象类和纯虚函数虚基类和虚继承虚基类的代码实现 虚函数虚函数表 在了解什么是虚函数之前,首先要理解什么是动态...
  • 最近写程序的时候发现,派生类对象的基类函数如果调用基类虚函数,则它将调用的是派生类的对应函数,而不是我想调用的基类的对应函数。 下面用一例子来说明: //基类 class Base { public: void funA(); virtual...
  • C++ - 虚基类虚函数与纯虚函数

    千次阅读 多人点赞 2017-04-10 17:50:07
    转自:... 虚基类 在说明其作用前先看一段代码 classA { public: intiValue; }; classB:publicA { public: voidbPrintf(){cout<<"This is class B"<<...
  • 基类虚函数和纯虚基类

    万次阅读 2010-02-08 14:17:00
    首先看一例子:class Base{public: virtual void print() //声明虚函数基类声明了虚函数,子类就不用在virtual了,基类可以传递给子类的同样的函数 //基类如果不声明成虚函数,声明成:void print(),则下面...
  • 在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生。编译器总是根据类型来调用类...一个基类的函数一旦声明为虚函数,那么不管你是否加上virtual修饰符,它在所有派生类中都成为虚
  • 继承&虚基类定义:继承:在继承定义中包含了virtual关键字的继承关系,它描述了一种无关于公有、私有、保护...需要注意的是:class B: public virtual A 其中A称之为B的虚基类,而不是说 A 就是个虚基类,因为 A
  • 为什么析构函数总是虚函数?如果这是必要的,那么为什么C++不把虚析构函数直接作为默认值?为什么纯虚析构函数可以通过编译,但是不能通过连接? 回答: 编译器总是根据类型来调用类成员函数。但是一派生类...
  • 理解虚基类虚函数与纯虚函数的概念 引言 一直以来都没有写过一篇关于概念性的文章,因为我觉得这些概念性的东西书本上都有并且说的也很详细写来也无用,今天突发奇想想写 一写,下面就和大家讨论一下虚基类、...
  • 理解虚基类虚函数与纯虚函数的概念虚基类,在说明其作用前先看一段代码class A{public: int iValue;};class B:public A{public: void bPrintf(){cout};class C:public A{public: void cPrintf(){cout};class D:...
  • 原文地址:理解虚基类虚函数与纯虚函数的概念 引言  一直以来都没有写过一篇关于概念性的文章,因为我觉得这些概念性的东西书本上都有并且说的也很详细写来也无用,今天突发奇想想写 一写,下面就和大家...
  • C++明确指出,当派生类对象经由一个基类指针被删除,而该基类带着一个non-virtual析构函数(即非析构函数),其结果是未有定义的,实际在执行时通常会发生的是对象的派生成分没有被销毁(即派生类的析构函数没有...
  • 虚函数与虚基类

    2017-10-30 10:00:36
    3.虚基类是用来在继承中,如果父类继承自同一父类,就只实例化一父类(说的有点绕,就是只实例化一爷爷的意思=。=)。 为了记住以上区别,首先,需要明白为什么要引入虚函数虚函数的作用是什么,有什
  • C++虚基类虚函数~

    2017-05-09 01:17:25
    我们一般说定义虚基类,是因为为了去解决二义性的问题(在菱形结构中,由于一级派生类函数重名,二级派生类对象调用同名函数时程序产生两种或多种可能,有歧义,就产生了二义性问题;或者像基类的数据成员在二级派生...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 68,603
精华内容 27,441
关键字:

多个基类情况下的虚函数