考慮以下代碼片段:
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base::constr" << std::endl;
print();
}
virtual ~Base() = default;
void print() const { printImpl(); }
private:
virtual void printImpl() const {
std::cout << "Base::printImpl" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived::constr" << std::endl;
}
private:
void printImpl() const override {
std::cout << "Derived::printImpl" << std::endl;
}
};
int main() {
Base* ptr = new Derived();
ptr->print();
delete ptr;
}
上面的代碼將列印以下內容:
Base::constr
Base::printImpl
Derived::constr
Derived::printImpl
但我不明白為什么可以從基礎函式訪問printImpl 私有print函式。在我的理解this中,隱式傳遞給print函式的指標保存了派生物件的地址,但我認為私有成員函式只能從同一個類的成員函式(和friend函式)中呼叫,在這里,Base類不是同一個類Derived,盡管有is a關系。
uj5u.com熱心網友回復:
首先,正如@Eljay 所說 -printImpl()是類的一種方法,盡管是虛擬的Base。因此,它可以從基類訪問。Derived只是提供了它的不同實作。虛函式的全部意義在于您可以使用基類參考或指標呼叫子類的覆寫。
換句話說,private只考慮子類的訪問;從類的基類中保留某些東西是沒有意義的private:如果基類完全知道方法,那么它必須是基類的方法……虛擬方法。
說了這么多——請注意,當它在基類建構式中被呼叫時,它的Derived版本printImpl()實際上是無法訪問的。print()這是因為在該呼叫期間,構造的 vtable 只是 的Base,所以printImpl指向Base::printImpl。
我認為只能從同一類的成員函式中呼叫私有成員函式
實際上,print()是 的成員Base,它呼叫printImpl()- 的另一個方法Base。
uj5u.com熱心網友回復:
基類的私有函式在派生類中是不可訪問的,但可以被覆寫,因為這兩個是不同的概念。
對于類print的建構式的呼叫,有如下規則:Base
在建構式中,虛擬呼叫機制被禁用,因為尚未發生從派生類的覆寫。物件是從基礎向上構建的,“在派生之前的基礎”。
PS 一般來說,最好避免從建構式或解構式呼叫虛函式
uj5u.com熱心網友回復:
情況1
在這里,我們考慮以下宣告:
Base* ptr = new Derived();
這些是上述陳述句的效果:
步驟 1 )使用默認 ctorDerived在堆上創建型別物件。 Derived::Derived()
步驟 2 ) 在進入默認 ctor 的主體之前Derived::Derived(),編譯器會隱式呼叫默認 ctor Base::Base()。因此我們得到輸出:
Base::constr
Step 3 ) 接下來遇到print()里面的陳述句。Base::Base()這個陳述句相當于寫:
this->print();
因此,print()類的函式Base被呼叫。
步驟 4 ) 在 的print成員函式內部,遇到Base呼叫陳述句。printImpl();這個陳述句相當于寫:
this->printImpl();
但請注意,目前,型別子物件的構建Base正在進行中。這意味著Derived整個物件的一部分尚未構建。因此,即使它printImpl是一個虛擬成員函式并且通過指標進行呼叫,此時虛擬機制也被禁用。因此this->printImpl()呼叫Base::printImpl版本而不是printImpl. 發生這種情況是因為派生類尚未初始化。因此我們得到輸出:
Base::printImpl
步驟 5 ) 最后,Derived::Derived()執行默認 ctor 的主體。因此我們得到輸出:
Derived::constr
Step 6 ) 指標ptr指向Base新創建的派生物件的一部分。
案例2
在這里,我們考慮以下宣告:
ptr->print();
現在,從這個先驅者的檔案中:
當在允許 this 關鍵字的任何背景關系(非靜態成員函式體、成員初始化器串列、默認成員初始化器)中使用非靜態類成員時,隱式
this->會自動添加到名稱之前,從而產生成員訪問運算式(如果成員是虛成員函式,則導致虛函式呼叫)。
所以,當你寫:
ptr->print(); //this is equivalent to writing (*ptr).print();
上面的陳述句相當于寫成:
(*ptr).print();
這意味著指標指向的物件的地址ptr(它只是ptr它自己)被隱式傳遞給成員函式的隱式this引數。print
現在,在print成員函式內部,包含呼叫的陳述句 printImpl()相當于:
this->printImpl();
根據我回答開頭參考的宣告。并且從同一個參考的陳述句中,由于成員printImpl是一個虛成員函式,因此運算式this->printImpl()會導致虛函式呼叫,這意味著它會導致呼叫派生類printImpl函式。因此我們得到輸出:
Derived::printImpl
uj5u.com熱心網友回復:
在您列印的代碼中的原因Base::printImpl是因為它是從Base. 派生類尚未構造,因此所有對虛函式的呼叫都將參考Base的版本。
print構造后,對will akways的呼叫將重定向到派生類的版本。函式是否被標記private, public, protected在這里是無關緊要的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/459986.html
上一篇:在兒童班中復制和移動成語?
下一篇:Ruby/關注點/繼承
