簡體   English   中英

C ++中虛擬函數的構造函數內部的動態綁定

[英]Dynamic binding inside constructor for virtual Function in C++

按照標准,我們知道構造函數總是在內部早先綁定虛擬函數,因為它們對派生類層次結構的缺點尚不完全了解。

在這種情況下,如果在我的基本構造函數中使用了早期綁定,那么我已經將派生對象傳遞給了完全可以接受的基類指針(此處是向上轉換)。 如果使用早期綁定,則虛函數的選擇應基於指針的類型(此處為Base *),而不是指針的內容(指針所指向的對象,因為我們不知道確切的對象)被指出)。 在那種情況下,由於指針類型為Base *,在這兩種情況下,我們只應調用Base類虛函數。 有人可以澄清一下嗎?

我認為這里使用動態綁定,而不是早期綁定。 如果我的理解是錯誤的,請糾正我。

調用base的輸出的第一行完全正確

class Base
    {
        public:
        Base(){
            fun();
        }
        Base(Base *p)
        {
            p->fun();
        }
        virtual void fun()
        {
            cout<<"In Base"<<endl;
        }
    };

    class Derived : public Base
    {
        public:
        void fun()
        {
            cout<<"In Derived"<<endl;
        }
    };

    int main()
    {
        Derived d;
        Base b(&d);
    }

O / P:

In Base
In Derived

構造函數體內的虛擬調用規則適用於當前正在構造的對象,因為該對象尚未被視為任何派生類的對象。 (對於當前被銷毀的對象,也有類似的規則。)從使用編譯時類型(例如語法ClassName::member_func()的意義上講,它與“早期綁定”沒有任何關系。

您的代碼具有兩個不同的對象db ,並且在到達p->fun();時, d的構造函數已完全完成p->fun(); 線。

詳細:

  1. 程序進入main
  2. 使用隱式聲明的默認構造函數Derived創建對象d
  3. Derived::Derived()所做的第一件事是通過調用默認的構造函數Base::Base()來創建基類子對象。
  4. Base::Base()的主體調用fun() 由於我們尚未進入Derived::Derived()構造函數的主體,因此此虛擬查找將調用Base::fun()
  5. Base::Base()完成。
  6. 空的Derived::Derived()主體執行並完成。
  7. 回到main ,通過將指向d的指針傳遞給構造函數Base::Base(Base*)來創建對象b
  8. Base::Base(Base *p)的主體調用p->fun() 由於p是指向d的指針,該d已經是一個完全構造的Derived類型的對象,因此此虛擬查找將調用Derived::fun()

與這個稍有不同的示例相反,我們定義了默認的Derived構造函數,以將this (隱式轉換為Base* )傳遞給Base子對象的構造函數。 (這是有效的,但是如果以其他方式使用指針(例如在Base的初始化程序或Base成員中)可能會帶來風險。)

#include <iostream>
using std::cout;
using std::endl;

class Base
{
public:
    Base(){
        fun();
    }
    Base(Base *p)
    {
        p->fun();
    }
    virtual void fun()
    {
        cout<<"In Base"<<endl;
    }
};

class Derived
{
public:
    Derived() : Base(this) {}
    virtual void fun() override
    {
        cout << "In Derived" << endl;
    }
};

int main()
{
    Derived d;
}

此程序僅在“ Base”中打印,因為現在在Base::Base(Base *p)p確實指向當前正在構造的同一對象。

原因是C ++類是從基類構造到派生類的,並且在對象創建過程完成時會創建完整對象的虛擬調用表。 因此,在上面的代碼摘錄中調用了基類函數。 除非另有規定,否則永遠不要在構造函數中進行虛函數調用。 以下是Bjarne Stroustrup的C ++樣式和技術常見問題解答的摘錄:

  • 在構造函數中,虛擬調用機制被禁用,因為尚未發生從派生類的重寫。 對象是從頭開始構建的,即“派生之前的基礎”。

  • 銷毀是在“派生類先於基類”完成的,因此虛擬函數的行為與構造函數相同:僅使用局部定義-不會調用覆蓋函數,以避免觸及對象的(現已銷毀)派生類部分。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM