初識c++(類與對象——上)

一、類的定義

1、類定義格式

? class為定義類的關鍵字,Stack為類的名字,{}中為類的主體,注意類定義結束時后面分號不能省

略。類體中內容稱為類的成員:類中的變量稱為類的屬性或成員變量; 類中的函數稱為類的方法或

者成員函數。

? 為了區分成員變量,?般習慣上成員變量會加?個特殊標識,如成員變量前面或者后面加_ 或者 m

開頭,注意C++中這個并不是強制的,只是?些慣例。

? C++中struct也可以定義類,C++兼容C中struct的用法,同時struct升級成了類,明顯的變化是

struct中可以定義函數,一般情況下我們還是推薦用class定義類。

? 定義在類面的成員函數默認為inline。

#include<iostream>
using namespace std;
class Stack
{
public:// 成員函數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){// ...擴容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;
}; // 分號不能省略
int main()
{Stack st;st.Init();st.Push(1);st.Push(2);cout << st.Top() << endl;st.Destroy();return 0;
}
#include<iostream>
using namespace std;
// C++升級struct升級成了類
// 1、類??可以定義函數
// 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;
};

2、訪問限定符

? C++?種實現封裝的方式,用類將對象的屬性與方法結合在?塊,讓對象更加完善,通過訪問權限

選擇性的將其接口提供給外部的用戶使用。

? public修飾的成員在類外可以直接被訪問;protected和private修飾的成員在類外不能直接被訪

問。

在這里插入圖片描述

? 訪問權限作用域從該訪問限定符出現的位置開始直到下?個訪問限定符出現時為止,如果后面沒有

訪問限定符,作用域就到 }即類結束。

? class定義成員沒有被訪問限定符修飾時默認為private,struct默認為public。

? ?般成員變量都會被限制為private/protected,需要給別?使?的成員函數會放為public。

在這里插入圖片描述

3、類域

? 類定義了?個新的作用域,類的所有成員都在類的作用域中,在類體外定義成員時,需要使用 :: 作

用域操作符指明成員屬于哪個類域。

? 類域影響的是編譯的查找規則,下面程序中Init如果不指定類域Stack,那么編譯器就把Init當成全

局函數,那么編譯時,找不到array等成員的聲明/定義在哪里,就會報錯。指定類域Stack,就是知

道Init是成員函數,當前域找不到的array等成員,就會到類域中去查找。

#include<iostream>
using namespace std;
class Stack
{
public:
// 成員函數void Init(int n = 4);
private:// 成員變量int* array;size_t capacity;size_t top;
};
// 聲明和定義分離,需要指定類域
void Stack::Init(int n)
{array = (int*)malloc(sizeof(int) * n);if (nullptr == array){perror("malloc申請空間失敗");return;}capacity = n;top = 0;
}
int main()
{Stack st;st.Init();return 0;
}

二、實例化

1、概念

? 用類類型在物理內存中創建對象的過程,稱為類實例化出對象。

? 類是對象進用?種抽象描述,是?個模型?樣的東西,限定了類有哪些成員變量,這些成員變量只

是聲明,沒有分配空間,?類實例化出對象時,才會分配空間。

? ?個類可以實例化出多個對象,實例化出的對象 占用實際的物理空間,存儲類成員變量。打個

比方:類實例化出對象就像現實中使用建筑設計圖建造出房子,類就像是設計圖,設計圖規劃了有多

少個房間,房間大小功能等,但是并沒有實體的建筑存在,也不能住人,用設計圖修建出房子,房

子才能住人。同樣類就像設計圖?樣,不能存儲數據,實例化出的對象分配物理內存存儲數據。

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

#include<iostream>
using namespace std;
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和d2Date d1;Date d2;d1.Init(2024, 3, 31);d1.Print();d2.Init(2024, 7, 5);d2.Print();return 0;
}

2、對象大小

分析?下類對象中哪些成員呢?類實例化出的每個對象,都有獨立的數據空間,所以對象中肯定包含

成員變量,那么成員函數是否包含呢?首先函數被編譯后是?段指令,對象中沒辦法存儲,這些指令

存儲在?個單獨的區域(代碼段),那么對象中非要存儲的話,只能是成員函數的指針。再分析?下,對

象中是否有存儲指針的必要呢,Date實例化d1和d2兩個對象,d1和d2都有各自獨立的成員變量

_year/_month/_day存儲各自的數據,但是d1和d2的成員函數Init/Print指針卻是?樣的,存儲在對象

中就浪費了。

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

C++規定類實例化的對象也要符合內存對齊的規則。

  1. 結構體的第?個成員對齊到和結構體變量起始位置偏移量為0的地址處

  2. 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。

? 對齊數 = 編譯器默認的?個對齊數 與 該成員變量大小的較小值。VS 中默認的值為 8 Linux中 gcc 沒有默認對齊數,對

齊數就是成員自身的大小

  1. 結構體總大小為最大對齊數(結構體中每個成員變量都有?個對齊數,所有對齊數中最大的)的

整數倍。

  1. 如果嵌套了結構體的情況,嵌套的結構體成員對齊到自己的成員中最大對齊數的整數倍處,結構

體的整體大小就是所有最大對齊數(含嵌套結構體中成員的對齊數)的整數倍。

這么說有點晦澀難懂,接下來給大家幾個例子了解。

class MyClass1
{char c1;int i;char c2;
};
int main()
{MyClass cl;cout << sizeof(cl) << endl;return 0;
}

對于結構體第一個變量我們是不用考慮c1的偏差值,直接存放,根據對齊規則,i要放在4的倍數的位置,即偏差值為4的位置,c2是char類型所以可以隨便放(任何數都是1的倍數),所以這個在內存中是這么放的:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

所以總大小為9但其成員最大對齊數是4,5不是4的倍數,所以最終內存大小為12字節。

class MyClass1
{char c1;int i;char c2;
};
class S4
{char c1;MyClass1 s1;int d;
};
int main()
{S4 s;cout << sizeof(s) << endl;return 0;
}

由上面知道MyClass1大小為12,其中它的成員最大對齊數為4,所以待會對齊到4的倍數那所以其在內存存放是這樣的:

外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

總大小為20,其中最大對齊數為4,而20是4的倍數,所以這個大小為20字節。

最后,我們思考如果一個類沒有成員對象大小是怎么樣?

class A
{
public:void Print(){cout << _ch << endl;}
private:char _ch;int _i;
};
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;
}

上面的程序運行后,我們看到沒有成員變量的B和C類對象的大小是1,為什么沒有成員變量還要給1個

字節呢?因為如果?個字節都不給,怎么表示對象存在過呢!所以這里給1字節,純粹是為了占位標識

對象存在。

三、this指針

前面我們了解到在計算類的大小時我們是不用管函數的,函數是同一儲存在代碼區的,那么編譯器是如何識別我在哪個類里面調用了該函數的呢?其實就是this的作用

? Date類中有 Init 與 Print 兩個成員函數,函數體中沒有關于不同對象的區分,那當d1調用Init和

Print函數時,該函數是如何知道應該訪問的是d1對象還是d2對象呢?那么這里就要看到C++給了

?個隱含的this指針解決這里的問題

? 編譯器編譯后,類的成員函數默認都會在形參第?個位置,增加?個當前類類型的指針,叫做this

指針。比如Date類的Init的真實原型為, void Init(Date* const this, int year,int month, int day)

? 類的成員函數中訪問成員變量,本質都是通過this指針訪問的,如Init函數中給_year賦值, this-

>_year = year;

? C++規定不能在實參和形參的位置顯示的寫this指針(編譯時編譯器會處理),但是可以在函數體內顯

示使用this指針。

#include<iostream>
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 = year;this->_month = month;this->_day = day;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:// 這?只是聲明,沒有開空間int _year;int _month;int _day;
};
int main()
{// Date類實例化出對象d1和d2Date d1;Date d2;// d1.Init(&d1, 2024, 3, 31);//把d1的地址傳過去讓this接收d1.Init(2024, 3, 31);d1.Print();d2.Init(2024, 7, 5);d2.Print();return 0;
}

這里就有兩道非常有意思的題:

#include<iostream>
using namespace std;
class A
{
public:void Print(){cout << "A::Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}

上面這個程序是正常運行的,雖然有可能有些人會以為對空指針解引用會造成編譯錯誤,實際上并沒有對空指針經行解引用操作

#include<iostream>
using namespace std;
class A
{
public:void Print()//void Print((A* const this)//沒有對空指針解引用{cout << "A::Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();//相當于Print(p)return 0;
}

再來看下一個:

#include<iostream>
using namespace std;
class A
{
public:void Print()//void Print((A* const this){cout << "A::Print()" << endl;cout << _a << endl;//this->_a這進行了對空指針的解引用}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();//相當于Print(p)return 0;
}

這個就會出現編譯報錯,因為載打印類的成員對象_ a時對空指針進行了解引用。

#include<iostream>
using namespace std;
class A
{
public:void Print()//void Print((A* const this){cout << "A::Print()" << endl;cout << _a << endl;//this->_a這進行了對空指針的解引用}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();//相當于Print(p)return 0;
}

l;
cout << _a << endl;//this->_a這進行了對空指針的解引用
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();//相當于Print(p)
return 0;
}


這個就會出現編譯報錯,因為載打印類的成員對象_ a時對空指針進行了解引用。```c++
#include<iostream>
using namespace std;
class A
{
public:void Print()//void Print((A* const this){cout << "A::Print()" << endl;cout << _a << endl;//this->_a這進行了對空指針的解引用}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();//相當于Print(p)return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44267.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44267.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44267.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

損失函數 - Transformer教程

在人工智能和深度學習的領域&#xff0c;Transformer模型已經成為了非常流行的選擇。而在Transformer模型的訓練過程中&#xff0c;損失函數扮演了至關重要的角色。今天&#xff0c;我們就來深入探討一下什么是損失函數&#xff0c;以及它在Transformer中的應用。 什么是損失函…

【Node.js安裝教程】

Node.js安裝教程 第一步&#xff1a;下載 下載鏈接&#xff1a;https://nodejs.org/zh-cn 第二步&#xff1a;安裝 **方法一&#xff1a;**建議安裝在默認路徑 方法二&#xff1a;如果不是默認安裝路徑可能會出現一系列問題&#xff1a;這時可以選擇卸載重裝或者配置環境變量…

kotlin數據類型

人不走空 &#x1f308;個人主頁&#xff1a;人不走空 &#x1f496;系列專欄&#xff1a;算法專題 ?詩詞歌賦&#xff1a;斯是陋室&#xff0c;惟吾德馨 Kotlin基本數值類型 基本數據類型包括 Byte、Short、Int、Long、Float、Double 整數類型 類型位寬最小值最大…

安卓微信8.0之后如何利用緩存找回的三天之前不可見的朋友圈圖片

安卓微信8.0之后如何利用緩存找回的三天之前不可見的朋友圈圖片 復習了下安卓程序的知識&#xff0c;我們會了解到&#xff0c;安卓程序清楚數據的時候有兩個選項 一個是清除全部數據一個是清除緩存。 清除全部數據表示清除應用數據緩存。 對于安卓微信8.0之后而言&#xff0…

OTP防重放攻擊

OTP本意是一次性口令&#xff0c;比如郵箱驗證碼&#xff0c;短信驗證碼&#xff0c;或者根據totp或者hotp生成的默認30秒一變的6位數字。 不過開發者要注意&#xff0c;必須要在驗證成功后失效那個驗證碼&#xff0c;不然就會導致重放攻擊。 對于郵箱驗證碼&#xff0c;服務器…

徹底開源,免費商用,上海AI實驗室把大模型門檻打下來

終于&#xff0c;業內迎來了首個全鏈條大模型開源體系。 大模型領域&#xff0c;有人探索前沿技術&#xff0c;有人在加速落地&#xff0c;也有人正在推動整個社區進步。 就在近日&#xff0c;AI 社區迎來首個統一的全鏈條貫穿的大模型開源體系。 雖然社區有LLaMA等影響力較大…

從 ArcMap 遷移到 ArcGIS Pro

許多 ArcMap 用戶正在因 ArcGIS Pro 所具有的現代 GIS 桌面工作流優勢而向其遷移。 ArcGIS Pro 與其余 ArcGIS 平臺緊密集成&#xff0c;使您可以更有效地共享和使用內容。 它還將 2D 和 3D 組合到一個應用程序中&#xff0c;使您可以在同一工程中使用多個地圖和多個布局。 Arc…

【C++雜貨鋪】C++11新特性

目錄 &#x1f308; 前言&#x1f308; &#x1f4c1; C11介紹 &#x1f4c1; 統一初始化列表 &#x1f4c1; 聲明 &#x1f4c2; auto &#x1f4c2; decltype &#x1f4c2; 返回類型后置 &#x1f4c2; 范圍for &#x1f4c2; 模板別名 &#x1f4c2; nullptr &#x1…

服務器使用PC作為代理訪問外網

1、PC上啟動代理&#xff0c;比如nginx 下載nginx&#xff1a;http://nginx.org/en/download.html 修改配置文件&#xff0c;在conf下&#xff1a; http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;server…

六、 SpringBoot 配置?件 ★ ?

六、 SpringBoot 配置?件 本節?標1. 配置?件作?2. 配置?件快速??3. 配置?件的格式4. properties 配置?件說明4.1 properties 基本語法4.2 讀取配置?件4.3 properties 缺點分析 5. yml 配置?件說明5.1 yml 基本語法5.2 yml 使?進階5.2.1 yml 配置不同數據類型及 nul…

昇思25天學習打卡營第17天|文本解碼原理--以MindNLP為例

文本解碼就是根據當前已經輸入的內容不斷地預測下一個詞&#xff0c;前期通過大量的文本文章等輸入&#xff0c;讓模型學習好以后&#xff0c;根據已學習的內容&#xff0c;不斷預測下一個詞。就像鸚鵡學舌一樣你不斷的叫他說你好大帥哥&#xff0c;你好大帥哥。后面某一天&…

好用的源代碼加密軟件有哪些?5款源代碼防泄密軟件推薦

源代碼作為軟件產品的核心組成部分&#xff0c;其安全性直接關系到整個軟件系統的安全。源代碼的泄露可能導致企業的技術秘密暴露&#xff0c;商業競爭力下降&#xff0c;甚至可能引發經濟損失和法律責任問題。因此&#xff0c;對源代碼進行加密保護&#xff0c;已經成為企業不…

windows安裝啟動mysql8.0版本的簡單流程

1.下載mysql8.0.25版本 MySQL :: Download MySQL Community Server (Archived Versions) 2.解壓到D盤的mysql文件夾,并修改環境變量 配置環境變量: winr鍵>輸入control system>高級系統設置>點擊環境變量 雙擊path后,新建 將bin目錄粘貼進去,再點擊確定 在cmd命令行…

【JavaScript 報錯】未定義的變量或函數:Uncaught ReferenceError

&#x1f525; 個人主頁&#xff1a;空白詩 文章目錄 一、錯誤原因分析1. 變量未定義2. 函數未定義3. 塊級作用域問題 二、解決方案1. 確保變量已定義2. 確保函數已定義3. 正確使用塊級作用域 三、實例講解四、總結 在JavaScript開發中&#xff0c;Uncaught ReferenceError 是一…

C#使用異步方式調用同步方法的實現方法

使用異步方式調用同步方法&#xff0c;在此我們使用異步編程模型&#xff08;APM&#xff09;實現 1、定義異步委托和測試方法 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Task…

算法學習day10(貪心算法)

貪心算法&#xff1a;由局部最優->全局最優 貪心算法一般分為如下四步&#xff1a; 將問題分解為若干個子問題找出適合的貪心策略求解每一個子問題的最優解將局部最優解堆疊成全局最優解 一、擺動序列&#xff08;理解難&#xff09; 連續數字之間的差有正負的交替&…

Maven Nexus3 私服搭建、配置、項目發布指南

maven nexus私服搭建 訪問nexus3官方鏡像庫&#xff0c;選擇需要的版本下載&#xff1a;Docker Nexus docker pull sonatype/nexus3:3.49.0 創建數據目錄并賦權 sudo mkdir /nexus-data && sudo chown -R 200 /nexus-data 運行(數據目錄選擇硬盤大的卷進行掛載) …

mysql快速精通(五)數據庫備份與還原

主打一個實用 對于重要數據我們常常進行備份以應對突發情況&#xff0c;以下使用Navicat對數據進行備份&#xff0c;想了解sql語句的自尋 備份?? 還原??

自動化回復信息工具的開發分享!

在當今信息爆炸的時代&#xff0c;無論是個人還是企業&#xff0c;都面臨著大量的信息處理和回復工作&#xff0c;為了提高效率&#xff0c;自動化回復信息工具變得越來越重要。 本文旨在分享一個簡單但實用的自動化回復信息工具的五段源代碼開發過程&#xff0c;幫助讀者理解…

DNS正向解析,反向解析

目錄 一、正向解析 1.下載DNS軟件包 2.修改主配置文件 3.創建區域文件 4.配置DNS 5.測試 二、反向解析 1.修改主配置文件 2.創建區域文件 3.測試 一、正向解析 1.下載DNS軟件包 [rootwww ~]# yum indtall -y bind注意&#xff1a; 下載軟件前需要配置倉庫&…