[英]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()
的意義上講,它與“早期綁定”沒有任何關系。
您的代碼具有兩個不同的對象d
和b
,並且在到達p->fun();
時, d
的構造函數已完全完成p->fun();
線。
詳細:
main
。 Derived
創建對象d
。 Derived::Derived()
所做的第一件事是通過調用默認的構造函數Base::Base()
來創建基類子對象。 Base::Base()
的主體調用fun()
。 由於我們尚未進入Derived::Derived()
構造函數的主體,因此此虛擬查找將調用Base::fun()
。 Base::Base()
完成。 Derived::Derived()
主體執行並完成。 main
,通過將指向d
的指針傳遞給構造函數Base::Base(Base*)
來創建對象b
。 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.