精华内容
下载资源
问答
  • C++多态性,虚函数,重载,抽象类

    千次阅读 2016-07-21 14:55:17
    1,C++多态性,虚函数,重载,比较有意思,,,,,, 在面向对象的程序中,当不同的对象接受到相同的消息产生不同的动作,这种性质称为多态性。

    1,C++多态性,虚函数,重载,纯虚函数,比较有意思,,,,,,


    在面向对象的程序中,当不同的对象接受到相同的消息产生不同的动作,这种性质称为多态性。

    简单的来说:就是指用一个名字定义不同的函数,这些函数执行不同但有类似的操作,即所谓的同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”


    例如:比较两个数的大小,我们可以针对不同的数据类型(整型,浮点型,双精度型),写出多个不同名称的函数来

    实现。但是,事实上,由于它们的功能完全相同,所以可以利用多态性来完成这些功能。。。。

    int Max(int i, int j)
    {
            return i>j?i:j; 
    }
    
    float Max(float i, float j)
    {
            return i>j?i:j; 
    }
    
               鉴于此:如果3,4或着1.2,3.4比较大小的时候,那么Max(3,4)和Max(1.2,3.4)被调用时,编译器会自

                               判断调用哪一个函数。。。。。。


    多态性与联编(编译)


              在面向对象的语言中,多态性的实现和联编密不可分。联编(binding,绑定或装配)是将一个标识符名和一个

              存储地址联系在一起。。。。一个源程序经过编译,连接,最后生成可执行代码,就是将部分的执行代码联编

              在一起的过程。。。。


              一般而言,联编方式有两种:静态联编(static binding)和动态联编(dynamic binding)。静态联编是联编在一

              个源程序经过编译,连接,成为可执行文件这个过程中的编译阶段完成的,即联编过程是在运行之前完成的,,

              动态联编:是指联编在一个源程序经过编译,连接,成为可执行文件的这个过程中的程序执行阶段完成的,即

              联编过程是在程序运行时才动态完成的,,,,


              C++实现多态性有以下两种情况

              1,编译时多态行:通过函数重载,运算符重载,模板重载

              2,运行时多态:借助虚函数来实现


    虚函数声明:

    virtual   函数类型    函数名称   (形式参数表);
               虚函数的声明只能出现在类声明的函数原型声明中,,,在派生类中可以不显示的声明为虚函数,当调用此成员

               函数时,C++系统会自动判断调用哪一个对象的成员函数。。。。


               虚函数:单界面多实现

               基类中用虚函数定义了一个函数的原型(提供一个界面),那么就提供了公有继承对象的一个公共的界面,而

               多个派生类重新定义虚函数的函数体内容(多个实现版本),将基类的指针指向派生类对象,从而达到虚函数

               不同实现的目的。。。。。。。。


    代码1:

    #include <iostream>
    using namespace std;
    
    class shape
    {
            public:
                    float area()
                    {     return -1;}       
    };
    
    class circle:public shape
    {
            float radius;
            public:
                    circle(float r)
                    {     radius = r;}
                    float area()
                    {     return 3.14*radius*radius;}       
    };
    
    int main()
    {
            shape obj, *ptr;
            circle c(3.4);
            
            ptr = &obj;
            cout<<ptr->area()<<endl;
            
            ptr = &c;
            cout<<ptr->area()<<endl;        
    }

    程序运行结果:

    结果为什么会是这样的呢???我们希望的是求圆的面积啊,,,,,

    这是因为在这里只是用到了静态编译,,,,所以前后两次ptr->area()都调用了基类中的area函数,,,,

    但是,,,值得注意的是:对于我们的变量,如果有相同的话,那么就会调用派生类中的值。。。。。。。。。


    而如果,我们把代码改成了这样:


    #include <iostream>
    using namespace std;
    
    class shape
    {
            public:
                    virtual  float area()//跟上面的程序相比,只有这里不一样
                    {     return -1;}       //关键字virtual说明此函数为虚函数
    };
    
    class circle:public shape
    {
            float radius;
            public:
                    circle(float r)
                    {     radius = r;}
                    float area()      //在派生类中,虚函数被重新定义以实现不同的操作。
                    {     return 3.14*radius*radius;}       //这种方式称为:函数超越,或者函数覆盖。。。。
    };
    
    int main()
    {
            shape obj, *ptr;
            circle c(3.4);
            
            ptr = &obj;
            cout<<ptr->area()<<endl;
            
            ptr = &c;
            cout<<ptr->area()<<endl;        
    }

    运行结果:


    概述:

    1,virtual关键字:指示C++编译器对该函数的调用进行动态联编

    2,尽管可以用对象名和点运算符的方式调用虚函数,即向对象发送消息tri.area()或者rect.area()。这同样是静态联编

    3,只有当访问虚函数是通过基类指针s(或者引用)时才可获得运行时的多态行。。。。

    4,在派生类中,虚函数被重新定义时,其函数的原型必须与基类中的函数原型完全相同,,,,,,,,,,,,,

          也就是说:什么都一样,就是函数体的实现完全不同

    5,必须在继承的体系下实现。。。。。,必须是类的成员函数,普通函数不可能是虚函数。。。。。。

    6,虚函数不能是静态成员函数,,,,因为静态成员函数的存储位置说明了它不受限与某个具体的对象。。。。。

    7,根据特性,运行时确定,所以内联函数自然而然也就不是虚函数。。。。。。

    8,构造函数不能是虚函数,析构函数可以是:因为构造时,对象还是一片未定型的空间。。。。只有在构造完成后,

          对象才能成为一个类的名副其实的实例,,,,,、



    由此,我们再次简单的总结一下:

    虚函数,声明定义一定在基类中,其他的实现可能在派生类中(前提是:函数原型基本一样),这样,通过基类的指

    针(引用)跟派生类的对象挂钩,那么调用的就是派生类中的函数了,,,实现了多态。。。。


    这里可能跟函数重载有一定关系::

    #include <iostream>
    using namespace std;
    
    class base
    {
            public:
                    virtual void func1();
                    virtual void func2();
                    virtual void func3();
                    void fun4();    
    };
    
    class derived:public base
    {
            public:
                    virtual void func1();     //virtual  ,虚函数的再次实现
                    void func2(int x);
                    char func3();             //错误,返回类型不同
                    void func4();             //普通函数的重载,不是虚函数,,,
    };
    
    void main()
    {
            base d1,*bp;
            derived d2;
            
            bp = &d2;
            bp->func1();       //调用父类func1()                                                                                                  
            bp->func2();       //调用基类func2()
            bp->func3();       //调用基类func3()
            bp->func4();       //调用基类func4()  
    }

    虚函数:只能是成员函数,,,,,重载函数:没有要求

    虚函数:不同调用通过不同的对象指向或引用

    重载函数:根据参数的类型或个数来进行不同的调用。。。。。。



    什么函数不能声明为虚函数?

    普通函数(非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数


    虚函数通过继承方式来体现出多态作用,它必须是基类的非静态成员函数,其访问权限可

    以是protected或public,在基类的类定义中定义虚函数的一般形式是:


    virtual 函数返回值类型虚函数名(形参表){ 函数体 }


    常见的不能声明为虚函数的有:普通函数(非成员函数)、静态成员函数、内联成员函数、构造函数、友元函数。

    (1)普通函数不能声明为虚函数。普通函数(非成员函数)只能被重载(overload),不能被重写(override),声明为虚函数也没有什么意思,因此编译器会在编译时绑定函数。


    (2) 构造函数不能声明为虚函数。构造函数一般用来初始化对象,只有在一个对象生成之后,才能发挥多态作用。如果将构造函数声明为虚函数,则表现为在对象还没有生成的时候来定义它的多态,这两点是不统一的。另外,构造函数不能被继承,因而不能声明为虚函数。


    (3) 静态成员函数不能声明为虚函数。静态成员函数对于每个类来说只有一份代码,所有的对象都共享这份代码,它不归某个对象所有,所以也没有动态绑定的必要性。


    (4) 内联(inline)成员函数不能声明为虚函数。内联函数就是为了在代码中直接展开,减少函数调用开销的代价。虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。另外,内联函数在编译时被展开,虚函数在运行时才能动态的绑定函数。

    (5) 友元函数不能声明为虚函数。友元函数不属于类的成员函数,不能被继承。


    设置虚函数时须注意以下几点:


    • 只有类的成员函数才能说明为虚函数;

    • 静态成员函数不能是虚函数;

    • 内联函数不能为虚函数;

    • 构造函数不能是虚函数;

    • 析构函数可以是虚函数,而且通常声明为虚函数。



    纯虚函数:

    纯虚函数是在基类中说明的虚函数,它在该基类中没有定义具体的操作内容,而在各个派生类中根据实际需要定义

    自己的实现。。。。。。

    virtual  函数类型  函数名称(函数参数表) = 0;
    从声明格式看:纯虚函数是在虚函数成员的后面加上“= 0”,纯虚函数是根本没有函数体的,当声明为纯虚函数,那么

    为一个类的名副其实的实例,,,,,、



    由此,我们再次简单的总结一下:

    虚函数,声明定义一定在基类中,其他的实现可能在派生类中(前提是:函数原型基本一样),这样,通过基类的指

    针(引用)跟派生类的对象挂钩,那么调用的就是派生类中的函数了,,,实现了多态。。。。


    这里可能跟函数重载有一定关系:

    基类中就不能给出函数的实现部分,其函数体是由基类的派生类给出。。。。。。。。。。


    可以这样说:

    有一个动物类,,,,继承有狮子,老虎等,当没有具体的动物时,我们探究它的是不是哺乳动物(作为一个函数)

     就没有一点价值

    了,所以,此刻的动物类就是一个抽象类,,,,也就是说:动物类这个基类里面没有这个函数的实现,也不能有


    那么什么是抽象类(含有纯虚函数的类,抽象类)

    class animal
    {
            public:
                    virtual void ISLEG_NUMBER() = 0;        
    };
    
    int main()
    {
            animal pig;       //错误,抽象类中不能存在对象
            animal *p;        //正确,可以声明指向动物类(抽象类)的指针
            animal f();       //错误,抽象类不能作为函数的返回类型
            g(animal);        //错误,抽象类不能作为函数的参数类型  
    }

    抽象类,一定不能有对象,,,,,,,,,,,,,,,,,,,,,,,,,,



    展开全文
  • 模板成员函数为什么不能是虚函数

    千次阅读 2019-01-12 13:17:19
    模板成员函数为什么不能是虚函数
                   


    《Thinking in C++》volume 2第五章有这么一句话:
    Member template functions cannot be declared virtual.Current compiler technology experts to be able to determine the size of a class’s virtual function table when the class is parsed.Allowing virtual member template functions woule require knowing all calls to such member functions everywhere in the program ahead of time.This is not feasible,especially for multi-file projects.
    在这段话里作者解释了为什么类的成员模板函数不能是虚函数。自己半懂不懂。没写过编译器,遇到费解的概念如编译时/运行时、静态/动态、内部连接/外部连接,以及很多比如“函数模板不能有默认的模板参数”这样的规则时,只好望洋兴叹。
    然而问题不可累积。有必要借助这个问题明确一些基本的概念了。
    首先:当模板类被实例化时,它实际上也产生了一种新的类型,不同的参数实例化出不同的类型。例如,用五个不同的参数分别实例化一个模板类,则相当于新定义了五个普通类。用代码说明问题:

    1. #include <iostream> 
    2. #include <typeinfo> 
    3. using namespace std; 
    4.   
    5. template <class Type> class Test { 
    6. public
    7.      void f() { 
    8.          cout << "My Type is: " << typeid(*this).name() << endl; 
    9.      } 
    10. }; 
    11.   
    12. int main() { 
    13.      Test<char>().f(); 
    14.      Test<int>().f(); 
    15.      
    16.      cout.setf(ios::boolalpha); 
    17.      
    18.      cout << bool(typeid(Test<char>) == typeid(Test<int>)) << endl; 
    19.      return 0; 


    输出结果:
    My Type is: class Test<char>
    My Type is: class Test<int>
    false
    由此可知,这是两种完全不同的类型,它们的差别就像int和char的差别。顺便,如何实现这两种类型的转换呢?《Thinking in C++》volume 2告诉了我们一种技术:模板拷贝构造函数。代码示例:

    1. #include <iostream> 
    2. #include <typeinfo> 
    3. using namespace std; 
    4.   
    5. template <class Type> class Test { 
    6. public
    7.      void f() { 
    8.          cout << "My Type is: " << typeid(*this).name() << endl; 
    9.      } 
    10. }; 
    11.   
    12.   
    13.   
    14. int main() { 
    15.      Test<char> tc; 
    16.      Test<char> tc2; 
    17.      tc2 = tc; //ok 
    18.      Test<int> ti; 
    19.      ti = tc; // error :there is no acceptable conversion 
    20.      return 0; 


    上述代码直接将Test<char>类型的变量赋值给Test<int>类型的变量编译时会出现错误。不出错的方法就是使用模板拷贝构造函数进行类型转换,如下,把Test类定义成下面这个样子即可:

    1. template <class Type> class Test { 
    2. public
    3.      Test() {} 
    4.      template <class I> Test(const Test& t) {} 
    5.      void f() { 
    6.          cout << "My Type is: " << typeid(*this).name() << endl; 
    7.      } 
    8. }; 


    其二:在这一点上,模板函数与模板类有一样的特点。即:不同的参数实例化出不同的重载函数。代码演示:

    1. #include <iostream> 
    2. #include <typeinfo> 
    3. using namespace std; 
    4.   
    5. template <class T> T add(const T& a, const T& b) { 
    6.      return a+b; 
    7.   
    8. int main() { 
    9.      cout << typeid(add<int>).name() << endl;//int __cdecl(int const &,int const &) 
    10.      
    11.      cout.setf(ios::boolalpha); 
    12.      cout << bool(typeid(add<int>) == typeid(add<double>)) << endl;//false 
    13.   
    14.      add(3, 4); // add<int>(3,4), address: 004171C0 
    15.      add(3.0, 4.0); // add<double>(3.0, 4.0), address: 00417200 
    16.      return 0; 

    那么,又引出两个问题:关于重载以及函数指针。
    先看重载。C++编译器不会用返回值来重载函数。然而上面的例子说明不同的类型参数实例化出来不同的重载函数。且看看仅把模板类型参数作为模板函数的返回值时用不同的参数还能不能实例化出来不同的函数:

    1. #include <iostream> 
    2. #include <typeinfo> 
    3. using namespace std; 
    4.   
    5. template <class T> T onlyRet(int a) { 
    6.      cout << a << endl; 
    7.      return T(); 
    8.   
    9. int main() { 
    10.      cout.setf(ios::boolalpha); 
    11.      cout << bool(typeid(onlyRet<char>) == typeid(onlyRet<short>)) << endl; 
    12.      return 0; 

    输出仍然为false。由此可见:模板函数提供了一种实现特殊重载的编码方法,但跟重载是两回事。(说是特殊的重载,因为仅仅是类型替换性质的,而真正的重载函数彼此之间的内部实现可以完全不同)。可以生成该程序的汇编代码看看编译后的函数名字修饰,在命令行下:cl /Fatemp.asm /FAs template_test.cpp,然后打开temp.asm,搜索onlyRet,可以看到如下一段代码:
    PUBLIC  ??$onlyRet@F@@YAFH@Z                      ; onlyRet<short>
    PUBLIC  ??$onlyRet@D@@YADH@Z                            ; onlyRet<char>

    另一个问题就是函数指针的问题。既然模板可以接受任意类型作为参数,那么如果有模板函数指针的话,它是不是就可以指向任意一个有指定参数数目的函数了?还是用代码来说话:

    1. #include <iostream> 
    2. #include <typeinfo> 
    3. using namespace std; 
    4.   
    5. template <class T> void (*p)(T); 
    6.   
    7. void f(int a) {} 
    8.   
    9. int main() { 
    10.      p = f; 
    11.      return 0; 

    很可惜,上面的
    template <class T> void (*p)(T);
    这一句在编译时会得到一个错误:error C2998: 'void (__cdecl *__cdecl p)(T)' : cannot be a template definition。我想,这可能是”type deduction”的问题,也许高手不用测试程序就能推测出来,但不管怎样,自己也明白了这样不行。
    现在,就可以明白作者的解释了。作者说:当前的编译器都期望在处理类的定义的时候就能确定这个类的虚函数表的大小,如果允许有类的虚成员模板函数,那么就必须要求编译器提前知道程序中所有对该类的该虚成员模板函数的调用,而这是不可行的。
    为什么作者这样说呢?从上面的演示知道,对于一个模板函数,不同的模板参数会产生出不同的函数。这样的话,如果要知道类内包含多少个虚函数,就只能去代码中寻找。这就不单单是多文件的问题,还有RTTI的问题了。

               

    再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

    展开全文
  • C++ 虚函数是否需要定义?

    千次阅读 2015-09-04 20:15:06
    C++ 虚函数是否需要定义? ...(1)若该类有实例化对象,在类中仅仅声明虚函数而没有定义实现,编译是不可以通过的,将会出现连接错误!(2)若该类没有实例化对象,则只声明不定义是可以通过编译的

    C++ 虚函数是否需要定义?

    个人觉得这个问题比较重要。以下是鄙人在阅读了其他博客总结出来的,若有错误,希望得到大家的批评修改!谢谢!

    问题: C++中的虚函数必须有定义吗?

    答:若该类有实例化对象,则虚函数必须定义,而不是简单的声明。(1)若该类有实例化对象,在类中仅仅声明虚函数而没有定义实现,编译是不可以通过的,将会出现连接错误!2)若该类没有实例化对象,则只声明不定义是可以通过编译的。

    例题1:

    #include<iostream>
    using namespace std;
    
    class Base
    {
    public:
    	int Base_1;
    	int Base_2;
    public:
    	void func1();
    	virtual void func2();
    };
    int main(){
        Base obj_base;
    	return 0;
    }
    
    <p><span style="font-size:14px;">在例题</span><span style="font-family:Calibri;font-size:14px;">1</span><span style="font-size:14px;">中,虚函数</span><span style="font-family:Calibri;font-size:14px;">virtual void func2();</span><span style="font-size:14px;">只有声明,没有定义,但是在主函数中,有类</span><span style="font-family:Calibri;font-size:14px;">Base</span><span style="font-size:14px;">的实例化对象,所以编译通不过,错误如下图所示:</span></p><p><span style="font-size:14px;"><img src="https://img-blog.csdn.net/20150904202243761?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span></p><span style="font-size:14px;"></span><p><span style="font-size:14px;">修改程序</span><span style="font-family:Calibri;font-size:14px;">1</span><span style="font-size:14px;">,添加</span><span style="font-family:Calibri;font-size:14px;">virtual void func2();</span><span style="font-size:14px;">的定义,则程序编译通过。</span></p><span style="font-size:14px;"></span><p><span style="font-size:14px;">类</span><span style="font-family:Calibri;font-size:14px;">Base</span><span style="font-size:14px;">在内存中的分布如下所示:</span></p>
    <span style="font-size:14px;"><img src="https://img-blog.csdn.net/20150904202404596?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>
    <span style="font-size:14px;"></span><p><span style="font-size:14px;">当然若类没有实例化对象,则类中的虚函数可以不定义只声明,程序如下所示,可以通过编译。</span></p><span style="font-size:14px;"></span><pre class="cpp" name="code">#include<iostream>
    using namespace std;
    
    class Base
    {
    public:
    	int Base_1;
    	int Base_2;
    public:
    	void func1();
    	virtual void func2();
    };
    //void Base::func2(){
    //	cout << "pass" << endl;
    //}
    int main(){
     //   Base obj_base;
    	return 0;
    }

    同时也应该注意virtual关键字的使用,virtual关键字只能出现在类中,不能出现在出了类定义以外的程序中。

    例如下面的程序是错误的。

    #include<iostream>
    using namespace std;
    
    class Base
    {
    public:
    	int Base_1;
    	int Base_2;
    public:
    	void func1();
    	virtual void func2();
    };
    virtual void Base::func2(){
    	cout << "pass" << endl;
    }
    int main(){
        Base obj_base;
    	return 0;
    }


     
    

    展开全文
  • 虚函数虚函数相当于函数指针,占用四个字节(对于32位),在类中虚函数占用四个字节,其成员函数不占类的内存。基类定义虚函数,优先调用子类中的同名函数,覆盖虚函数。基类指针访问不同派生对象,调用不同方法。...

    虚函数:虚函数相当于函数指针,占用四个字节(对于32位),在类中虚函数占用四个字节,其成员函数不占类的内存。

    基类定义虚函数,优先调用子类中的同名函数,覆盖虚函数。基类指针访问不同派生对象,调用不同方法。

    注意:1.虚函数必须是类的成员函数

             2.不能将友元函数说明为虚函数,但是虚函数可以是另一个类的友元。

             3.析构函数可以是虚函数,但是构造函数不能是虚函数。

    #include<iostream>
    using namespace std;
    class base1
    {
    public:
    	virtual int run() = 0;
    	virtual double go() = 0;
    };
    
    class base2
    {
    public:
    	virtual void run()
    	{
    
    	}
    	virtual void go()
    	{
    
    	}
    };
    
    void main()
    {
    	cout << sizeof(base1)<<endl;
    	cout << sizeof(base2) << endl;
    
    
    	cin.get();
    
    }

    当父类对象指针指向派生类对象时,析构时会自动调用子类析构函数,

    #include<iostream>
    class base
    {
    public:
    	 virtual void name()	//虚函数占据四个字节的地址
    	{
    		std::cout << "base" << std::endl;
    		std::cout << "x=" << x << std::endl;
    	}
    	int x;
    	base(int a) :x(a)
    	{
    
    	}
    };
    class zi :
    	public base
    {
    	public:
    		void name()
    		{
    			std::cout << "zi" << std::endl;
    			std::cout << "x=" << x << "y=" << y<< std::endl;
    		}
    
    		int y;
    		zi(int a,int b) :base(a),y(b)
    		{
    
    		}
    };
    class sun :
    	public zi
    {
    public:
    	void name()
    	{
    		std::cout << "sun" << std::endl;
    		std::cout << "x=" << x << "y=" << y << "z=" << z << std::endl;
    	}
    	int z;
    	sun(int a,int b,int c) :zi(a,b),z(c)
    	{
    
    	}
    };
    //父类指针的迁移
    void main()
    {
    	base p1(1);
    	zi p2(2,3);
    	sun p3(4,5,6);
    	base *p;
    
    	p = &p1;  //访问父类成员数据
    	p->name(); //基类是虚函数的首先访问派生类的函数
    
    	p = &p2; //访问子类成员继承的父类成员的数据
    	p->name();
    
    	p = &p3;//访问孙类成员继承的父类成员的数据
    	p->name();
    
    	std::cout << sizeof(base) << std::endl;
    
    	std::cin.get();
    
    
    
    }
    void main1()
    {
    	base *pb = new base(1);
    	pb->name();
    
    	zi *pz = new zi(2,3);
    	pz->name();
    
    	sun *ps = new sun(4,5,6);
    	ps->name();
     
    	zi *p =static_cast<zi *>(pb) ;
    	p->name();
    
    
    	std::cin.get();
    }

    析构函数是虚函数时,会自动调用派生类和基类的析构函数。

    #include<iostream>
    
    class my
    {
    public:
    	//virtual  构造函数不能是虚函数 
    	my()
    	{
    		std::cout << "my creat" << std::endl;
    	}
    	virtual
    		~my()  //析构函数是虚函数时,派生类在析构时会自动调用派生类的析构函数,析构派生类和基类
    	{
    		std::cout << "my delete" << std::endl;
    	}
    };
    
    class myzi :public my
    {
    public:
    	myzi()
    	{
    		std::cout << "myzi creat" << std::endl;
    	}
    	~myzi()
    	{
    		std::cout << "myzi delete" << std::endl;
    	}
    
    };
    void run()
    {
    	/*my *p = new my;
    	delete p;*/
    
    	my *p1 = new myzi;
    	delete p1;
    }
    void main3()
    {
    	run();
    	std::cout << "hello world!" << std::endl;
    
    	std::cin.get();
    }

    虚函数重载特性:

    1.派生类中的任何函数属性都应该与基类中的相同。

    2.返回类型应该相同,否则则认为错误重载。

    3.原型不同,仅函数名相同,丢失虚函数特性。

    #include<iostream>
    
    class A
    {
    public:
    	virtual int run()
    	{
    		return 1;
    	}
    	virtual int go()
    	{
    		return 0;
    	}
    
    };
    
    class B :public A
    {
    public:
         virtual int run()
    	{
    		return 2;
    	}
    	 //void go()
    	 //{
    
    	// }
    
    };
    
    void main4()
    {
    	B c;
    	std::cout<<c.A::run()<<std::endl;
    
    	std::cin.get();
    }

    纯虚函数函数抽象类。

    1.纯虚函数,在基类中说明虚函数,但是在基类中没有定义,要求任何派生类都定义自己的版本。

    2.纯函数作为派生类的一个公共界面。

    3.具有纯虚函数的基类抽象类。抽象类没有办法进行实例化,通过派生进行实例化。

    4.抽象类不可以用于函数的参数以及返回值类型,但是抽象类指针可以。

    5.虚函数表位于编译器的代码区中,虚函数指针指向虚函数表,一个或者多个虚函数都占用四个字节。

    #include<iostream>
    
    //抽象类
    class A
    {
    public:
    	int a;
    	virtual void show() = 0;
    	virtual void go() = 0;//纯虚函数
    
    };
    
    class B :public A
    {
    public:
    	int num;
    	void show()
    	{
    		std::cout << "B show()" << std::endl;
    	}
    	void go()
    	{
    		std::cout << "B go()" << std::endl;
    	}
    };
    
    A *test()
    {
    	A *p(nullptr);
    	return p;
    }
    void main5()
    {
    	B c;
    	//A aa;//抽象类不能实例化对象,但是可以实例化指针;
    	A *p;
    	c.go();
    	c.show();
    
    	std::cin.get();
    }

    异质链表:利用基类可以存储派生类指针的特点,用基类指针接收派生类的地址。用基类指针生成一个连接不同派生类对象的动态链表,每个结点指针,指向类层次中不同的派生类对象。结点类型不同的链表。

    #include<iostream>
    #include<Windows.h>
    #include<stdlib.h>
    using namespace std;
    class base
    {
    public:
    	virtual void show() = 0;
    };
    
    class linknode
    {
    public:
    	base *p; //数据域
    	linknode *pnext; //指针域
    };
    
    class A :public base
    {  
    public:
    	void show()
    	{
    		cout << "class A\n";
    	}
    
    };
    
    class B :public base
    {
    public:
    	void show()
    	{
    		cout << "class B\n";
    	}
    };
    
    class C :public base
    {
    public:
    	void show()
    	{
    		cout << "class C\n";
    	}
    };
    class D:public base
    {
    public:
    	void show()
    	{
    		cout << "class D\n";
    	}
    };
    void showall(linknode *phead)
    {
    	while (phead != NULL)
    	{
    		phead->p->show();
    		phead = phead->pnext;
    	}
    }
    void add(linknode *phead, base *p)
    {
    	linknode *ptemp = phead;
    	if (ptemp == NULL)
    	{
    		ptemp->p = p;
    		ptemp->pnext = NULL;
    	}
    	else
    	{
    		while (ptemp->pnext != NULL)
    		{
    			ptemp = ptemp->pnext;
    		}
    		linknode *padd = new linknode; //新建一个节点,然后插入到链表的尾部
    		padd->p = p;
    		padd->pnext = NULL;
    
    		ptemp->pnext = padd; //让上一节点指向新节点
    	}
    	
    }
    void main()
    {
    	linknode *phead;
    	linknode node1, node2, node3;
    	A a1,a2,a3;
    	B b1,b2,b3;
    	C c1,c2,c3;
    	D d1;
    	phead = &node1;	     //指针指向
    	node1.pnext = &node2;
    	node2.pnext = &node3;
    	node3.pnext = NULL;
    
    	node1.p = &a1;	    //结点存储的数据
    	node2.p = &b1;
    	node3.p = &c1;
    
    
    
    	showall(phead);
    	add(phead,&d1);
    	showall(phead);
    
    	cin.get();
    }


    展开全文
  • C++ 虚函数的定义

    千次阅读 2015-05-18 14:49:17
     本文转自:... ... 需要实例化类的虚函数必须有定义,而仅仅定义带有虚函数的类且虚函数没有实现,该类编译是可以通过的!  纯虚函数出现在接口类中,并赋值为0,不
  • C++之:虚函数

    千次阅读 2016-03-25 11:27:08
    一、文章来由虚函数表究竟存放在哪里?二、概念C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让...
  • 虚函数-运行时多态的理解

    千次阅读 2016-03-16 09:20:29
    形状对外公开一个函数来把自己绘制出来。这是合理的,形状就应该能绘制出来,对吧?由于继承的原因,多边形和圆形也有了绘制自己这个函数。 现在我们来讨论在这三个类中的绘制自己的函数都应该怎么实现。在形状中嘛...
  • C++的多态与虚函数

    千次阅读 2015-01-06 21:49:28
    在C++中,多态有两种,一种是函数重载,一种是虚函数。函数重载发生在编译的时候,它的函数参是不一样的。而虚函数是发生在运行的时候,它的函数原型式样的,依靠的是指针的指向。 有一篇非常好的文章介绍多态与...
  • C++虚函数写在类外时的问题

    千次阅读 2017-04-16 23:39:47
    对于大部分虚函数的实现是可以像普通函数一样,.h文件中...但当虚函数中参数或返回值为不完整类型时,如果这样做就可能出现连接问题。 原因还不清楚,但是只要把类型变为完整类型(添加相应头文件),错误就得以解决。
  • 静态函数 用static声明的函数是静态函数。静态函数可以分为全局静态函数和类的静态成员函数。 Static关键字 在类中,用static声明的成员变量为静态成员变量,它为该类的公用变量,在第一次使用时被初始化,对于...
  • 虚函数必须实现么????

    千次阅读 2013-09-13 00:09:42
    需要实例化类的虚函数必须有定义,而仅仅定义带有虚函数的类且虚函数没有实现,该类... 一般的成员函数可以只有声明,前提是在应用中不能调用该函数,否则会因找不到定义产生连接错误!    实例化类的虚函数
  • OFDM完整仿真过程及解释(MATLAB)

    万次阅读 多人点赞 2019-04-19 17:03:45
    %星座图(也可以取实部用plot函数) %% 扩频 %————————————————————————————————————————————————————————% %扩频通信信号所占有的频带宽度远大于所传信息...
  • 深度剖析C++虚函数

    千次阅读 2014-11-27 20:57:40
    前面探索了C++对象的内存模型,其中简单的涉及到了虚函数,作为C++实现其多态的一个重要机制,这里进一步探索下虚函数机制,以前也看过网络上关于虚函数机制的一些精彩的文章,但现在决定自己再分析这个虚函数机制以...
  • c++ 类大小(含虚函数

    千次阅读 2011-12-20 13:06:14
    虽然很难找到一本不讨论多态性的C++书籍或杂志,但是,大多数这类讨论使多态性和C++虚函数的使用看起来很难。我打算在这篇文章中通过从几个方面和结合一些例子使读者理解在C++中的虚函数实现技术。说明一点,写这篇...
  • 这么一大堆名词,实际上就围绕一件事展开,就是多态,其他三个名词都是为实现C++的多态机制而提出的一些规则,下面分两部分介绍,第一部分介绍【多态】,第二部分介绍【虚函数,纯虚函数,抽象类】一 【多态】多态的...
  • C++面试题

    千次阅读 多人点赞 2019-01-07 17:06:10
    当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期...
  • 测试开发笔记

    万次阅读 多人点赞 2019-11-14 17:11:58
    40 视图/表 view 41 索引 42 存储过程 procedure 42 事务 transaction 43 触发器 trigger 46 练习 46 一、单表查询练习 46 二、聚合函数练习 47 三、分组查询练习 47 四、嵌套查询练习 48 五、联接查询练习 48 六...
  • 虚函数 纯虚函数

    2013-06-04 14:40:02
    虚函数必须实现 ,创建实例时连接错误。 多态一般就是通过指向基类的指针来实现的。也就是说用基类指针通过虚函数来决定运行时刻到底是谁而指向谁的函数。  有纯虚函数的类是抽象类,不能生成对象,只能派生。他...
  • 明明白白c++ 虚函数和多态性

    千次阅读 2013-04-26 00:27:37
    本文的目的,一方面是作者本人希望通过写文章来弄清楚虚函数和多态性,另一方面,也希望读者可以自己练习练习,不写代码,不编程序,只看别人的代码和文章很难把知识转换为自己的。大家一起加油。 一 概念 多态性...
  • 9)Reimplemented Protected Functions(保护的虚函数) 10)Detailed Description(详细说明) 三、第一个Qt程序 1.创建工程目录 mkdir Hello 注:每个Qt程序都要放在一个独立的工程目录下 2.进入...
  • C#基础教程-c#实例教程,适合初学者

    万次阅读 多人点赞 2016-08-22 11:13:24
    也避免指针的误操作产生的错误。CLR执行中间语言代码前,要对中间语言代码的安全性,完整性进行验证,防止病毒对中间语言代码的修改。  版本支持:系统中的组件或动态联接库可能要升级,由于这些组件或动态联接库...
  • C++中虚函数必须有定义吗?

    千次阅读 2010-02-25 14:31:00
    需要实例化类的虚函数必须有定义,而仅仅定义带有虚函数的类且虚函数... 一般的成员函数可以只有声明,前提是在应用中不能调用该函数,否则会因找不到定义产生连接错误!    实例化类的虚函数必须有定义,原因如
  • 《数据库原理》— 数据库系统概论第五版习题解析

    万次阅读 多人点赞 2017-05-29 14:57:48
    数据库恢复:当计算机系统发生硬件故障、软件故障,或者由于操作员的失误以及故意的破坏影响数据库中数据的正确性,甚至造成数据库部分或全部数据的丢失时,能将数据库从错误状态恢复到某一已知的正确状态(亦称为...
  • 虚函数

    2013-11-27 16:28:17
    需要实例化类的虚函数必须有定义,而仅仅定义带有虚函数的类且虚函数没有实现,该类... 一般的成员函数可以只有声明,前提是在应用中不能调用该函数,否则会因找不到定义产生连接错误!    实例化类的虚函数
  • WPF开发教程

    万次阅读 多人点赞 2019-07-02 23:13:20
    CLR 提供一系列的功能,可以令开发效率更高并且更加可靠(包括内存管理、错误处理和通用类型系统等),但这是需要付出代价的。 下图说明了 WPF 的主要组件。关系图的红色部分(PresentationFramework、...
  • verilog 综合注意事项

    万次阅读 多人点赞 2016-07-29 15:46:40
    *而且,书中都是讲语句的具体使用办法,例如always @(),但是什么时候用always,几个always之间、时序电路、逻辑电路、任务与函数什么时候用,却没有一本书能讲清楚。* *这个笔记详细写了这些思路的问题,分享给...
  • 让我们直切正题:在程序进行构造或析构期间,你绝不能调用虚函数,这是因为这样的调用并不会按你所期望的执行,即使能够顺利执行,你也不会觉得十分舒服。如果你曾经是一个Java或C#的程序员,并且在最近期望...
  • 多态性提供了同一个接口可以用多种方法进行调用的机制,从而可以通过相同的接口访问不同的函数。具体地说,就是同一个函数名称,作用在不同的对象上将产生不同的操作。 多态性提供了把接口与实现分开的另一种方法,...
  • Qt连接信号到子类槽函数提示父类无相应槽函数通过connect连接一个信号到子类的一个槽函数却提示父类没有对应的槽函数。代码class Parent : public QObject { Q_OBJECT public: Parent(){}; virtual ~Parent(){}; ...
  • 你不应该在构造或析构期间调用虚函数,因为这样的调用不会如你想象那样工作,而且他们做的事情确保会让你非常郁闷。如果你转为 Java 或 C# 程式员,也请你密切关注本文,因为在 C++ 急转弯的地方,那些语言也紧急转...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,621
精华内容 15,448
关键字:

虚函数链接错误