C++中空指針訪問(wèn)成員函數(shù)的實(shí)現(xiàn)
一、核心結(jié)論先明確
空指針(nullptr/NULL)可以調(diào)用類(lèi)的非靜態(tài)成員函數(shù),但有一個(gè)嚴(yán)格前提:
函數(shù)內(nèi)部沒(méi)有通過(guò)this指針訪問(wèn)任何非靜態(tài)成員變量 / 調(diào)用依賴(lài)成員變量的函數(shù)。
一旦訪問(wèn)成員變量,就會(huì)觸發(fā) “空指針解引用”,程序直接崩潰(段錯(cuò)誤)。
二、代碼示例:空指針調(diào)用成員函數(shù)的兩種情況
- 能正常調(diào)用的情況(函數(shù)內(nèi)不訪問(wèn)成員變量)
#include <iostream>
using namespace std;
class Person {
public:
int age; // 非靜態(tài)成員變量
// 情況1:成員函數(shù)僅打印固定內(nèi)容,不訪問(wèn)任何成員變量
void showHello() {
cout << "Hello, this is a member function!" << endl;
}
// 情況2:靜態(tài)成員函數(shù)(無(wú)this指針,和空指針無(wú)關(guān))
static void showStatic() {
cout << "Hello, this is a static member function!" << endl;
}
};
int main() {
// 定義空指針
Person* p = nullptr;
// 1. 空指針調(diào)用不訪問(wèn)成員變量的非靜態(tài)成員函數(shù) → 正常執(zhí)行
p->showHello();
// 2. 空指針調(diào)用靜態(tài)成員函數(shù) → 正常執(zhí)行(靜態(tài)函數(shù)無(wú)this)
p->showStatic();
// 更規(guī)范的寫(xiě)法:Person::showStatic(); (靜態(tài)函數(shù)優(yōu)先用類(lèi)名調(diào)用)
return 0;
}
- 調(diào)用后崩潰的情況(函數(shù)內(nèi)訪問(wèn)成員變量)
#include <iostream>
using namespace std;
class Person {
public:
int age;
// 函數(shù)內(nèi)訪問(wèn)成員變量(會(huì)通過(guò)this->age訪問(wèn))
void showAge() {
cout << "Age: " << this->age << endl; // 等價(jià)于 cout << age;
}
};
int main() {
Person* p = nullptr;
p->showAge(); // 運(yùn)行崩潰!空指針解引用
return 0;
}
崩潰原因:
調(diào)用p->showAge()時(shí),編譯器會(huì)把p(空指針)作為this指針傳入函數(shù),函數(shù)內(nèi)this->age等價(jià)于(nullptr).age—— 對(duì)空指針解引用,觸發(fā)內(nèi)存訪問(wèn)錯(cuò)誤(段錯(cuò)誤)。
三、底層原因:成員函數(shù)的存儲(chǔ)與 this 指針
要理解這個(gè)現(xiàn)象,必須回到 C++ 對(duì)象模型和this指針的本質(zhì):
- 成員函數(shù)的存儲(chǔ):所有非靜態(tài)成員函數(shù)都存儲(chǔ)在代碼段(全局共享),不屬于任何對(duì)象,對(duì)象的內(nèi)存中只存成員變量;
- 調(diào)用成員函數(shù)的本質(zhì):p->func()的邏輯是:
- 找到Person類(lèi)的func函數(shù)在代碼段的地址;
- 把p的地址作為this指針傳入func;
執(zhí)行func的代碼。
空指針調(diào)用的關(guān)鍵:
- 如果func內(nèi)不使用this指針(不訪問(wèn)成員變量),僅執(zhí)行固定邏輯,那么即使this是空指針,也能正常執(zhí)行(因?yàn)闆](méi)解引用this);
- 如果func內(nèi)使用this指針(訪問(wèn)成員變量),就會(huì)對(duì)空指針解引用,觸發(fā)崩潰。
四、易踩坑的細(xì)節(jié)補(bǔ)充
const 成員函數(shù)也不例外:
const成員函數(shù)只是把this限定為const Person const,但如果this是空指針,訪問(wèn)成員變量依然崩潰:
void showAge() const {
cout << age << endl; // 崩潰!this是空指針,this->age非法
}
空指針調(diào)用虛函數(shù)的特殊情況:
虛函數(shù)調(diào)用依賴(lài)this指針指向的虛表指針(vptr),空指針沒(méi)有 vptr,因此調(diào)用虛函數(shù)會(huì)直接崩潰(無(wú)論是否訪問(wèn)成員變量):
class Person {
public:
virtual void showHello() { // 虛函數(shù)
cout << "Hello" << endl;
}
};
int main() {
Person* p = nullptr;
p->showHello(); // 直接崩潰!需要訪問(wèn)this->vptr
return 0;
}
語(yǔ)法合法但邏輯非法:
空指針調(diào)用成員函數(shù)在語(yǔ)法上是允許的(編譯器不報(bào)錯(cuò)),但邏輯上是錯(cuò)誤的 —— 除非你明確知道函數(shù)內(nèi)不訪問(wèn)成員變量,否則絕對(duì)不要這么寫(xiě)。
總結(jié)
空指針可以調(diào)用非靜態(tài)、非虛、且不訪問(wèn)成員變量的成員函數(shù)(語(yǔ)法合法,運(yùn)行不崩潰);
空指針調(diào)用訪問(wèn)成員變量的成員函數(shù) / 虛函數(shù)會(huì)崩潰(本質(zhì)是對(duì)空指針解引用);
核心原因:成員函數(shù)存在于代碼段,調(diào)用僅需傳入this指針;是否崩潰取決于是否使用this指針訪問(wèn)內(nèi)存。
開(kāi)發(fā)建議:永遠(yuǎn)不要用空指針調(diào)用成員函數(shù)!即使語(yǔ)法上能運(yùn)行,也是不規(guī)范、易出錯(cuò)的寫(xiě)法,可在函數(shù)開(kāi)頭加assert(this != nullptr)做校驗(yàn),提前暴露問(wèn)題。
到此這篇關(guān)于C++中空指針訪問(wèn)成員函數(shù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 空指針訪問(wèn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++求解二叉樹(shù)的下一個(gè)結(jié)點(diǎn)問(wèn)題
本文將通過(guò)C++求解以下問(wèn)題:給定一個(gè)二叉樹(shù)其中的一個(gè)結(jié)點(diǎn),請(qǐng)找出中序遍歷順序的下一個(gè)結(jié)點(diǎn)并且返回。文中示例代碼講解詳細(xì),感興趣的可以了解一下2022-04-04
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)與算法之圖的遍歷(二)
這篇文章主要是介紹了利用廣度優(yōu)先算法實(shí)現(xiàn)圖的遍歷,文中利用圖文詳細(xì)的介紹了實(shí)現(xiàn)步驟,對(duì)我們學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法有一定的幫助,需要的朋友可以參考一下2021-12-12
詳解C語(yǔ)言解決經(jīng)典問(wèn)題之兔子產(chǎn)子
有一對(duì)兔子,從出生后的第 3 個(gè)月起每個(gè)月都生一對(duì)兔子。小兔子長(zhǎng)到第 3 個(gè)月后每個(gè)月又生一對(duì)兔子,假設(shè)所有的兔子都不死,問(wèn) 30 個(gè)月內(nèi)每個(gè)月的兔子總數(shù)為多少?本文將用C語(yǔ)言解決這一經(jīng)典問(wèn)題,需要的可以參考一下2022-03-03
C語(yǔ)言如何利用異或進(jìn)行兩個(gè)值的交換詳解
最近在工作中遇到了兩個(gè)值交換的需求,發(fā)現(xiàn)自己對(duì)異或有些忘記,所以索性寫(xiě)出來(lái),方便以后需要的時(shí)候參考學(xué)習(xí),下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言如何利用異或進(jìn)行兩個(gè)值的交換的相關(guān)資料,需要的朋友可以參考下。2017-09-09
C++ 創(chuàng)建桌面快捷方式 開(kāi)始菜單的實(shí)現(xiàn)代碼
這篇文章介紹了C++ 創(chuàng)建桌面快捷方式,開(kāi)始菜單的實(shí)現(xiàn)代碼,需要的朋友可以參考一下2013-06-06
C++對(duì)string進(jìn)行大小寫(xiě)轉(zhuǎn)換操作方法
這篇文章主要介紹了C++對(duì)string進(jìn)行大小寫(xiě)轉(zhuǎn)換操作方法,本文通過(guò)兩種方法結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02

