??個人主頁:Quitecoder
??專欄:c++筆記倉
朋友們大家好,本節(jié)內(nèi)容來到類和對象第二篇,本篇文章會帶領大家了解this指針
1.this指針
1.1this指針的引出
首先我們定義一個日期類date:
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2;
d1.Init(2005, 6, 23);
d2.Init(2024, 3, 25);
d1.Print();
d2.Print();
return 0;
}
我們來思考這么一個問題:
Date類中有 Init 與 Print 兩個成員函數(shù),函數(shù)體中沒有關(guān)于不同對象的區(qū)分,也就是說,d1和d2調(diào)用的是同一個函數(shù),那當d1調(diào)用 Init 函數(shù)時,該函數(shù)是如何知道應該設置d1對象,而不是設置d2對象呢
首先思考,這里打印函數(shù),訪問的變量是哪里的?
void Print()
{
cout << _year << "-" << _month << "-" << _day <<endl;
}
這里訪問的是private聲明下的嗎?
private:
int _year;
int _month;
int _day;
并不是,因為這里只是聲明,并沒有開辟空間,真正訪問的是實例化的d1,d2
在
private
部分聲明的變量_year
、_month
、_day
等,在類中只是進行了聲明,實際上并沒有為它們分配內(nèi)存空間。**內(nèi)存空間是在創(chuàng)建類的實例(也就是對象)**時為這些成員變量分配的。每個對象都有自己獨立的一套成員變量,占用各自的內(nèi)存空間
因此,當成員函數(shù)
Print()
通過this
指針(隱式指向當前對象)訪問這些成員變量時,它實際上訪問的是調(diào)用這個成員函數(shù)的那個==特定對象(實例)==的成員變量。每個對象的_year
、_month
和_day
都存儲在各自獨立的內(nèi)存區(qū)域中,這些內(nèi)存區(qū)域是在對象被創(chuàng)建時隨對象一起分配的
那么我d1,d2如何找到這兩個函數(shù)呢?
這里就與隱含的this
指針有關(guān)了
this指針是面向?qū)ο缶幊陶Z言中的一個特殊指針,它指向調(diào)用成員函數(shù)的那個對象。通過this指針,成員函數(shù)可以訪問調(diào)用它的那個對象的成員變量和成員函數(shù)。this指針是隱式傳遞給成員函數(shù)的,是成員函數(shù)的一個隱含參數(shù)
可以理解為,編譯器處理后處理為上述的樣子,調(diào)用的地方,編譯器也會處理:
它會把調(diào)用對象當做形參進行傳遞
這里我們也能知道,為什么d1訪問能打印d1,d2訪問能打印d2
這個東西我們并不陌生,在前面數(shù)據(jù)結(jié)構(gòu)中我們也有學過:
1.2this指針的特性
- this指針是“成員函數(shù)”第一個隱含的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞
- this指針的類型:類類型* const,(
Date* const this
)即成員函數(shù)中,不能給this指針賦值,但是this指向的內(nèi)容可以被改變![]()
特點:
-
在形參和實參的位置,我們不能顯示寫出來
- 在函數(shù)內(nèi)部可以使用
1.3思考題
一,this指針是存在哪里的?
不同的數(shù)據(jù)是存儲在不同的區(qū)域的,思考一下this指針是存在哪個區(qū)域的呢?
const int i = 0;
int j = 1;
cout << &i << endl;
cout << &j << endl;
c++中,const定義的變量是存儲在棧中的,我們可以打印它們的地址:
發(fā)現(xiàn)是相鄰的
const int i = 0;
int j = 1;
const char* p = "abcdefg";
cout << &i << endl;
cout << &j << endl;
cout << &p << endl;
cout << (void*)p << endl;
在C++中,變量和數(shù)據(jù)的存儲位置分為幾個區(qū)域,主要包括棧(Stack)、堆(Heap)、全局/靜態(tài)存儲區(qū)(Global/Static Area)和常量區(qū)(Constant Pool)。具體到提供的代碼示例中的變量,它們的存儲位置如下:
-
const int i = 0;
-
i
是一個常量整型變量。在C++中,const
修飾的局部變量默認存儲在棧上,但是編譯器優(yōu)化可能會將其存儲在程序的只讀數(shù)據(jù)段中(常量區(qū)),尤其是當它被視為編譯時常量時。然而,取地址操作&i
表明i
必須在內(nèi)存中有實際的存儲位置,所以它很可能位于棧上,除非進行了特殊的優(yōu)化
-
-
int j = 1;
-
j
是一個非const
局部變量,存儲在棧上。棧用于存儲局部變量和函數(shù)調(diào)用的上下文
-
-
const char* p = "abcdefg";
- 這里
p
是一個指針,指向一個字符串常量。字符串常量"abcdefg"
存儲在常量區(qū)(也稱為字符串字面量區(qū)或只讀數(shù)據(jù)段),這是因為字符串字面量在程序的整個生命周期內(nèi)都不應被修改。而指針p
本身(即存儲字符串地址的變量)作為局部變量,存儲在棧上
- 這里
-
i
(取決于編譯器優(yōu)化)和j
存儲在棧上。 - 字符串常量
"abcdefg"
存儲在常量區(qū)。 - 指針
p
(存儲字符串常量的地址)存儲在棧上。
在上述的講解后,我們能夠推出this指針的存儲位置:this是一個形參,它指向調(diào)用該成員函數(shù)的對象,this指針在成員函數(shù)調(diào)用時需要被快速訪問并用于訪問對象的成員,所以我們推測它存儲在棧上
為了提高訪問速度,某些編譯器可能選擇將this指針存儲在某個寄存器中,尤其是在成員函數(shù)調(diào)用時。這實際上可以減少內(nèi)存訪問次數(shù),從而提高程序的執(zhí)行效率,寄存器是CPU內(nèi)部的極小量存儲器,具有非常高的數(shù)據(jù)訪問速度
二,判斷下面程序的運行結(jié)果(this能否是空指針?)
class A
{
public:
void PrintA()
{
cout << "PrintA()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
我們發(fā)現(xiàn)它是可以正常運行的,我們接下來簡單分析一下
盡管p被初始化為nullptr,指向A類型對象的指針p是空的,但PrintA()函數(shù)只是打印一條消息,沒有訪問任何對象的成員變量。這種特殊情況下,代碼可運行,主要是因為成員函數(shù)的調(diào)用并沒有實際依賴于this指針指向的對象實例的狀態(tài)
因為PrintA()不訪問對象的任何成員變量,所以這個調(diào)用在技術(shù)上不需要訪問通過this指針指示的內(nèi)存地址。因此,對于這種不訪問任何成員變量的成員函數(shù),通過nullptr調(diào)用可能不會導致運行時錯誤
簡單來說,
void PrintA()
{
cout << "PrintA()" << endl;
}
這串代碼傳遞空指針并沒有任何影響
接下來看下面的代碼:
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
這串代碼運行異常,因為這里訪問a是通過this->_a
來實現(xiàn)的
1.4C語言和C++實現(xiàn)Stack的對比
c語言實現(xiàn)
void StackInit(Stack* ps)
{
assert(ps);
ps->array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == ps->array)
{
assert(0);
return;
}
ps->capacity = 3;
ps->size = 0;
}
void StackDestroy(Stack* ps)
{
assert(ps);
if (ps->array)
{
free(ps->array);
ps->array = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
void CheckCapacity(Stack* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity * 2;
DataType* temp = (DataType*)realloc(ps->array,
newcapacity * sizeof(DataType));
if (temp == NULL)
{
perror("realloc申請空間失敗!!!");
return;
}
ps->array = temp;
ps->capacity = newcapacity;
}
}
void StackPush(Stack* ps, DataType data)
{
assert(ps);
CheckCapacity(ps);
ps->array[ps->size] = data;
ps->size++;
}
int StackEmpty(Stack* ps)
{
assert(ps);
return 0 == ps->size;
}
void StackPop(Stack* ps)
{
if (StackEmpty(ps))
return;
ps->size--;
}
DataType StackTop(Stack* ps)
{
assert(!StackEmpty(ps));
return ps->array[ps->size - 1];
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->size;
}
int main()
{
Stack s;
StackInit(&s);
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
printf("%d\n", StackTop(&s));
printf("%d\n", StackSize(&s));
StackPop(&s);
StackPop(&s);
printf("%d\n", StackTop(&s));
printf("%d\n", StackSize(&s));
StackDestroy(&s);
return 0;
}
在用C語言實現(xiàn)時,Stack相關(guān)操作函數(shù)有以下共性:
- 每個函數(shù)的第一個參數(shù)都是Stack*
- 函數(shù)中必須要對第一個參數(shù)檢測,因為該參數(shù)可能會為NULL
- 函數(shù)中都是通過Stack*參數(shù)操作棧的
- 調(diào)用時必須傳遞Stack結(jié)構(gòu)體變量的地址
結(jié)構(gòu)體中只能定義存放數(shù)據(jù)的結(jié)構(gòu),操作數(shù)據(jù)的方法不能放在結(jié)構(gòu)體中,即數(shù)據(jù)和操作數(shù)據(jù)的方式是分離開的
c++實現(xiàn):
typedef struct Stack
{
DataType* array;
int capacity;
int size;
}Stack;
typedef int DataType;
class Stack
{
public:
void Init()
{
_array = (DataType*)malloc(sizeof(DataType) * 3);
if (NULL == _array)
{
perror("malloc申請空間失敗!!!");
return;
}
_capacity = 3;
_size = 0;
}
void Push(DataType data)
{
CheckCapacity();
_array[_size] = data;
_size++;
}
void Pop()
{
if (Empty())
return;
_size--;
}
DataType Top() { return _array[_size - 1]; }
int Empty() { return 0 == _size; }
int Size() { return _size; }
void Destroy()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
void CheckCapacity()
{
if (_size == _capacity)
{
int newcapacity = _capacity * 2;
DataType* temp = (DataType*)realloc(_array, newcapacity *
sizeof(DataType));
if (temp == NULL)
{
perror("realloc申請空間失敗!!!");
return;
}
_array = temp;
_capacity = newcapacity;
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
int main()
{
Stack s;
s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
printf("%d\n", s.Top());
printf("%d\n", s.Size());
s.Pop();
s.Pop();
printf("%d\n", s.Top());
printf("%d\n", s.Size());
s.Destroy();
return 0;
}
C++中通過類可以將數(shù)據(jù)以及數(shù)據(jù)的方法進行完美結(jié)合,通過訪問權(quán)限可以控制那些方法在類外可以被調(diào)用,即封裝,在使用時就像使用自己的成員一樣,更符合人類對一件事物的認知。而且每個方法不需要傳遞Stack*的參數(shù)了,編譯器編譯之后該參數(shù)會自動還原,即C++中
Stack *
參數(shù)是編譯器維護的,C語言中需用用戶自己維護文章來源:http://www.zghlxwxcb.cn/news/detail-847267.html
感謝大家閱讀?。?!后續(xù)給大家?guī)砦鰳?gòu)函數(shù)和構(gòu)造函數(shù)有關(guān)內(nèi)容!文章來源地址http://www.zghlxwxcb.cn/news/detail-847267.html
到了這里,關(guān)于【c++】類和對象(二)this指針的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!