`
隐形的翅膀
  • 浏览: 498202 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

虚基类,虚函数,虚析构函数

 
阅读更多
继承的类的前面加上virtual关键字表示被继承的类是一个虚基类,它的被继承成员在派生类中只保留一个实例

class A
{
public:
    int iValue;
};

class B:public A
{
public:
    void bPrintf(){cout<<"This is class B"<<endl;};
};

class C:public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //错误,不明确的访问
    cout<<d.A::iValue<<endl; //正确
    cout<<d.B::iValue<<endl; //正确
    cout<<d.C::iValue<<endl; //正确
}

从代码中可以看出类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++中就引用了虚基类的概念,来解决这个问题。


class A
{
public:
    int iValue;
};

class B:virtual public A
{
public:
    void bPrintf(){cout<<"This is class B"<<endl;};
};

class C:virtual public A
{
public:
    void cPrintf(){cout<<"This is class C"<<endl;};
};

class D:public B,public C
{
public:
    void dPrintf(){cout<<"This is class D"<<endl;};
};

void main()
{
    D d;
    cout<<d.iValue<<endl; //正确
}

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


声明指向类的指针时,不管引用的实例是哪个类的, 当你调用的时候系统会调用左值那个对象所属类的方法

class A
{
public:
    void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}
答案是funPrint of class A 与 funPrint of class A
因为p是一个A类的指针,所以不管你将p指针指向类A或是类B,最终调用的函数都是类A的funPrint 函数。这就是静态联篇,编译器在编译的时候就已经确定好了

如果我想实现跟据实例的不同来动态决定调用哪个函数呢?这就须要用到 虚函数(也就是动态联篇)

在基类的成员函数前加virtual关键字表示这个函数是一个虚函数,所谓虚函数就是在编译的时候不确定要调用哪个函数,而是动态决定将要调 用哪个函数,要实现虚函数必须派生类的函数名与基类相同,参数名参数类型等也要与基类相同。但派生类中的virtual关键字可以省略,也表 示这是一个虚函数

class A
{
public:
    virtual void funPrint(){cout<<"funPrint of class A"<<endl;};
};

class B:public A
{
public:
    virtual void funPrint(){cout<<"funPrint of class B"<<endl;};
};

void main()
{
    A *p; //定义基类的指针
    A a;
    B b;
    p=&a;
    p->funPrint();
    p=&b;
    p->funPrint();
}
funPrint of class A
funPrint of class B



用C++开发的时候,用来做基类的类的析构函数一般都是虚函数
这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

并不是要把所有类的析构函数都写成虚函数。因为当类里面有虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间。
所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。

class ClxBase
{
public:
    ClxBase() {};
    virtual ~ClxBase() {};

    virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; };
};

class ClxDerived : public ClxBase
{
public:
    ClxDerived() {};
    ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; 

    void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
};



ClxBase *pTest = new ClxDerived;
pTest->DoSomething();
delete pTest;
分享到:
评论

相关推荐

    C++虚基类 虚函数 虚析构函数

    //析构函数做成员函数 }; Base::~Base()//成员函数实现 { cout; } class Derived:public Base { public: Derived(); ~Derived(); private: int *p; }; Derived::Derived() { p=new int(0);//从堆上分配一个int型...

    虚基类 虚函数成员 虚析构函数

    在C++编程语言中,虚基类、虚函数成员和虚析构函数是面向对象编程中的关键概念,它们对于理解和实现多态性至关重要。多态性允许我们编写更灵活、可扩展的代码,使得程序能处理多种不同类型的对象。下面将详细解释这...

    C++中基类的析构函数为什么要用virtual虚析构函数.pdf

    虚函数、虚析构函数都是动态绑定的基础。 在 C++ 中,基类的析构函数如果不使用 virtual 虚析构函数,可能会导致内存泄漏。为什么?因为当我们使用基类指针指向派生类对象时,如果基类的析构函数不是 virtual 的,...

    为何要虚析构函数.docx

    然而,如果析构函数是虚的,将调用子类的析构函数,然后自动调用基类的析构函数,确保所有资源被正确释放。 那么,为什么构造函数不能是虚函数呢?构造函数本身是要初始化对象,虚函数的作用在于通过父类的指针或者...

    虚析构函数示例

    在继承体系中,如果没有声明析构函数为虚的,那么通过基类指针或引用删除派生类对象时,只会调用基类的析构函数,而不会调用派生类的析构函数,这可能会导致资源泄露和其他未定义行为。而声明了虚析构函数后,系统会...

    C++中虚析构函数的作用

    只有当一个类被用来作为基类时,才需要将析构函数设置为虚函数。这是因为虚函数表会增加类的存储空间,所以我们需要根据实际情况进行判断。 虚析构函数在C++中起着非常重要的作用,它可以确保资源的正确释放,避免...

    构造函数不能声明为虚函数,析构函数可以声明为虚函数

    1. **避免内存泄漏**:如果基类指针指向派生类对象,并且基类的析构函数不是虚函数,那么通过基类指针删除派生类对象时,只会调用基类的析构函数。这样就会导致派生类中额外分配的资源没有得到释放,从而引发内存...

    C++中析构函数定义成虚函数的原因.doc

    1. **避免内存泄漏**:如前所述,如果不将基类的析构函数声明为虚函数,则通过基类指针删除派生类对象时,只会调用基类的析构函数,派生类的析构函数不会被调用。这可能导致派生类中的资源未被正确释放,引发内存...

    浅谈C++基类的析构函数为虚函数

     在实现多态时, 当用基类指针操作派生类, 在析构时候防止只析构基类而不析构派生类。 2、例子:  (1)、    #include  using namespace std;  class Base{  public: Base() {};  ~Base() {cout &lt;&...

    C++箴言:避免析构函数调用虚函数

    2. **成员变量未被正确释放**:由于析构函数遵循从派生类到基类的顺序,如果在基类析构函数中调用了虚函数,那么该函数可能会试图访问已经释放的派生类成员变量,从而导致未定义行为。 3. **潜在的内存泄漏**:如果...

    DLL虚基类的方式导出类

    3. **构造函数与析构函数**:由于虚基类可能在多个派生类中被初始化,因此必须确保DLL中的构造函数和析构函数能够正确处理这种情况。通常,基类的构造函数应该是虚的,以便在派生类的构造函数中调用。 4. **内存...

    C++析构函数使用virtual的原因

    如果这些类型还涉及多态,即作为其他类的基类,那么为了确保资源的正确释放,基类的析构函数应当声明为虚函数。 3. **一致性原则** 如果一个类提供了至少一个虚函数,意味着它可能用于多态性场景,此时其析构函数...

    c++虚析构函数及虚函数的详细例子.rar

    总结,C++的虚函数和虚析构函数是实现多态性的重要机制,它们使得基类指针能够透明地操作派生类对象,从而提高了代码的可扩展性和灵活性。通过理解和正确使用这些特性,我们可以编写出更加优雅和健壮的C++程序。

    C++ 课程作业 继承与派生 (motorcycle类设计(虚基类))

    定义一个motorcycle的对象,分别调用run()和stop(),观察构造/析构函数的调用情况。 注意:构造函数和析构函数中均为cout语句,说明哪个构造/析构函数被调用。 该题重点和难点在于构造函数的设计,需考虑怎么给基类及...

    C++虚析构函数、纯虚析构函数

     因为在C++中,当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,则结果是未定义的。运行时比较有代表性的后果是对象的派生部分不会被销毁。然而,基类部分很可能已被销毁,这导致了一...

    详解C++中虚析构函数的作用及其原理分析

    当子类中有指针成员变量时,虚析构函数可以确保在删除基类指针时调用子类的析构函数,以释放子类中的堆内存。 虚析构函数的作用可以总结为以下两点: 1. 如果父类的析构函数不加virtual关键字,当父类的指针指向...

    C++析构函数 快速学习

    不声明为虚析构函数可能导致只调用了基类的析构函数,而忽略了派生类的析构函数,这是一种常见的错误。 此外,析构函数可以重载,就像普通成员函数一样。但要注意,析构函数的重载并不意味着它们会自动被调用,你...

    基类子类虚函数的调用顺序问题

    相反,析构函数则按照自顶向下的顺序调用。这个规则称为构造器的初始化顺序。 然而,当我们在构造函数中调用虚函数时,情况会有所不同。由于构造期间对象尚未完全初始化,此时的虚函数调用遵循“静态绑定”(早绑定...

Global site tag (gtag.js) - Google Analytics