這篇博客開始總結《深度探索C++物件模型》的第四章,即C++中成員函式(除了建構式和解構式)相關的內容,介紹了C++類中的成員函式,包括常規成員函式、虛函式、靜態函式,
其實我一直猶豫要不要寫這一篇,因為在書中講到多重繼承下的虛函式時只是說實作很復雜,并沒有具體講有什么樣的實作,我個人也因為技術力的原因沒辦法總結出g++實作的多重繼承下的虛函式實作,只能通過除錯軟體管中窺豹,但我最侄訓是決定,權當給自己鞏固的機會,
C++類中各種成員函式
非靜態(非static)、非虛(非virtual)成員函式
這是最常規的一種成員函式,必須通過其系結的物件或指標呼叫,
// test20.cpp
#include <cstdio>
class Test20 {
public:
explicit
Test20(int i)
: m_i(i)
{}
int getInt() const { return m_i; }
void setInt(int i) { m_i = i; }
private:
int m_i;
};
int main() {
Test20 t20(1);
std::printf("%d\n", t20.getInt());
t20.setInt(2);
std::printf("%d\n", t20.getInt());
Test20* pt20 = &t20;
pt20->setInt(3);
std::printf("%d\n", t20.getInt());
}
// Output:
// 1
// 2
// 3
實際上,編譯器會將這種成員函式改寫為非成員函式,并在函式的引數串列前面加一個型別為Test20* const的形式引數(如果成員函式加了const關鍵詞則形參型別為const Test20* const),引數名為this,而且,在成員函式內所有對于該類所有的成員函式、成員變數的操作都通過this指標來進行,以Test20::setInt為例,改寫之后就變成了:
void setInt(Test20* const this, int i) {
this->m_i = i;
}
同時,為了放置名字沖突,編譯器還會對函式的名字進行修飾,包括在前面添加其命名空間資訊,其后添加引數資訊等,這里就不詳細展開了,
虛函式
為了支持多型,C++引入了虛函式這一概念,對于虛函式的呼叫會轉化為對于虛函式表中某一表項內填寫的函式指標的呼叫,
如下面的呼叫:
ptr->virtalFunc();
實際上會轉化為:
// C++偽碼
(ptr->vptr[1])(ptr);
其中ptr為指向某一物件的指標,virtualFunc為物件的某個virtual的成員函式,vptr為其虛函式表,1為virtualFunc這個虛函式在虛函式表中的下標,
注意,只有使用指標和參考呼叫虛函式時才會表現出多型性(也就是從虛函式表中取函式的實際地址然后呼叫),而使用物件呼叫一個虛函式,即便其被宣告為一個虛函式,也只是被當作常規函式呼叫,不會展現多型性(也就是不會從虛函式表中取得實際函式地址),
// test21.cpp
#include <cstdio>
class Test21 {
public:
explicit
Test21(int i)
: m_i(i)
{}
virtual
int getInt() { return m_i; }
private:
int m_i;
};
void call(Test21& t21) {
int i = t21.getInt();
}
int main() {
Test21 t21(1);
int i = t21.getInt();
call(t21);
Test21* pt21 = &t21;
i = pt21->getInt();
}


可以看到在main函式中使用t21這個物件呼叫虛函式getInt()直接call了函式真實地址,沒有多型性;而使用pt21這個指標呼叫虛函式則從虛表中取出了虛函式的地址,然后call這個地址,會有多型性,而在call函式中,使用參考t21也時從虛表中取得的虛函式地址,
關于繼承體系下的虛函式會在下個博客講,
靜態成員函式
對于靜態成員函式,編譯器不會改變其形參串列,也就是說不會傳入一個名為this的、指向類的一個實體的指標,所以靜態成員函式一般通過類名直接呼叫,或是使用類的實體物件/指標/參考呼叫,但和使用類名直接呼叫不會有區別,也不會傳入this指標,
而且因為靜態函式不傳入this指標,所以靜態函式內不能直接讀寫非靜態成員變數和非靜態成員函式(因為不知道其系結的物件),同時,靜態函式也不能定義為const,因為其他成員函式定義為const時為了限定this指標,而靜態成員函式不傳入this指標,定義為const也就沒有意義,
// test22.cpp
#include <cstdio>
class Test22 {
public:
Test22(int i)
: m_i(i)
{}
static
void staticMethod() {
std::printf("static member %d\n", s_i); // OK
// std::printf("commom member %d\n", m_i); // ERROR
anotherStaticMethod(); // OK
// commomMethod(); // ERROR
}
static
void anotherStaticMethod() {
std::printf("Test::anotherStaticMethod()\n");
}
void commomMethod() {
std::printf("Test22::commomMethod()\n");
}
private:
int m_i;
static const int s_i;
};
const int Test22::s_i = 2;
int main() {
Test22 t22(1);
t22.staticMethod(); //OK
Test22::staticMethod(); // OK
}
// Output
// static member 2
// Test::anotherStaticMethod()
// static member 2
// Test::anotherStaticMethod()
這篇博客就先總結到這里,關于虛函式的詳細實作和成員函式指標等內容就放到后續的博客中吧,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/523849.html
標籤:C++
