柚子快報邀請碼778899分享:算法 【C++】類和對象(一)
柚子快報邀請碼778899分享:算法 【C++】類和對象(一)
文章目錄
一、類的引入二、類的定義語法
三、類的訪問限定符及封裝訪問限定符`public ``private``protected`訪問限定符說明
面試題:question:C++中struct和class的區(qū)別是什么?
四、類的兩種定義方式五、類的作用域六、面向?qū)ο笕筇匦灾唬悍庋b七、類的實(shí)例化八、類對象模型如何計算類對象的大小回顧:結(jié)構(gòu)體內(nèi)存對齊規(guī)則
類對象的存儲方式
一、類的引入
C語言,結(jié)構(gòu)體中只能定義變量。C++中,結(jié)構(gòu)體內(nèi)可以定義變量也可以定義函數(shù)。
?C語言方式實(shí)現(xiàn)的棧,結(jié)構(gòu)體中只能定義變量?,F(xiàn)在以C++的方式實(shí)現(xiàn),結(jié)構(gòu)體中也可以定義函數(shù)。
typedef int DataType;
struct Stack
{
void Init(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (nullptr == _array)
{
perror("malloc申請空間失敗");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(const DataType& data)
{
// 擴(kuò)容
_array[_size] = data;
++_size;
}
DataType Top()
{
return _array[_size - 1];
}
void Destroy()
{
if (_array)
{
free(_array);
_array = nullptr;
_capacity = 0;
_size = 0;
}
}
DataType* _array;
size_t _capacity;
size_t _size;
};
int main()
{
//我們可以直接用stack來定義一個結(jié)構(gòu)體s。
//C++中s也可以稱作一個對象。
Stack s;
s.Init(10);
s.Push(1);
s.Push(2);
s.Push(3);
cout << s.Top() << endl;
s.Destroy();
return 0;
}
c語言中,我們通過這種方式來建立一個棧的結(jié)構(gòu),c++將結(jié)構(gòu)體升級為了類,類提供了一種封裝數(shù)據(jù)成員(屬性或變量)和成員函數(shù)上面結(jié)構(gòu)體的定義,在C++中更喜歡用class來代替。
二、類的定義
語法
class className
{
// 類體:由成員函數(shù)和成員變量組成
};
// 一定要注意后面的分號
class為定義類的關(guān)鍵字,ClassName為類的名字,{}中為類的主體,注意類定義結(jié)束時后面分號不能省略。
類體中內(nèi)容稱為類的成員:類中的變量稱為類的屬性或成員變量; 類中的函數(shù)稱為類的方法或者成員函數(shù)。
我們可以簡單定義一個日期類:
class Date
{
int year;
int month;
int day;
};
C++習(xí)慣定義成員變量時在其前面加上符號_,比如以下情況:
class Date
{
void Init(int year, int month, int day)
{
year = year;
}
int year;
int month;
int day;
};
為了防止成員變量和成員函數(shù)的形參混淆,我們可以這樣定義:
class Date
{
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
現(xiàn)在我們來調(diào)用這個函數(shù):
class Date
{
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(2005, 6, 23);
return 0;
}
發(fā)現(xiàn)無法調(diào)用該成員函數(shù)
這就引出我們接下來要講的內(nèi)容,類的訪問限定符
三、類的訪問限定符及封裝
訪問限定符
C++實(shí)現(xiàn)封裝的方式:用類將對象的屬性與方法結(jié)合在一塊,讓對象更加完善,通過訪問權(quán)限選 擇性的將其接口提供給外部的用戶使用。
在 C++ 中,訪問限定符(Access Specifiers)定義了類成員(即變量和函數(shù))的訪問權(quán)限。這些限定符有助于實(shí)現(xiàn)封裝和抽象,是面向?qū)ο缶幊痰暮诵母拍钪?。主要的訪問限定符有三種:public、private 和 protected
public
public成員在任何地方都可以被訪問,類的內(nèi)部、外部都可訪問>使用public限定符可以提高代碼的使用率,但是會降低封裝性,因?yàn)橥獠看a可以直接讀寫公開的數(shù)據(jù)成員
private
private成員只能被類的成員函數(shù)和友元函數(shù)訪問,不能被類的外部訪問。使用 private 限定符是封裝的一種體現(xiàn)。通過限制對成員的直接訪問,你可以保護(hù)類內(nèi)部成員的狀態(tài),避免外部代碼隨意修改,從而維護(hù)對象的完整性
protected
protected 成員可以被其所在類的成員函數(shù)、派生類的成員函數(shù)訪問,但是**不能被類的外部直接訪問**protected 在處理繼承時尤其有用,它允許派生類訪問基類中的成員,同時防止了類的外部直接訪問這些成員
訪問限定符說明
1. 訪問權(quán)限作用域從該訪問限定符出現(xiàn)的位置開始直到下一個訪問限定符出現(xiàn)時為止 2. 如果后面沒有訪問限定符,作用域就到}即類結(jié)束。 3. class的默認(rèn)訪問權(quán)限為private,struct為public(因?yàn)閟truct要兼容C)
注意:訪問限定符只在編譯時有用,當(dāng)數(shù)據(jù)映射到內(nèi)存后,沒有任何訪問限定符上的區(qū)別
面試題
?C++中struct和class的區(qū)別是什么?
答:C++兼容C語言,C++中struct可以當(dāng)作結(jié)構(gòu)體使用,也可以當(dāng)作類去使用。在struct當(dāng)類使用的時候,跟class關(guān)鍵字的區(qū)別是默認(rèn)訪問限定符不同。struct的默認(rèn)訪問限定符是public,class的默認(rèn)訪問限定符是private。在繼承和模板參數(shù)列表位置,struct和class也存在區(qū)別。
四、類的兩種定義方式
聲明和定義全部放在類體中,需注意:成員函數(shù)在類中定義,編譯器可能會將其當(dāng)成內(nèi)聯(lián)函數(shù)處理
class stack
{
public:
int* a;
int size;
int capacity;
void Init(int n=4)
{
a = (int*)malloc(sizeof(int) * n);
if (nullptr == a)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
size = 0;
}
};
類聲明放在.h文件中,成員函數(shù)定義放在.cpp文件中,注意:成員函數(shù)名前需要加類名::
stack.h文件
class stack
{
public:
int* a;
int size;
int capacity;
void Init();
};
stack.cpp文件,如果我們直接定義該成員函數(shù)則會報錯
報錯原因是:Init()函數(shù)被編譯器當(dāng)作全局函數(shù),Init()函數(shù)操作Stack類的成員變量被編譯器認(rèn)定是訪問未定義的標(biāo)識符。
這說明,如果程序員不加指定,編譯器則無法找到該函數(shù)的出處,根據(jù)搜索規(guī)則,只會先去局部域找然后去全局域搜索這些變量是否被定義過。所以我們要使用訪問限定符::告訴編譯器該函數(shù)操作的成員變量 屬于 哪一個類域。
void stack::Init(int n=4)
{
a = (int*)malloc(sizeof(int) * n);
if (nullptr == a)
{
perror("malloc申請空間失敗");
return;
}
capacity = n;
size = 0;
}
五、類的作用域
由此我們可知,除了全局域、局部域類作用域也會影響編譯器的搜索規(guī)則 ,比如下面兩個類,棧和隊列都有init和push函數(shù):
class stack {
public:
void init(int n);
void push(int x);
};
class queue {
public:
void init(int n);
void push(int x);
};
如果沒有域,則會有沖突。所以,類本身就是一個域。
六、面向?qū)ο笕筇匦灾唬悍庋b
面向?qū)ο蟮娜筇匦裕悍庋b、繼承、多態(tài)
在類和對象階段,主要是研究類的封裝特性,那什么是封裝呢?
封裝(Encapsulation)是面向?qū)ο缶幊蹋∣OP)中的一個核心概念,它指的是將對象的數(shù)據(jù)(屬性)和操作這些數(shù)據(jù)的方法(行為)捆綁在一起的做法。這種機(jī)制可以防止外部代碼隨意訪問對象內(nèi)部的狀態(tài),從而保護(hù)對象的完整性和安全性。封裝的關(guān)鍵在于提供了一個抽象層,使得對象的使用者不需要知道對象內(nèi)部的復(fù)雜邏輯,只需要通過對象提供的接口(公共方法)來與對象進(jìn)行交互
封裝本質(zhì)上是一種管理,讓用戶更方便使用類。比如:對于電腦這樣一個復(fù)雜的設(shè)備,提供給用戶的就只有開關(guān)機(jī)鍵、通過鍵盤輸入,顯示器,USB插孔等,讓用戶和計算機(jī)進(jìn)行交互,完成日常事務(wù)。但實(shí)際上電腦真正工作的卻是CPU、顯卡、內(nèi)存等一些硬件元件
對于計算機(jī)使用者而言,不用關(guān)心內(nèi)部核心部件,比如主板上線路是如何布局的,CPU內(nèi)部是如何設(shè)計的等,用戶只需要知道,怎么開機(jī)、怎么通過鍵盤和鼠標(biāo)與計算機(jī)進(jìn)行交互即可。因此計算機(jī)廠商在出廠時,在外部套上殼子,將內(nèi)部實(shí)現(xiàn)細(xì)節(jié)隱藏起來,僅僅對外提供開關(guān)機(jī)、鼠標(biāo)以及鍵盤插孔等,讓用戶可以與計算機(jī)進(jìn)行交互即可。
七、類的實(shí)例化
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
思考:這里的int _year、int _month、int _day是聲明還是定義?它是聲明
聲明和定義的本質(zhì)區(qū)別是是否開辟一塊空間
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
return 0;
}
Date d1通過實(shí)例化對象,為該對象的所有成員變量開辟空間,整體定義了該對象的所有成員變量。
我們可以通過對象,對該對象的成員變量進(jìn)行訪問。
int main()
{
Date d1;
d1._year++;
//Date::_year++//不能通過類域限定訪問,因?yàn)樗皇锹暶?,沒有開辟一塊空間
return 0;
}
類是對對象進(jìn)行描述的,是一個模型一樣的東西,限定了類有哪些成員,定義出一個類并沒有分配實(shí)際的內(nèi)存空間來存儲它
類實(shí)例化出對象就像現(xiàn)實(shí)中使用建筑設(shè)計圖建造出房子,類就像是設(shè)計圖,只設(shè)計出需要什么東西,但是并沒有實(shí)體的建筑存在,同樣類也只是一個設(shè)計,實(shí)例化出的對象才能實(shí)際存儲數(shù)據(jù),占用物理空間
八、類對象模型
類中既可以有成員變量,又可以有成員函數(shù),那么一個類的對象中包含了什么?如何計算一個類的大???
如何計算類對象的大小
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
int main()
{
cout << sizeof(d1) << endl;
Date d1;
return 0;
}
思考:上面Date類的大小是多少字節(jié)?
先來回顧以下結(jié)構(gòu)體內(nèi)存對齊規(guī)則
回顧:結(jié)構(gòu)體內(nèi)存對齊規(guī)則
第一個成員在與結(jié)構(gòu)體變量偏移量為0的地址處。 其他成員變量要對齊到對齊數(shù)的整數(shù)倍的地址處。 對齊數(shù) = min(編譯器默認(rèn)的對齊數(shù), 該成員大?。?VS中默認(rèn)的值為8 結(jié)構(gòu)體總大小為最大對齊數(shù)(每個成員變量都有一個對齊數(shù))的整數(shù)倍。 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體對齊到自己的最大對齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對齊數(shù)(含嵌套結(jié)構(gòu)體的對齊數(shù))的整數(shù)倍。
這里存在成員函數(shù)的情況下,類所占字節(jié)大小依然是12,驗(yàn)證測試如下:
我們嘗試調(diào)用以下不同對象的成員函數(shù)
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
}
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.Init(2024, 01, 01);
d2.Init(2025, 01, 01);
return 0;
}
從反匯編代碼中,我們發(fā)現(xiàn)兩個對象分別調(diào)用的成員函數(shù)地址是相同的。
所以類的成員變量、成員函數(shù)到底是如何存儲的呢?
類對象的存儲方式
每個對象中存儲各自的成員變量,成員函數(shù)因?yàn)橐磸?fù)被不同的對象調(diào)用,所以成員函數(shù)存儲在一段公共的區(qū)域。
class A2 {
public:
void f2() {}
};
這個類的大小是多少呢?
在C++中,沒有成員變量的類對象大小占位1字節(jié),不實(shí)際存儲任何數(shù)據(jù),是為了標(biāo)識該對象已經(jīng)被實(shí)例化了。
對于給出的A2類,因?yàn)樗缓幸粋€成員函數(shù),它所占用的內(nèi)存大小應(yīng)該是1字節(jié)
再看下面的代碼示例:將p設(shè)為空指針,p還能調(diào)用函數(shù)嗎?
class fun {
public:
void Print()
{
cout << "被調(diào)用" << endl;
}
};
int main()
{
fun a1;
fun* p1 = &a1;
p1->Print();
fun* p = nullptr;
p->Print();
return 0;
}
測試:
可以調(diào)用的原因:
這里的Print函數(shù)并不在指針指向的空間里面,既不在p1指向的空間里面,也不再p指向的空間里面,p1,p指向的是對象,對象里面沒有函數(shù)的地址,雖然有箭頭,但是并沒有進(jìn)行解引用。
柚子快報邀請碼778899分享:算法 【C++】類和對象(一)
相關(guān)文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。