這是我的代碼
class BaseClass
{
public:
BaseClass() {}
void init(const int object) { cout<<"BaseClass::init"<<endl; }
void run(const int object) { cout<<"BaseClass::run calls =>"; init(object); }
};
class Derived : public BaseClass {
public:
Derived() {}
void init(const int object) { cout<<"Derived::init"<<endl; }
};
int main() {
BaseClass b;
b.init('c');
b.run('c');
Derived d;
d.init(5); // Calls Derived::init
d.run(5); // Calls Base::init. **I expected it to call Derived::init**
}
這是生成的輸出
BaseClass::init
BaseClass::run calls =>BaseClass::init
Derived::init
BaseClass::run calls =>BaseClass::init
通過呼叫 d.run(5),為什么呼叫 "BaseClass::init" 而不是 "BaseClass::init" ?我雖然只有在通過指標呼叫時才需要虛函式。
保持這種行為的原因是什么?
uj5u.com熱心網友回復:
為什么呼叫 "BaseClass::init" 而不是 "BaseClass::init" ?
因為init是非虛成員函式。為了達到預期的效果,你需要制作init一個虛擬成員函式,如下所示:
class BaseClass
{
public:
BaseClass() {}
//NOTE THE VIRTUAL KEYWORD HERE
virtual void init(const int object) { cout<<"BaseClass::init"<<endl; }
//other member function here as before
};
演示
我雖然只有在通過指標呼叫時才需要虛函式。
注意陳述句init(object);相當于寫
this->init(object); //here `this` is a pointer
當你寫道:
d.run(5);
在上面的陳述句中,首先將 object 的地址d作為第一個引數隱式傳遞給member function的隱式 引數。這個隱式引數的型別是,這是由于派生到基轉換而發生的。現在,呼叫等效于. 但由于是一個非虛擬成員函式,呼叫在編譯時決議,這意味著基類將被呼叫。thisrunthisBaseClass*init(object);this->init(object);initinit
基本上,當使用派生類物件呼叫成員函式時,編譯器首先查看該成員是否存在于派生類中。如果沒有,它開始沿著繼承鏈向上走并檢查該成員是否已在任何父類中定義。它使用它找到的第一個。這意味著對于呼叫,在派生類內部開始d.run(5)搜索名為的成員函式。run但是由于派生類內部沒有命名函式run,編譯器會查看直接基類BaseClass并找到名為 的成員函式run。所以它停止搜索并使用這個找到的run成員函式。正如我所說,在你的例子中,init是非虛擬的,因此呼叫在編譯時決議到基類run。
另一方面,如果我們將init其設為虛成員函式,則此呼叫將在運行時init決議,這意味著將呼叫派生類。
uj5u.com熱心網友回復:
我雖然只有在通過指標呼叫時才需要虛函式。
不,那不對。當您想要根據物件的動態型別啟用呼叫它時,您需要將函式設為虛擬。一旦你有一個虛函式,你就可以使用指標或參考來使用虛擬調度。
當您想要覆寫它時,您需要將方法設為虛擬。由于BaseClass::run沒有被覆寫,Derived::run因此沒有虛擬調度和呼叫呼叫init。如果要啟用虛擬調度,則需要將其宣告為虛擬的。BaseClass::runBaseClass::initBaseClass::init
此外,請考慮您的代碼等效于:
void run(const int object) {
cout<<"BaseClass::run calls =>";
this->init(object);
}
this是BaseClass*,因此BaseClass::init被稱為。您是init通過指標呼叫的,但這并不重要,因為init它不是虛擬的。如果你想init根據物件的動態型別來呼叫,這正是virtual它的好處。您在評論中提到的“開銷”并不是真正的開銷,而只是獲得所需行為所需的最低限度。如果您可以更改設計并且不需要運行時多型,則可以查看 CRTP,它是靜態多型的一種形式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/438731.html
