文章目錄
- 前言
- 面向程序與面向物件
- 類與物件的引入
- 何為類
- 類的定義
- 訪問限定符說明
- 封裝
- 類的實體化
- 類模型
- 1.如何計算類物件大小
- 2.類物件的存盤方式
- this指標
- 面試題
- 1.`this`指標存在哪里?
- 2.this指標可以為空嗎?
前言
在講解了C++的部分基礎后,我們來到了C++的精華部分,類與物件.在本章節中,博主將會帶領大家了解
類的定義和類的使用.
面向程序與面向物件
我們熟悉的C語言是面向程序,關注的是程序,通過分析問題而寫出對應的函式并呼叫來解決問題.
而C++是基于面向物件的,關注的是物件,通過把復雜的事情拆解成物件之間的互動完成.
那他們到底什么區別呢?博主這里不解釋,請繼續玩下面看,大家就自然會明白.
類與物件的引入
在c語言中,我們經常會用到結構體,但是在使用結構體上,C和C++ 卻有一定差異,比如下圖兩張圖:

你會發現,在C++中,如果想定義鏈表的時候,在內部可以直接寫結構體名,而不需要
struct,原因:在c語言中,struct是結構體,但是在c++中,struct卻被升級為類.
何為類
類,即是一種型別,和
int,char等性質一樣,只是類的功能與作用比它們大很多.具體大多少呢?我們往下看.
pp
類的定義
class className
{
// 類體:由成員函式和成員變陣列成
}; // 一定要注意后面的分號
class為關鍵字,用于定義類.classname為類名,即我們定義的類的名字.其中,類里面的函式稱為類的方法或者成員函式
類里面的變數稱為類的屬性或者成員變數
其中,這里博主要先介紹下類訪問限定符:public(公有),protected(保護),private(私有),其中public的作用是無論類內還是類外,都可以訪問類的屬性和方法,后兩者的作用是類外不能訪問,類內可以訪問,至于后兩者區別是什么?博主在后面的章節會詳細介紹.
現在,我們來定義一個日期類
class Date
{
private:
int _year;
int _month;
int _day;
};
注意:類的方法定義有兩種方式,一是宣告與定義一起,二是宣告與定義分離.
第一種:
class Date { public: void print() { cout<<"我們創建的第一個類"<<endl; } };第二種:
class Date { public: void print(); }; void Date:: print() //需要使用 類名 加上 類作用域符號::在函式名前面. { cout<<"我們創建的第一個類"<<endl; }
那這兩種定義方式有什么區別呢?
如果所定義的函式比較短,且沒有回圈和遞回,便可以函式宣告與定義放在一起.
如果所定義的函式比較長,且含有回圈或者遞回,那么函式必須宣告與定義分離.
原因是,如果成員函式在類的內部定義,編譯器會把函式當成行內函式進行展開.如果含有遞回或者回圈,可能會被忽略.
現在我們知道了完整的類定義方法,博主便拋出一個需求:
定義一個計算類, 其有屬性n,代表一個數字n.該類具有兩個方法,一是給_n賦值,一是計算1累加到n的值,然后輸出.
class caculate
{
public:
void outn(int n) //宣告與定義一起
{
_n = n;
}
void sumval(); //宣告與定義分離
private:
int _n;
};
void caculate:: sumval()
{
int ans = 0;
for(int i = 1;i<=_n;i++)
{
ans+=i;
}
cout<<"累加結果為:"<<ans<<endl;
}
訪問限定符說明
-
public修飾的成員在類外可以直接被訪問(已講解)
-
protected和private修飾的成員在類外不能直接被訪問(此處protected和private是類似的)(已講解)
-
訪問權限作用域從該訪問限定符出現的位置開始直到下一個訪問限定符出現時為止
-
class的默認訪問權限為private,struct為public(因為struct要兼容C)
其中第四點的意思是,如果我們在定義類時不寫訪問限定符,那么class類中成員是默認為
private屬性,而struct類中的成員默認public屬性.
問題:C++中struct和class的區別是什么?
解答:C++需要兼容C語言,所以C++中struct可以當成結構體去使用,另外C++中struct還可以用來定義類,
和class是定義類是一樣的,區別是struct的成員默認訪問方式是public,class是的成員默認訪問方式是
private.
封裝
面向物件有三個特點,分別是:封裝,繼承與多型.
博主在這篇文章主要介紹封裝,那么什么是封裝呢?
封裝:將資料和操作資料的方法進行有機結合,隱藏物件的屬性和實作細節,僅對外公開介面來和物件進行互動
其實簡而言之就是 保證資料的安全性. 比如我們上面介紹類的定義時候其實就是封裝特性,利用訪問限定符,限制性的讓某些資料只在某些地方可以訪問.
而封裝的特性其實就好比去看 兵馬俑,學習c語言時候是沒有封裝特性的,也就相當于兵馬俑沒有圍欄,游客可能會去坑里涂畫,造成兵馬俑不安全,但是C++有了該特性,就相當于給兵馬俑修了圍欄,游客只能按照特定的路線去觀看兵馬俑,而不會破壞兵馬俑.
類的實體化
概念: 用類創建物件的程序稱為類的實體化
其中我們創建的類就好像一張圖紙,而我們通過該圖紙建造出來的房子就是物件,而這個建造的程序就稱為 類的實體化
這個圖紙在我們生活中是不占據空間的,但是建造出來的防止確占據很大空間.
這在我們的程式中同樣如此,我們設計的類并不會占據空間,但是通過類實體化出來的空間確占據空間.
類的實體化例子:

通過我們創建的人類—這個可以知道身高體重名字,以及可以說話的抽象類,而在右邊實體出來無數個具有這些功能的物件,就是實體化.
類模型
在看下一知識點前,大家猜猜下面的結果是啥:
class person
{
public:
void say()
{
cout << "我可以說話" << endl;
}
double height;
string name;
double weight;
};
int main()
{
person man1;
man1.height = 122.3;
man1.name = "dddd";
man1.weight = 123.4;
person man2;
man2.height = 150.3;
man2.name = "uuuu";
man2.weight = 326.1;
if(sizeof(man1) == sizeof(man2)) cout<<"111111111111111111111111";
else cout<<"222222222222222222";
return 0;
}
答案:

其實這樣很好理解,man1和man2都是創建的物件,相當于房子,而他們的圖紙是同一份,也就是說創建出來的防止空間占據一樣大,那無論房子里面裝了多少東西,房子占據的空間仍一樣大.
1.如何計算類物件大小
那么,如何計算類物件的大小呢?其實類的大小計算和在c語言中結構體計算方法一模一樣,并且只計算成員屬性,成員方法不需要計算.
比如:
class person
{
public:
void say()
{
cout << "我可以說話" << endl;
}
private:
double height;
char name[20];
double weight;
};
按照結構體對齊,我們知道,
height占據8位元組,此時偏移量已經到了8,剛好是8的整數倍,所以name從8開始往后又占據20位元組,此時一共占據了28位元組,偏移量也已經是28了,而28并不是8的整數倍,所以需要往后空出4位元組,也就是現在消耗了32位元組,偏移量也達到了32%8==0,所以weight開始往后占據8位元組,到此,一共占據40位元組,而40也是8的倍數,所以person類物件的大小是40位元組
2.類物件的存盤方式
我們知道,一個類一般是包括成員屬性和成員方法的,那么其實體出的物件,會是怎樣存兩者的呢?
答:
每個物件都會存盤其成員屬性,但不會存盤成員方法,而類的成員方法是放在一個公共區域(代碼段)中.
原因:
每個物件都有不同的屬性(屬性值不一樣),但是他們的方法確是一樣的(方法定義上),而不同的物件,我們只需要知道其屬性,然后需要哪個方法就去公共區域呼叫哪個方法,這將會極大的節約空間.反之,如果每個物件都存盤一個相同的方法,就會造成極大的空間浪費
this指標
什么是this指標呢?這個問題先放一放,現在我們會想一下學習資料結構時候,以堆疊為例.
我們給某一個創建的堆疊放資料時候,函式宣告是這樣的
void StackPush(Stack* stack,DataType x);
我們要判斷堆疊是否滿時,函式宣告是這樣的
bool StackFull(Stack* stack);
我們要彈出堆疊的資料時候,函式宣告是這樣的
void stackPop(Stack* stack);
我們會發現,無論如何我么都必須給函式傳一個
堆疊指標過去,否則函式的操作物件將不知道.但是在類中,是否存在這種情況呢?我們看下面:
class caculate
{
public:
void init(int m,int n)
{
_m = m,_n = n;
}
void sum()
{
int ans = _m + _n;
cout <<"相加結果為"<<ans<<endl;
}
void sub()
{
int ans = _m - _n;
cout <<"相乘結果為"<<ans<<endl;
}
private:
int _m;
int _n;
};
int main()
{
caculate p1;
p1.init(1,2);
p1.sum();
p1.sub();
return 0;
}

在上面的例子中,我們可以清晰的看到,我們通過物件呼叫類方法時候,并沒有傳p1地址,而類函式又是放在公共區域的,它是怎么知道要計算哪一個類呢?而這就是我們要講解的this指標
其實,我么在呼叫函式時候,系統便已經悄悄幫我們轉換了:
比如我們呼叫init時候,編譯器會悄悄傳給init一個caculate*指標,即實際傳參為p1.init(&p1,1,3)
而定義init時候,編譯器也悄悄的給init放了caculate*形參,即實際定義為void init(caculate* this,int m,int n)
而那個悄悄放的指標就稱為
this指標,而init內部的_m編譯器會變成this->_m,同理還有this->_n
面試題
1.this指標存在哪里?
通過上面的講解我們可以看到,this指標是函式形參,而形參是放在記憶體四區的堆疊區,所以this指標存在堆疊區
2.this指標可以為空嗎?
下面分別呼叫了
PrintA和Show函式,請問下面的兩個函式是否會崩潰?
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
void Show()
{
cout<<"Show()"<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
p->Show();
}
答:
PrintA呼叫時會崩潰,Show呼叫時順利進行
原因:呼叫PrintA時候,它會接收this指標,然后通過this指標訪問其成員_a(之前已經說過成員值本質是this->成員),但是,但是,但是,我們的p是nullptr,那么this也就是nullptr,而空指標是沒有辦法訪問其成員的,所以報錯.
那么this指標可以為空指標嗎? 答案是可以的,但是要注意分情況,正如上面介紹的面試題一樣
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/310575.html
標籤:其他
