柚子快報(bào)激活碼778899分享:c語言 C++類和對象(上)
柚子快報(bào)激活碼778899分享:c語言 C++類和對象(上)
??個(gè)人主頁:落葉
目錄
類的定義
類定義格式
訪問限定符
類域
實(shí)例化
實(shí)例化概念
對象??
this指針
C++和C語?實(shí)現(xiàn)Stack對?
C實(shí)現(xiàn)Stack代碼
C++實(shí)現(xiàn)Stack代碼
類的定義
類定義格式
class為定義類的關(guān)鍵字,Stack為類的名字,{}中為類的主體,注意類定義結(jié)束時(shí)后?分號不能省 略。類體中內(nèi)容稱為類的成員:類中的變量稱為類的屬性或成員變量;類中的函數(shù)稱為類的?法或 者成員函數(shù)。為了區(qū)分成員變量,?般習(xí)慣上成員變量會(huì)加?個(gè)特殊標(biāo)識(shí),如成員變量前?或者后?加_或者m 開頭,注意C++中這個(gè)并不是強(qiáng)制的,只是?些慣例,具體看公司的要求。C++中struct也可以定義類,C++兼容C中struct的?法,同時(shí)struct升級成了類,明顯的變化是 struct中可以定義函數(shù),?般情況下我們還是推薦?class定義類。定義在類?的成員函數(shù)默認(rèn)為inline。
在類里,我們可以定義,成員變量和成員函數(shù),
#include
#include
class Stack
{
//成員函數(shù)
//成員變量
}; // 分號不能省略
下面這個(gè)代碼我們可以看到,棧的成員變量和成員函數(shù)。
#include
#include
class Stack
{
// 成員函數(shù)
//初始化
void Init(int n = 4)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
top = 0;
}
//入棧
void Push(int x)
{
// ...擴(kuò)容
array[top++] = x;
}
//出棧
int Top()
{
assert(top > 0);
return array[top - 1];
}
//銷毀
void Destroy()
{
free(array);
array = nullptr;
top = capacity = 0;
}
// 成員變量
int* array;
size_t capacity;
size_t top;
}; // 分號不能省略
class為定義類的關(guān)鍵字,Stack為類的名字,{}中為類的主體,注意類定義結(jié)束時(shí)后?分號不能省 略。類體中內(nèi)容稱為類的成員:類中的變量稱為類的屬性或成員變量;類中的函數(shù)稱為類的?法或 者成員函數(shù)。C++中struct也可以定義類,C++兼容C中struct的?法,同時(shí)struct升級成了類,明顯的變化是 struct中可以定義函數(shù),?般情況下我們還是推薦?class定義類。定義在類?的成員函數(shù)默認(rèn)為inline。
為了區(qū)分成員變量,?般習(xí)慣上成員變量會(huì)加?個(gè)特殊標(biāo)識(shí),如成員變量前?或者后?加_或者m 開頭,注意C++中這個(gè)并不是強(qiáng)制的,只是?些慣例,具體看公司的要求。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
// 為了區(qū)分成員變量,?般習(xí)慣上成員變量
// 會(huì)加?個(gè)特殊標(biāo)識(shí),如_ 或者 m開頭
int _year; // year_ m_year
int _month;
int _day;
};
int main()
{
Date d;
d.Init(2024, 3, 31);
return 0;
}
C++升級了struct升級成了類。
#include
using namespace std;
// C++升級struct升級成了類
// 1、類??可以定義函數(shù)
// 2、struct名稱就可以代表類型
// C++兼容C中struct的?法
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表類型
struct ListNodeCPP
{
void Init(int x)
{
next = nullptr;
val = x;
}
ListNodeCPP* next;
int val;
};
int main()
{
return 0;
}
訪問限定符
C++?種實(shí)現(xiàn)封裝的?式,?類將對象的屬性與?法結(jié)合在?塊,讓對象更加完善,通過訪問權(quán)限選擇性的將其接?提供給外部的??使?。public修飾的成員在類外可以直接被訪問;protected和private修飾的成員在類外不能直接被訪問,protected和private是?樣的,以后繼承章節(jié)才能體現(xiàn)出他們的區(qū)別。訪問權(quán)限作?域從該訪問限定符出現(xiàn)的位置開始直到下?個(gè)訪問限定符出現(xiàn)時(shí)為?,如果后?沒有訪問限定符,作?域就到?}即類結(jié)束。class定義成員沒有被訪問限定符修飾時(shí)默認(rèn)為private,struct默認(rèn)為public。?般成員變量都會(huì)被限制為private/protected,需要給別?使?的成員函數(shù)會(huì)放為public。
?protected保護(hù)和私有是一樣的。
公有可以在class外使用或修改,私有class里面才能使用或修改。
#include
#include
class Stack
{
public://公有
// 成員函數(shù)
//初始化
void Init(int n = 4)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
top = 0;
}
//入棧
void Push(int x)
{
// ...擴(kuò)容
array[top++] = x;
}
//出棧
int Top()
{
assert(top > 0);
return array[top - 1];
}
//銷毀
void Destroy()
{
free(array);
array = nullptr;
top = capacity = 0;
}
private://私有
// 成員變量
int* array;
size_t capacity;
size_t top;
}; // 分號不能省略
類域
類定義了?個(gè)新的作?域,類的所有成員都在類的作?域中,在類體外定義成員時(shí),需要使? :: 作?域操作符指明成員屬于哪個(gè)類域。類域影響的是編譯的查找規(guī)則,下?程序中Init如果不指定類域Stack,那么編譯器就把Init當(dāng)成全局函數(shù),那么編譯時(shí),找不到array等成員的聲明/定義在哪?,就會(huì)報(bào)錯(cuò)。指定類域Stack,就是知道Init是成員函數(shù),當(dāng)前域找不到的array等成員,就會(huì)到類域中去查找。
//Stack.h文件
#include
using namespace std;
class Stack
{
public:
// 成員函數(shù)
void Init(int n = 4);
private:
// 成員變量
int* array;
size_t capacity;
size_t top;
};
//Stack.cpp文件
// 聲明和定義分離,需要指定類域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
top = 0;
}
//test.cpp文件
int main()
{
Stack st;
st.Init();
return 0;
}
在Stack.cpp文件中,聲明和定義分離,需要使? :: 作?域操作符指明成員屬于哪個(gè)類域,Stack這個(gè)域Stack:: ,這樣就可以找到這個(gè)函數(shù)了。
實(shí)例化
實(shí)例化概念
?類類型在物理內(nèi)存中創(chuàng)建對象的過程,稱為類實(shí)例化出對象。類是對象進(jìn)??種抽象描述,是?個(gè)模型?樣的東西,限定了類有哪些成員變量,這些成員變量只是聲明,沒有分配空間,?類實(shí)例化出對象時(shí),才會(huì)分配空間。?個(gè)類可以實(shí)例化出多個(gè)對象,實(shí)例化出的對象,占?實(shí)際的物理空間,存儲(chǔ)類成員變量。打個(gè)??:類實(shí)例化出對象就像現(xiàn)實(shí)中使?建筑設(shè)計(jì)圖建造出房?,類就像是設(shè)計(jì)圖,設(shè)計(jì)圖規(guī)劃了有多少個(gè)房間,房間??功能等,但是并沒有實(shí)體的建筑存在,也不能住?,?設(shè)計(jì)圖修建出房?,房?才能住?。同樣類就像設(shè)計(jì)圖?樣,不能存儲(chǔ)數(shù)據(jù),實(shí)例化出的對象分配物理內(nèi)存存儲(chǔ)數(shù)據(jù)。
d1和d2是不同的對象,它們的成員變量不是一樣的,就像上面這個(gè)圖一樣,
舉例:
一張房子的圖紙,可以建很多套房子,建成房子后才可以住人。
就像類是房子一樣,要實(shí)例化對象,才可以使用。
成員函數(shù)也不是存在類里的,是存在棧里的,當(dāng)類實(shí)例化出多個(gè)對象后,多個(gè)對象調(diào)用的都是同一個(gè),成員函數(shù)。
using namespace std;
class Date
{
public:
//成員函數(shù)
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()
{
// 類實(shí)例化出對象
// Date類實(shí)例化出對象d1和d2
Date d1;
Date d2;
d1.Init(2024, 8, 6);
d2.Init(2025, 8, 7);
d1.Print();
d2.Print();
return 0;
}
對象??
分析?下類對象中哪些成員呢?類實(shí)例化出的每個(gè)對象,都有獨(dú)?的數(shù)據(jù)空間,所以對象中肯定包含成員變量,那么成員函數(shù)是否包含呢??先函數(shù)被編譯后是?段指令,對象中沒辦法存儲(chǔ),這些指令存儲(chǔ)在?個(gè)單獨(dú)的區(qū)域(代碼段),那么對象中?要存儲(chǔ)的話,只能是成員函數(shù)的指針。再分析?下,對象中是否有存儲(chǔ)指針的必要呢,Date實(shí)例化d1和d2兩個(gè)對象,d1和d2都有各?獨(dú)?的成員變量_year/_month/_day存儲(chǔ)各?的數(shù)據(jù),但是d1和d2的成員函數(shù)Init/Print指針卻是?樣的,存儲(chǔ)在對象中就浪費(fèi)了。如果?Date實(shí)例化100個(gè)對象,那么成員函數(shù)指針就重復(fù)存儲(chǔ)100次,太浪費(fèi)了。這?需要再額外哆嗦?下,其實(shí)函數(shù)指針是不需要存儲(chǔ)的,函數(shù)指針是?個(gè)地址,調(diào)?函數(shù)被編譯成匯編指令[call地址],其實(shí)編譯器在編譯鏈接時(shí),就要找到函數(shù)的地址,不是在運(yùn)?時(shí)找,只有動(dòng)態(tài)多態(tài)是在運(yùn)?時(shí)找,就需要存儲(chǔ)函數(shù)地址,這個(gè)我們以后會(huì)講解。
上?我們分析了對象中只存儲(chǔ)成員變量,C++規(guī)定類實(shí)例化的對象也要符合內(nèi)存對?的規(guī)則。
內(nèi)存對?規(guī)則
第?個(gè)成員在與結(jié)構(gòu)體偏移量為0的地址處。其他成員變量要對?到某個(gè)數(shù)字(對?數(shù))的整數(shù)倍的地址處。注意:對?數(shù) = 編譯器默認(rèn)的?個(gè)對?數(shù) 與 該成員??的較?值。VS中默認(rèn)的對?數(shù)為8結(jié)構(gòu)體總??為:最?對?數(shù)(所有變量類型最?者與默認(rèn)對?參數(shù)取最?)的整數(shù)倍。如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對?到??的最?對?數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體??就是所有最?對?數(shù)(含嵌套結(jié)構(gòu)體的對?數(shù))的整數(shù)倍。
我們來計(jì)算ABC實(shí)例化對象大小是多少。
using namespace std;
// 計(jì)算一下A/B/C實(shí)例化的對象是多大?
class A
{
public:
void Print()
{
cout << _ch << endl;
}
private:
char _ch;
int _i;
};
// 沒有成員變量的類對象,開1byte,占位,不存儲(chǔ)有效數(shù)據(jù)
// 標(biāo)識(shí)對象的存在
class B
{
public:
void Print()
{
//...
}
};
class C
{};
int main()
{
A a;
B b;
C c;
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
cout << sizeof(c) << endl;
return 0;
}
A這個(gè)類,_ch是一個(gè)字節(jié)占用在0的位置,因?yàn)槭前幢稊?shù)來存的,int類型是4字節(jié)只能在4的倍數(shù)開始占用,
_i是4個(gè)字節(jié),從4開始往下占用4個(gè)字節(jié),大小是8個(gè)字節(jié)。
B這個(gè)類, 只有一個(gè)成員函數(shù),但是成員函數(shù)不是存放在類里的,是存放在棧里,所以成員函數(shù)不算類的大小。
沒有成員變量的類對象,開1byte,占位,不存儲(chǔ)有效數(shù)據(jù)標(biāo)識(shí)對象的存在。
所以大小是1字節(jié)。
C這個(gè)類,沒有成員變量和成員函數(shù)
沒有成員變量的類對象,開1byte,占位,不存儲(chǔ)有效數(shù)據(jù)標(biāo)識(shí)對象的存在。
所以大小是1字節(jié)。
上?的程序運(yùn)?后,我們看到?jīng)]有成員變量的B和C類對象的??是1,為什么沒有成員變量還要給1個(gè)字節(jié)呢?因?yàn)槿绻?個(gè)字節(jié)都不給,怎么表?對象存在過呢!所以這?給1字節(jié),純粹是為了占位標(biāo)識(shí)對象存在。
結(jié)果:
我們可以看到結(jié)果確實(shí)是正確的。
this指針
Date類中有 Init 與 Print 兩個(gè)成員函數(shù),函數(shù)體中沒有關(guān)于不同對象的區(qū)分,那當(dāng)d1調(diào)?Init和Print函數(shù)時(shí),該函數(shù)是如何知道應(yīng)該訪問的是d1對象還是d2對象呢?那么這?就要看到C++給了?個(gè)隱含的this指針解決這?的問題 a編譯器編譯后,類的成員函數(shù)默認(rèn)都會(huì)在形參第?個(gè)位置,增加?個(gè)當(dāng)前類類型的指針,叫做this指針。?如Date類的Init的真實(shí)原型為,void Init(Date* const this, int year,int month, int day)類的成員函數(shù)中訪問成員變量,本質(zhì)都是通過this指針訪問的,如Init函數(shù)中給_year賦值,this->_year = year;C++規(guī)定不能在實(shí)參和形參的位置顯?的寫this指針(編譯時(shí)編譯器會(huì)處理),但是可以在函數(shù)體內(nèi)顯?使?this指針。
在類里還有一個(gè)隱藏的this指針。
#include
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
//this->_year
_year = year;
_month = month;
_day = day;
}
//void Print(Date* const this)
void Print()
{
//cout << this->_year << "/" << this->_month << "/" << this->_day << endl;
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 聲明
int _year;
int _month;
int _day;
};
int main()
{
// Date類實(shí)例化出對象d1和d2
Date d1;
Date d2;
//d1.Init(&d1, 2024, 8, 6);
//d2.Init(&d2, 2025, 8, 7);
d1.Init(2024, 8, 6);
d2.Init(2025, 8, 7);
//d1.Print(&d1);
//d2.Print(&d2);
d1.Print();
d2.Print();
return 0;
}
當(dāng)我們傳參數(shù)給成員函數(shù)的時(shí)候,還隱藏傳著實(shí)例化的地址,
當(dāng)然C++規(guī)定不能在實(shí)參和形參的位置顯?的寫this指針(編譯時(shí)編譯器會(huì)處理)。
我們可以看到這個(gè),this接收實(shí)例化后的地址,,但是這個(gè)const修飾地址不能改變,但是可以修改。
當(dāng)成員函數(shù)在使用的時(shí)候,lnit在訪問成員變量的時(shí)候,不是year = year這樣的,而是this->year = year這樣的,
傳過來的是實(shí)例化的地址,通過實(shí)例化地址來訪問成員變量,進(jìn)行賦值,
Print那個(gè)打印函數(shù)也是一樣的,通過實(shí)例化地址進(jìn)行打印。
下?通過兩個(gè)選擇題測試?下前?的知識(shí)學(xué)得如何?
1.下?程序編譯運(yùn)?結(jié)果是()
A、編譯報(bào)錯(cuò) B、運(yùn)?崩潰 C、正常運(yùn)?
#include
using namespace std;
class A
{
public:
void Print()//this接收了p,但是p是空指針
{
//但是這里沒有對空指針進(jìn)行訪問,所以不會(huì)報(bào)錯(cuò)
//這里只是打印
cout << "A::Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;//初始化為空
p->Print();//這里相當(dāng)于把p的地址傳過去,這里雖然寫了p->,但是沒有解引用
return 0;
}
分析:成員函數(shù)在編譯時(shí)確定的,沒有存在對象中,所以這里雖然寫了p->,但是沒有解引用。
所以選擇【C.正常運(yùn)行】
2.下?程序編譯運(yùn)?結(jié)果是()
A、編譯報(bào)錯(cuò)??B、運(yùn)?崩潰? C、正常運(yùn)?
#include
using namespace std;
class A
{
public:
void Print()//this接收了p,p是空指針
{
cout << "A::Print()" << endl;
//這里this->_a對空指針進(jìn)行解引用了
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;//初始化為空
p->Print();//這里相當(dāng)于把p的地址傳過去,這里雖然寫了p->,但是沒有解引用
return 0;
}
分析:成員函數(shù)這里,_a已經(jīng)對空指針解引用了,所以運(yùn)行崩潰。
3. this指針存在內(nèi)存哪個(gè)區(qū)域的 ()
A. 棧??B.堆??C.靜態(tài)區(qū)??D.常量區(qū)??E.對象??
this是一個(gè)隱藏的形參,函數(shù)調(diào)用要建立棧,函數(shù)要存放在棧里,this是一個(gè)隱藏的形參,也是存放在棧里的。
在VS系列編譯器,this通過寄存器ecx傳遞。
C++和C語?實(shí)現(xiàn)Stack對?
?向?qū)ο笕?特性:封裝、繼承、多態(tài),下?的對?我們可以初步了解?下封裝。 通過下?兩份代碼對?,我們發(fā)現(xiàn)C++實(shí)現(xiàn)Stack形態(tài)上還是發(fā)?了挺多的變化,底層和邏輯上沒啥變 化。
C++中數(shù)據(jù)和函數(shù)都放到了類??,通過訪問限定符進(jìn)?了限制,不能再隨意通過對象直接修改數(shù) 據(jù),這是C++封裝的?種體現(xiàn),這個(gè)是最重要的變化。這?的封裝的本質(zhì)是?種更嚴(yán)格規(guī)范的管 理,避免出現(xiàn)亂訪問修改的問題。當(dāng)然封裝不僅僅是這樣的,我們后?還需要不斷的去學(xué)習(xí)。C++中有?些相對?便的語法,?如Init給的缺省參數(shù)會(huì)?便很多,成員函數(shù)每次不需要傳對象地 址,因?yàn)閠his指針隱含的傳遞了,?便了很多,使?類型不再需要typedef?類名就很?便在我們這個(gè)C++??階段實(shí)現(xiàn)的Stack看起來變了很多,但是實(shí)質(zhì)上變化不?。等著我們后?看STL 中的?適配器實(shí)現(xiàn)的Stack,?家再感受C++的魅?。
C實(shí)現(xiàn)Stack代碼
C語言的這個(gè)棧沒有封裝函數(shù),可以自己訪問到結(jié)構(gòu)體成員,進(jìn)行修改。
這種情況很容易出現(xiàn)問題。
#include
#include
#include
#include
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = ps->capacity = 0;
}
void STPush(ST* ps, STDataType x)
{
assert(ps);
// 滿了, 擴(kuò)容
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void STPop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
int main()
{
ST s;
STInit(&s);
STPush(&s, 1);
STPush(&s, 2);
STPush(&s, 3);
STPush(&s, 4);
while (!STEmpty(&s))
{
printf("%d\n", STTop(&s));
STPop(&s);
}
STDestroy(&s);
return 0;
}
C++實(shí)現(xiàn)Stack代碼
向C++這種可以把成員變量進(jìn)行私有,不能外部進(jìn)行修改訪問,只能通過公有成員函數(shù)進(jìn)行訪問修改,這樣比較安全。
#include
#include
using namespace std;
typedef int STDataType;
class Stack
{
public:
// 成員函數(shù)
void Init(int n = 4)
{
_a = (STDataType*)malloc(sizeof(STDataType) * n);
if (nullptr == _a)
{
perror("malloc申請空間失敗");
return;
}
_capacity = n;
_top = 0;
}
void Push(STDataType x)
{
if (_top == _capacity)
{
int newcapacity = _capacity * 2;
STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
--_top;
}
bool Empty()
{
return _top == 0;
}
int Top()
{
assert(_top > 0);
return _a[_top - 1];
}
void Destroy()
{
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
// 成員變量
STDataType* _a;
size_t _capacity;
size_t _top;
};
int main()
{
Stack s;
s.Init();
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
while (!s.Empty())
{
printf("%d\n", s.Top());
s.Pop();
}
s.Destroy();
return 0;
}
柚子快報(bào)激活碼778899分享:c語言 C++類和對象(上)
相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。