C++继承中虚函数的使用

原创 2012年03月30日 17:19:06
一:继承中的指针问题。
1. 指向基类的指针可以指向派生类对象,当基类指针指向派生类对象时,这种指针只能访问派生对象从基类继承

而来的那些成员,不能访问子类特有的元素 ,除非应用强类型转换,例如有基类B和从B派生的子类D,则B

*p;D  dd; p=ⅆ是可以的,指针p只能访问从基类派生而来的成员,不能访问派生类D特有的成员.因为基类不

知道派生类中的这些成员。
2. 不能使派生类指针指向基类对象 .
3. 如果派生类中覆盖了基类中的成员变量或函数,则当声明一个基类指针指向派生类对象时,这个基类指针只能

访问基类中的成员变量或函数。例如:基类B和派生类D都定义了函数f,则B *p; D m; p=&m; m.f()将调用基类中

的函数f()而不会调用派生类中的函数f()。
4. 如果基类指针指向派生类对象,则当对其进行增减运算时,它将指向它所认为的基类的下一个对象,而不会指

向派生类的下一个对象,因此,应该认为对这种指针进行的增减操作是无效的.
二:虚函数
1. 为什么要使用虚函数:正如上面第1 和3 点所讲的,当声明一个基类指针指向派生类对象时,这个基类指针只

能访问基类中的成员函数,不能访问派生类中 特有的成员变量或函数 。如果使用虚函数就能使这个指向派生类对

象的基类指针访问派生类中的成员函数,而不是基类中的成员函数,基于这一点派生类中的这个成员函数就必须和

基类中的虚函数的形式完全相同,不然基类指针就找不到派生类中的这个成员函数。使用虚函数就实现了一个接口

多种方法。
2. 注意不能把成员变量声明为虚有的,也就是说virtual关见字不能用在成员变量前面。
3. 正如上面所介绍的,一般应使用基类指针来调用虚函数,如果用点运算符来调用虚函数就失去了它的意义.
4. 如果基类含有虚函数则当声明了一个基类的指针时 ,当基类指针指向不同的派生类时,它就会调用相应派生

类中定义的虚函数版本.这种调用方法是在运行时 决定的 ,例如在类B中声明了虚函数,C,D,E 都从B继承而

来且都实现了自已的虚函数版本,那么当定义了一个B类的指针P时,当P指向子类C时就会调用子类C中定义的

虚函数,当
P指向子类D时就会调用子类D中定义的虚函数 ,当P指向子类E时就会调用子类E中定义的虚函数 .
5. 虚函数须在基类中用virtual 关见字声明也可以在基类中定义虚函数,并在一个或多个 子类中重新定义 .重

定义虚函数时不需再使用virtual关见字,当然也可以继续标明virtual关见字,以便程序更好理解。
6. 包括虚函数的类被称为多态类.C++使用虚函数支持多态性.
7. 在子类中重定义 虚函数时 ,虚函数必须有与基类虚函数的声明完全相同的参数类型和数量,这和重载是不同

的.如果不相同,则是函数重载,就失去了虚函数的本质.
8. 虚函数不能是声明它的类的友元函数,必须是声明它的类的成员函数,不过虚函数可以是另一个类的友元.
9. 一旦将函数声明为虚函数,则不管它通过多少层继承,它都是虚函数,例如D和B继承,而E又从D继承,那

么在B中声明的虚函数,在类E中仍然是虚函数.
10.隐藏虚函数:如果基类定义了一个 虚函数 ,但派生类中却定义了一个虚函数的重载板本 ,则派生类的这个

版本就会把基类的虚函数隐藏掉,当使用基类指针调用该函数时只能调用基类的虚函数 ,而不能调用派生类的重

载版本,当用派生类的对象调用基类的 虚函数时就会出现错误了 ,因为基类的虚函数被派生类的重载版本隐藏了


11.带默认形参的虚函数:当基类的虚函数带有默认形参时,则派生类中对基类 虚函数的重定义也必须有相同数

量的形参,但形参可以有默认值也可以没有,如果派生类中的 形参数量和基类中的不一样多 ,则是对基类的虚函

数的重载 。
对虚函数的重定义也就意味着,当用指向派生类的基类指针调用该虚函数时就会调用基类中的虚函数版本。比如基
类定义virtual void f(int i=1, int j=2){}则派生类中必须定义带有两个形参的函数f才是对基类虚函数f的重定

义, 不然就是函数f的重载版本,比如派生类中定义的void f(),void f(int i),void f(int i=2)都是对函数f

的重载,不是对f的重定义。而void f(int i, int j),void f( int i, int j=3),void f(int i=4, int j=5)都

是对虚函数f的重定义。
12.如果虚函数形参有默认值,那么派生类中的虚数的形参不论有无默认值,当用指针调用派生类中的虚函数时就

会被基类的默认值覆盖,即派生类的默认值不起作用 。但用派生类的对象调用该函数时,就不会出现这种情况 。
13.当用指向派生类的基类指针调用虚函数时是以基类中的虚函数的形参为标准的,也就是只要调用的形式符合基

类中定义的虚函数的标准就行了。比如基类中定义virtual void f(int i=1,int j=2){}派生类中重定义为void f

(int i, int j=3){}这时如果用派生类的对象调用这个派生类中的 虚函数 f 时必须至少要有一个实参,但是用指

向派生类的基类指针调用该虚函数时就可以不用任何形参就能调用派生类中的这个函数f, 比如语句p->f()就会调

用派生类中的 虚函数版本 。当用指向派生类的基类指针调用虚函数时是以基类中的虚函数的形参为标准的,也就

是只要调用的 形式符合基类中定义的虚函数的标准就行了。
14.析构函数可以是虚函数,但构造函数不能.
15.纯虚函数声明形式为 virtual 类型 函数名(参数列表)=0;注意后面的等于0;
16.如果类至少有一个纯虚函数,则这个类就是抽象的。
17.如果基类只是声明虚函数而不定义虚函数则此虚函数是纯虚函数 . 任何派生类都必须实现纯虚函数的自已的

版本. 如果不实现纯虚函数那么该类也是抽象类。
18.抽象类不能有对象,抽象类只能用作其它类的基类,因为抽象类中的一个或多个函数没有定义,所以不能用抽

象类声明对象,
19.仍然可以用抽象类声明一个指针,这个指针指向派生类对象.
20.如果派生类中未定义虚函数 ,则会使用基类中定义的函数.
21.虚函数虚拟特性是以层次结构的方式来继承的,例如C从B派生而且C中重定义了B中的虚函数,而D又从C

派生且未重定义B中的虚函数,这时声明一个基类指针P,当P指向类D,并调用D中的虚函数时,由于D中未重

定义虚函数他会调用基类中的虚函数版本,这时他会调用类C中的虚函数而不是类B中的虚函数,因为类C比类B

更接近于类D.
#include <iostream>

using namespace std;
class A
{
    public:
    int b;  
        //virtual int b;  //错误,不能把成员变量声明为虚有的
    virtual void f()
    {
        cout<<"继续"<<endl;
    }
    virtual void h(int i=1,int j=2)
    {
        cout<<"继续H"<<endl;
    }
    ~A(){
        cout<<"析构A"<<endl;
    }
};
class B:public A
{
    public:
    int b;
    void f(int i)//重载虚函数f
    {
        cout<<"paif()"<<endl;
    }
    void f(){//在派生类中重定义虚函数f
        cout<<"paixu"<<endl;
    }
    void h(){ //重载虚函数h的版本。注意这里不是对基类虚函数的重定义。
        int b;
        b=5;
        cout<<"B的"<<b<<endl;
    }
    void h(int i,int j=3){
        int b;
        b=j;
        cout<<"paixuH"<<b<<endl;
    }//当基类中的虚函数有默认形参时,派生类中重定义基类中的虚函数的版本必须有相同数量的形参,
     //形参可以有默认值,也可以没有。如果形参数量不一样多则是对虚函数的重载。
    ~B(){
        cout<<"析构B"<<endl;
    }
};
int main(){
    B m;
    A *p=&m;
        //p->b=3/错误,指向派生类的基类指针不能调用派生类中的成员,只能调用基类中的成员,
        //除非该成员是虚函数
    p->f();W//调用派生类中的函数f

        //p->f(4);错误,注意这里不是在调用派生类中带一个形参的f函数,因为带一个参数的f函数不是虚函数,       

      //用指向派生类的基类指针时不会调用派生类中的函数,除非这个函数是虚函数。这里基类中没有定义这种        //带一个形参的f函数,所以这时会出现错误。

    p->A::f(); W//调用基类的虚函数f,可以用作用域运算符使用指向派生类的基类指针调用基类的虚函数

    p->h();//调用派生类中的虚函数版本h,用指向派生类的基类指针调用虚函数时派生类中的虚函数的默认值在这里不起作用。虽然派生类中的虚函数需要一个参数,

             //但这里不给参数仍是调用的派生类的带两个参数的虚函数h,而不是调用派生类中的不带参数的h函数

    m.h(); //调用派生类中不带参数的h函数,如果要用对象调用派生类中带两个形参的h函数,在本例中必须使用一个实参值。
    m.h(1);//调用派生类中带两个形参的h函数,用对象调用派生类中的虚函数时函数的默认值不受基类虚函数默认值的影响
    m.A::h(1);// 调用基类中的虚函数h.
}

相关文章推荐

C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

1.虚函数(impure virtual)   C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。   子类可以重写父类的虚函数实现子类的特殊化。   如...
  • ybhjx
  • ybhjx
  • 2016年06月30日 10:45
  • 2205

C++中虚函数工作原理和(虚)继承类的内存占用大小计算

一、虚函数的工作原理       虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。典型情况下,这一信息具有一种被称为 vptr(virtual table po...

C++ 深入理解 虚继承、多重继承和直接继承

本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承、虚继承与多重继承,几个交叉概念,详细的阐释了继承、虚函数与虚继承的基本概念,深入剖析了继承于虚继承的区别...

C++类有继承时,析构函数必须为虚函数

虚函数与多态一文中讲了虚函数的用法和要点,但少讲了一点,就是虚函数在析构中的用法,本文就是修复一bug的。C++类有继承时,析构函数必须为虚函数。如果不是虚函数,则使用时可能存在内在泄漏的问题。假设我...

c++继承2虚函数

这篇文章谈谈c++中的虚函数,在我看来如果是单纯的通过对象来访问的话,虚函数和一般的函数是没有什么区别的,当然这里没有包括纯虚函数,如果我们只通过对象来访问的话,那么这也就缺少了c++的动态特性了,当...

C++虚继承的概念

http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成...

关于C++虚继承、虚函数继承的几个例子

今天看虚继承以及虚函数继承时写了几个实例来加深一下理解。相关技术原理参见:c++虚继承对象的内存布局 代码1: 没有虚继承的情况下 #include using namespace std; clas...
  • memewry
  • memewry
  • 2012年08月04日 20:58
  • 1602

详谈C++虚函数表那回事(一般继承关系)

C++一般继承关系中虚函数表的深度展现

C++虚函数(2) - 虚函数中的参数默认值

参考下面程序的输出: #include using namespace std; class Base { public: virtual void fun ( int x = 0 ) ...
  • shltsh
  • shltsh
  • 2015年05月26日 00:23
  • 858

虚函数在对象中的内存布局

典型地,C++通过虚函数实现多态性。多态性的定义:“无论发送消息的对象属于什么类,他们均发送具有相同形式的消息,对消息的处理方式可能随接受消息的对象而变。”具体地说,“在某个基类上建立起来的类的层次结...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++继承中虚函数的使用
举报原因:
原因补充:

(最多只允许输入30个字)