C++基礎(二)

目錄

1.類和對象

1.1類的定義

1.2訪問限定符

1.3類域

2.實例化?

?2.1實例化概念

2.2對象大小

3.this指針

4.類的默認成員函數

4.1構造函數

4.2析構函數

4.5運算符重載


1.類和對象

1.1類的定義

類的定義格式

class為定義類的關鍵字,Stack為類的名字,{}中為類的主體,注意定義結束時后面分號不省略。類體中內容稱為類的成員:類中的變量稱為類的屬性或成員變量;類中的函數稱為類的方法或成員函數。

//text.cpp
#include<iostream>
using namespace std;class Stack
{//成員變量int* a;int top;int  capacity;//成員函數void Push(){}void Pop(){}
};//分號不能省略
int main()
{return 0;
}

  • 為了區分成員變量,一般習慣上成員變量會加一個特殊標識,如成員變量前面或后面加_或者m_開頭。這個C++語法上并沒有規定,僅憑個人或公司喜好
//為區分成員變量,一般前面加_
//成員變量
int* _a;
int _top;
int  _capacity;
  • C++中struct也可以定義類,C++兼容c中struct的用法,同時struct升級成了類,明顯的變化是struct中也可以定義函數,一般情況下我們還是推薦用class定義類

  • 定義在類里面的成員默認為inline

1.2訪問限定符

C++一種實現封裝的方式,用類將對象的屬性與方法結合在一塊,讓對象更加完善,通過訪問權限選擇性的將其接口提供給外部的用戶使用

  • public(公有)修飾的成員在類外可以直接被訪問,protected(保護)和private(私有)修飾的成員在類外不能直接被訪問,protected和private是一樣的
  • 訪問權限作用域從該訪問權限出現的位置開始直到下一個訪問限定符出現為止,如果后面沒有訪問限定符,作用域就到 } 即類結束
//text.cpp
#include<iostream>
using namespace std;class Stack
{///void Push(){}//Push 沒給限定符 class默認私有 private ///
public:void Pop(){}int Swap(){}//Pop和Swap 被public修飾,直到下一個限定符出現之前都為公有///
protected:int add();
//add 被public修飾,直到下一個限定符出現之前都為保護
/// /private:int* _a;int _top;int  _capacity;
//成員變量被private修飾,直到}結束都為私有
};
int main()
{Stack st;//公有可以訪問st.Pop();st.Swap();//私有不可訪問st._top;return 0;
}

額外注意:?

  1. class定義成員沒有被訪問限定符修飾時默認為private,struct默認為public
  2. 一般成員變量都會被限制為private/protected,需要給別人使用的成員函數會被放為public

1.3類域

類定義了一個新的作用域,類所有成員都在類的作用域中,在類體外定義成員時,需要使用? ::作用域操作符指明成員屬于哪個類域

類域影響的是編譯的查找規則,下面程序中Init如果不指定類域Stack,那么編譯器就會把Init當成全局函數,那么編譯時找不到_top等成員,就會到類域去找

//text.cpp
#include<iostream>
using namespace std;class Stack
{public:void Init(int x, int y);};
void Stack::Init(int x, int y){_top = x;_capacity = y;}
int main()
{return 0;
}

?注意:

  1. 類里面的函數聲明定義分離,類創建后形成了新的類域,需要指定類域,否則不可訪問

2.實例化?

?2.1實例化概念

  • 用類型在物理內存中創建的過程,稱為類實例化出對象
  • 類是對象進行一種抽象描述,是一個模型一樣的東西,限定了類有哪些成員變量,這些成員變量只是聲明,沒有分配空間,用類實例化出對象時,才會去分配空間

//text.cpp
#include<iostream>
using namespace std;class Stack
{//聲明int* _a;int _top;int  _capacity;};int main()
{Stack::_top = 2024;//編譯器報錯,_top只是聲明,并未實例化return 0;
}
  • 一個類可以實例化出多個對象,實例化出的對象占用實際的物理空間,存儲成員變量。打個比方:類實例化出對象就像現實中使用建筑設計圖造房子一樣,類就像設計圖,設計圖規劃出有多少個房間,房子大小等,但是并沒有實體的建筑存在,也不能住人,用設計圖修建出房子,房子才能住人。同樣類就像設計圖一樣,只是告訴編譯器即將要開多大的內存,但是不開內存,只有實例化出的對象才分配物理內存存儲數據
  • ?
    //text.cpp
    #include<iostream>
    using namespace std;class Stack
    {//聲明int* _a;int _top;int  _capacity;};int main()
    {Stack st;st._top=2024;
    //Stack實例化出st,系統已經給st分配內存了,可以存儲數據,編譯通過return 0;
    }?

    2.2對象大小

  • 分析一下類對象都有哪些成員呢?類實例化出的每個對象,都有獨立的數據空間,所以對象中肯定包含成員變量,那么成員函數是否包含呢?首先函數被編譯后是一段指令,對象中沒法儲存,這些指令存儲在一個單獨的區域(代碼段),那么對象非要存儲的話,只能是成員函數的指針。對象中是否有存儲指針的必要呢,Date實例化出兩個對象d1和d2,di和d2都有各自獨立的成員變量_year/_month/_day存儲各自的數據,但是d1和d2的成員函數Init/Print指針卻是一樣的,存儲在對象中就浪費了。如果用Date實例化出100個對象,那么成員函數指針就重復存儲100次,太浪費了。其實函數指針不需要存儲的,函數指針是一個地址,調用函數被編譯成匯編指令[call地址],其實編譯器在編譯鏈接時,就要找到函數的地址,不是在運行時找,只有動態多態是在運行時找,就需要存儲函數地址。

內存對齊規則

  • 第一個成員在與結構體偏移量為0處的地址處
  • 其他成員變量要對齊對齊數的整數倍的地址處
  • 對齊數=編譯器默認的對齊數與該成員的大小的較小值
  • VS x64平臺默認對齊數是4,x86默認對齊數是8
  • 結構體總大小為:最大對齊數(所有類型變量最大者與默認對齊數取最小)的整數倍
  • 如果嵌套了結構體的情況,嵌套的結構體對齊到自己最大對齊數的整數倍,結構體整體大小就是所有最大對齊數(含嵌套結構體對齊數)的整數倍
class A
{
public:void Print(){cout << _ch << endl;}
private:char _ch;int _i;
};
//_ch 是一個字節,默認對齊數是4,最大對齊數是4,所以開辟4個字節用來存在_ch
// _i是4個字節,默認對齊數是4,最大對齊數是4,所以開辟4個字節用來存儲_i
class B
{
public:void Print(){//。。。}};
class B
{};
//B和C里面沒有存儲任何成員變量,只有一個函數,可成員函數不存對象里面
// 按理來說是0,但是結構體怎么會沒大小,為表示對象存在C++對這種規定大小為1,為了占位標識對象存在

3.this指針

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

例如Date類中的Init原型為 void Init(Date * const this,int year ,int month,int day),類的成員函數中訪問成員變量,本質是通過this指針訪問的,如Init函數中給_year賦值,this->_year=year

?原型:

class Date
{void Print(){cout << _year << "\n" << _month << "\n" << _day << endl;}void Init( int year, int month,int day){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;};

Date d1;
d1.Init(2024,7,10);d1.Print();
Date d2;d2.Init(2024, 7, 9);
d2.Print();

???真實原型? ? ?

class Date
{void Init(Date* const this,int year, int month,int day){this->_year = year;this->_month = month;this->_day = day;}void Printf(Date* const this){cout << this->_year << "\n" <<this-> _month << "\n" << this->_day << endl;}
private:int _year;int _month;int _day;};

Date d1;
d1.Init(&d1,2024,7,10);
d1.Print(&d1);Date d2;
d2.Init(&d2,2024, 7, 9);
d2.Print();

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

this指針存在棧里

4.類的默認成員函數

默認成員函數就是用戶沒有顯示定義,編譯器會自動生成的成員函數稱為默認成員函數

4.1構造函數

構造函數是特殊的成員函數,需要注意的是,構造函數雖然名字叫構造函數,但是構造函數的主要內容并不是開辟空間創造對象(我們平常使用的局部對象是棧幀創建時,空間就開好了),而是對象實例化時初始化對象。構造函數的本質是要替代我們以前Stack和Date類中寫的Init函數的功能,構造函數自動調用的特點就完美替代了Init

構造函數的特點:

  1. 函數名與類名相同
  2. 無返回值(返回值啥也不需要給,也不要寫void C++就是這樣規定)
  3. 對象實例化時系統會自動調用對應的構造函數
  4. 構造函數可以重載
  5. 如果類中沒有顯示定義構造函數,則C++編譯器會自動生成一個無參的默認構造函數,一旦用戶顯示定義編譯器將不再生成

class Date
{public://1.無參構造函數Date(){_year = 1;_month = 1;_day = 1;}//2.帶參構造函數Date(int year, int month, int day)
{_year = year;_month = month;_day = day;}//3.全缺省構造函數Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;};

無參構造函數,全缺省構造函數,我們不寫構造時編譯器默認生成的構造函數,都叫做默認構造函數。但是這三個有且只能有一個存在,不能同時存在。無參構造函數和全缺省構造函數雖然構成函數重載,但是調用時會存在歧義。注意并不是只有默認構造函數就是編譯器默認生成的那就是構造函數,無參構造函數,全缺省構造函數也是默認構造函數,總結一下就是不傳參就能調用

我們不寫,編譯器默認生成的構造,對內置類型成員變量的初始化沒有要求,也就是說是是否初始化是不確定的,看編譯器。

//text.cpp
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申請失敗");}_capacity = n;_top = 0;}private:STDataType* _a;size_t _capacity;size_t _top;
};
//兩個Stack實現隊列
class MyQueue
{
private:int size;Stack pushst;Stack popst;
};int main()
{MyQueue my;return 0;
}

?C++把類型分為自定義類型和內置類型(基本類型)。內置類型就是語言提供的原生數據類型,如int/char/double/指針等,自定義類型就是我們使用class/struct等關鍵字自己定義的類型。這里構造函數自動初始化,VS也將內置類型size初始化了,不同的編譯器初始化值不同,C++并沒有規定

對于自定義類型成員變量,要求調用這個成員變量的默認構造函數初始化。如果這個成員變量,沒有默認的構造函數,那么就會報錯,我們要初始化這個成員變量,需要用初始化列表才能解決

?總結:大多數情況下,構造函數都需要我們自己去實現,少數情況類似MyQueue且Stack有默認構造函數時,MyQueue自動生成就可以用

4.2析構函數

~Stack()
{free(_a);_a = nullptr;_top = _capacity = 0;
}

析構函數的特點:

1.析構函數名是在類名前面加上字符~

2.無參無返回值(與構造函數一致)

3.一個類只能有一個析構函數。若未顯示定義,系統也會自動生成默認的析構函數

4.對象聲明周期結束時,系統會自動調用析構函數,

5.跟構造函數類似,我們不寫編譯器自動生成的析構函數對內置類型成員不做處理,自定義類型成員會調用其他的析構函數

6.還需注意的是我們顯示析構函數,對于自定義類型成員也會調用他的析構函數,也就是說自定義類型成員無論什么情況都會自動調用析構函數。

//text.cpp
#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申請失敗");}_capacity = n;_top = 0;}~Stack(){free(_a);_a = nullptr;_top=_capacity=0;}
private:STDataType* _a;size_t _capacity;size_t _top;};
//兩個Stack實現隊列
class MyQueue
{public://編譯器默認生成MyQueue的構造函數調用了Stack的構造,完成了兩個成員的初始化//編譯器默認生成MyQueue的析構函數調用了Stack的析構,釋放了Stack內部的資源//顯示寫析構也會調用Stack的析構~MyQueue(){cout << "~MyQueue" << endl;}
private: Stack pushst;Stack popst;};int main()
{MyQueue my;return 0;
}

?MyQueue里的析構啥也沒干,但是C++規定會調用其他的析構來釋放內存

如果沒有申請資源時,析構可以不寫,直接使用編譯器生成的默認析構函數,如Date,如果默認生成的析構可以用,也就不需要顯示寫析構如MyQueue,但是有資源申請時,一定要直接寫析構,否則會造成資源泄漏如Stack

4.5運算符重載

  • 當運算符被用于類型的對象時,C++語言允許我們通過運算符重載的形式指定新的含義。C++規定類類型對象使用運算符時,必須轉換成調用對應運算符重載,若沒有則編譯器報錯
  • 運算符重載是具有特定名字的函數,他的名字是由operator和后面要定義的運算符共同構成。和其他函數一樣,它也具有其返回類型和參數列表以及函數體
bool operator<(Date d1, Date d2)
{}
bool operator==(Date d1,Date d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
  • 重載運算符函數的參數個數和該運算符作用的參數一樣多。一元運輸安撫有一個參數,二元運算符有兩個參數,二元運算符的左側運算對象傳給第一個參數,右側運算對象傳給第二個參數
//text.cpp
#include<iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year= year;_month = month;_day = day;}int _year;int _month;int _day;};bool operator<(Date d1, Date d2)
{}
bool operator==(Date d1,Date d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{Date d1(2024, 7, 10);Date d2(2024,7,9);
//兩種用法都可以d1 == d2;operator==(d1 , d2);return 0;
}
  • 如果一個重載運算符函數是成員函數,則他的第一個運算對象默認傳給隱式的this指針,因此運算符重載作為成員函數時,參數比運算對象少一個
  • 運算符重載以后,其優先級和結合性與內置類型運算保持一致
  • 不能通過連接語法中沒有的符合來創建性的操作符:比如operator@

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

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

相關文章

7月11日學習打卡,數據結構棧

大家好呀&#xff0c;本博客目的在于記錄暑假學習打卡&#xff0c;后續會整理成一個專欄&#xff0c;主要打算在暑假學習完數據結構&#xff0c;因此會發一些相關的數據結構實現的博客和一些刷的題&#xff0c;個人學習使用&#xff0c;也希望大家多多支持&#xff0c;有不足之…

dataX入門

下載dataX https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz 然后 下載后解壓至本地某個目錄&#xff0c;進入bin目錄&#xff0c;即可運行同步作業&#xff1a; $ cd {YOUR_DATAX_HOME}/bin $ python datax.py {YOUR_JOB.json} 要求你有python…

vue-grid-layout詳解

vue-grid-layout 教程 vue-grid-layout 是一個用于 Vue.js 的響應式拖放網格布局組件&#xff0c;允許開發者創建可調整大小、可拖放的布局&#xff0c;廣泛用于儀表板、管理面板等復雜布局需求。本教程將介紹如何安裝、配置和使用 vue-grid-layout。 目錄 安裝基本使用 布局…

怎樣在 C 語言中進行類型轉換?

&#x1f345;關注博主&#x1f397;? 帶你暢游技術世界&#xff0c;不錯過每一次成長機會&#xff01; &#x1f4d9;C 語言百萬年薪修煉課程 通俗易懂&#xff0c;深入淺出&#xff0c;匠心打磨&#xff0c;死磕細節&#xff0c;6年迭代&#xff0c;看過的人都說好。 文章目…

暑假自律日記九

7.10 &#xff08;半小時日記打卡之——暑假第九天&#xff09; 前言&#xff1a;或許是累了&#xff0c;今天的效率和進度可以說是歷史最低了&#xff0c;調休了一天&#xff0c;算是歇會 日程 八點二十起床&#xff0c;在床上賴了一會 九點二十抵達逸夫樓&#xff0c;開始補…

kafka發送消息流程

配置props.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, RoundRobinPartitioner.class); public Map<String,Object> producerConfigs(){Map<String,Object> props new HashMap<>();props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootstrapServers…

Spring Boot中的安全配置與實現

Spring Boot中的安全配置與實現 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將深入探討Spring Boot中的安全配置與實現&#xff0c;看看如何保護你的…

在表格中把tab換成enter鍵------ivx

為了方便用戶輸入&#xff0c;把tab鍵替換成enter回車 方法如下&#xff1a; 添加一個fx函數 document.addEventListener(‘keydown’, function(event) { if (event.key ‘Enter’ && !event.shiftKey) { event.preventDefault(); var focusableElements document.q…

昇思25天打卡營-mindspore-ML- Day22-應用實踐-自然語言處理-LSTM+CRF序列標注

昇思25天打卡營-mindspore-ML- Day22-應用實踐-自然語言處理-LSTMCRF序列標注 今天學習了 LSTMCRF 序列標注方法&#xff0c;它是一種結合了循環神經網絡&#xff08;RNN&#xff09;和條件隨機場&#xff08;CRF&#xff09;的強大模型&#xff0c;用于處理序列標注問題&#…

【C++BFS】690. 員工的重要性

本文涉及知識點 CBFS算法 LeetCode690. 員工的重要性 你有一個保存員工信息的數據結構&#xff0c;它包含了員工唯一的 id &#xff0c;重要度和直系下屬的 id 。 給定一個員工數組 employees&#xff0c;其中&#xff1a; employees[i].id 是第 i 個員工的 ID。 employees[…

RabbitMQ 高級功能

RabbitMQ 是一個廣泛使用的開源消息代理&#xff0c;它支持多種消息傳遞協議&#xff0c;可以在分布式系統中用于可靠的消息傳遞。除了基本的消息隊列功能外&#xff0c;RabbitMQ 還提供了一些高級功能&#xff0c;增強了其在高可用性、擴展性和靈活性方面的能力。以下是一些主…

軟件架構之嵌入式系統設計(2)

軟件架構之嵌入式系統設計&#xff08;2&#xff09; 12.4 嵌入式網絡系統12.4.1 現場總線網12.4.2 家庭信息網11.4.3 無線數據通信網12.4.4 嵌入式 Internet 12.5 嵌入式數據庫管理系統12.5.1 使用環境的特點12.5.2 系統組成與關鍵技術 12.6 實時系統與嵌入式操作系統12.6.1 嵌…

MyBatis(38)MyBatis 如何與 Spring Boot 集成,有哪些實踐技巧

集成MyBatis與Spring Boot可以極大地提升開發效率&#xff0c;簡化配置&#xff0c;并利用Spring Boot的自動配置特性優化項目結構和性能。下面我們將詳細探討如何實現這一集成&#xff0c;并分享一些實踐技巧。 1. 添加依賴 首先&#xff0c;在pom.xml中添加MyBatis和Spring…

AI學習指南機器學習篇-聚類樹的剪枝

AI學習指南機器學習篇-聚類樹的剪枝 在機器學習領域&#xff0c;聚類是一種常用的無監督學習方法&#xff0c;通過對數據進行分組來發現數據中的結構和模式。聚類樹是一種常用的聚類算法之一&#xff0c;它通過構建一個樹狀結構來展示聚類的層次關系&#xff0c;并能夠幫助我們…

Linux 忘記root密碼,通過單用戶模式修改

銀河麒麟桌面操作系統 V10&#xff08;sp1&#xff09;”忘記用戶密碼&#xff0c;需要修改用戶密碼所寫&#xff0c;可用于 X86 架構和 arm 架構。 2. 選擇第一項&#xff0c;在上圖界面按“e”鍵進行編輯修改。 3. 在以 linux 開頭這行的行末&#xff0c;添加“init/bin/bas…

Rockchip Android平臺編譯生成userdata.img

Rockchip Android平臺編譯生成userdata.img 適用版本 本修改方法適用于Android12及以上版本 代碼修改 device/rockchip/rk3576&#xff1a; --- a/rk3576_u/BoardConfig.mkb/rk3576_u/BoardConfig.mk-28,4 28,7 PRODUCT_KERNEL_CONFIG pcie_wifi.configBOARD_GSENSOR_MXC…

SSE(Server-Send-Event)服務端推送數據技術

SSE&#xff08;Server-Send-Event&#xff09;服務端推送數據技術 大家是否遇到過服務端需要主動傳輸數據到客戶端的情況&#xff0c;目前有三種解決方案。 客戶端輪詢更新數據。服務端與客戶端建立 Socket 連接雙向通信服務端與客戶建立 SSE 連接單向通信 幾種方案的比較&…

【前端】fis框架學習

文章目錄 1. 介紹 1. 介紹 FIS是專為解決前端開發中自動化工具、性能優化、模塊化框架、開發規范、代碼部署、開發流程等問題的工具框架。 使用FIS我們可以快速的完成各種前端項目的資源壓縮、合并等等各種性能優化工作&#xff0c;同時FIS還提供了大量的開發輔助功能 首先我們…

Nginx上配置多個網站

一、需求描述 我們只有一臺安裝了Nginx的服務器,但是我們需要實現在這臺服務器上部署多個網站,用以對外提供服務。 二、Nginx上配置多個網站分析 一般網站的格式為:【http://ip地址:端口號/URI】(比如:http://192.168.3.201:80),IP地址也可用域名表示;那么要實現在Nginx…

QT實現WebSocket通信

文章目錄 WebSocket服務端WebSocket客戶端html websocket客戶端在Qt5中實現WebSocket通信可以通過使用QtWebSockets模塊來實現。這個模塊提供了一個WebSocket客戶端和服務器的實現,可以很方便地在你的應用程序中集成WebSocket功能。 使用的時候,首先在pro工程文件中添加對應的…