c++|類和對象(上)

目錄

一、面向過程和面向對象初步認識

二、類的引入和定義

2.1類的引入

2.2類的定義

三、類的訪問限定符及封裝

3.1訪問限定符?

3.2封裝?

四、類的作用域

五、類的實例化

六、類的對象大小的計算

6.1如何計算對象的大小

6.2類對象的存儲方式?

七、類成員函數的this指針

7.1this指針的引出

7.2this指針的特性


一、面向過程和面向對象初步認識

?面向過程:關注的是過程,分析出求解問題的步驟,通過函數調用逐步解決問題。

例如:洗衣服

拿盆子——>放水——>放衣服——>放洗衣粉——>手搓——>換水——>手搓——>擰干——>晾衣服?

我們知道C語言面向的是過程,例如我們要得到兩數之和的結果,那么得造一個和函數,先進行傳實參,形參相加,得到參數之和,返回之和的值,這就是得到兩數之和的一個過程。

面向對象:關注的是對象,將一件事情拆分成不同的對象,靠對象之間的交互完成。

?還是洗衣服,有四個對象:人、衣服、洗衣粉、洗衣機?

那么洗衣服過程:人將衣服放入洗衣機、倒入洗衣粉,啟動洗衣機,洗衣機就會完成洗衣過程,人不需要關注洗衣機是如何洗衣服的。

那么照樣我們也要得到兩數之和的結果,就不需要去實現函數了,而是直接用函數就行,這個函數是已經被實現的,這就是直接使用對象,不需要關注實現過程。

那么還有疑問的是,對象之間的操作怎么看起來也是一個過程,其實是不同角度產生的看法,那么對于同一件事情,洗衣服,從過程來看,這個衣服需要執行被洗的過程,在乎的是怎么洗的過程,而從對象來看,只需要用洗衣機完成洗衣服,怎么洗的過程不在乎。其實這就相當與面向對象是以更高的角度去看待面向過程。

我再從更高的角度去看待面向對象,那么面向對象又可以看成面向過程。所以對于任何實現從不同角度既可以看成面向過程,也可以看成面向對象

對于c++我們將其看成面向對象的語言,因為對標C語言,c++很多實現要比C語言簡單的多,不用造輪子(自己實現沒必要去浪費時間做的函數),當然你以更高的角度來看c++實現的函數也可以看成過程?,但是你得在更高的角度弄出點東西來,不然也改變不了啥。

二、類的引入和定義

2.1類的引入

回顧C語言實現棧時,需要有一個結構體存放定義的變量,而具體的實現需要額外定義函數完成壓棧、出棧、銷毀棧等。那么在c++中,結構體不僅可以定義變量,也可以定義函數了,同樣我們用棧來舉個例子

#include <iostream>
using namespace std;typedef int DataType;
struct stack
{//棧的初始化void Init(size_t capacity){_array = (DataType*)malloc(sizeof(DataType*) * capacity);if (nullptr == _array){perror("malloc fail");exit(-1);}_capacity = capacity;_size = 0;}//壓棧void Push(const DataType& data){_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 s;s.Init(5);s.Push(1);s.Push(2);s.Push(3);cout << s.Top() << endl;s.Destroy();return 0;
}

運行結果:

?

?從上面代碼可以看出,我們將變量的定義與函數的實現都放在結構體中,且這些變量可以在函數中使用,像這種將函數和變量都放在一個結構體中,在這種情況下,在c++中我們稱之為類,也就是說,C語言中的結構體在c++中升級成了類,當然類依然支持C語言中的結構體用法,畢竟c++兼容C語言嘛。那么接下來講解類定義的詳細用法

注意:上述結構體的定義,在c++中更喜歡用class來代替,其實struct和class還是有區別的,在類的訪問限定中會講解。

2.2類的定義

形式規則:

class className
{//類體:由成員函數和成員變量組成};//一定要注意后面的分號

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

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

以上是類定義的形式規則,但對于類中的成員函數定義和成員變量命名也有一定的規則。

成員函數的定義有兩種方式:

1.成員函數的聲明和定義都放在類體中,需要注意的是,編譯器可能會將其當成內聯函數處理。

?2.類聲明放在.h文件中,成員函數放在.cpp中,注意:成員函數名前需要加類名::

//聲明放在類的頭文件fun.h中
#include <iostream>
using namespace std;class Person
{
public://顯示基本信息void showinfo();
public:char* _name;char* _sex;int _age;
};//定義放在類的實現文件中
#include "fun.h"void Person::showinfo()
{cout << _name << "-" << _sex << "-" << _age << endl;
}int main()
{Person s;s._name = (char*)"張三";//由于張三是字面常量,類型為const char*而_name為char*類型s._sex = (char*)"男";s._age = 18;s.showinfo();return 0;
}

?運行結果:

一般情況下,最好采用第二種方式,但在之后的講解中,為了方便,可能會使用方式一。但在以后的工作中的話,那盡量使用方式二?。

成員變量命名規則的建議:

#include <iostream>
using namespace std;class Date
{
public:void Init(int year){//這里的year到底是成員變量還是函數形參,雖然知道左邊的year是定義的變量,而右邊的year是形參的year//但難免帶來一種令人不友好的感覺year = year;//所以一般建議定義的變量加上前綴或者后綴,對于我而言習慣加上前綴int _year = year;int year_ = year;int myear = year;}
private:int year;int _year;int year_;int myear;
};int main()
{Date d;d.Init(3);return 0;
}

注意:默認直接在類里面定義的函數就是inline函數,不需要加inline關鍵字,當然在編譯時展不展開函數還是取決編譯器 。

三、類的訪問限定符及封裝

3.1訪問限定符?

?在上述代碼中,看到我們加了public、private這類符號,是什么?

像這類符號我們稱之為訪問限定符,其實,這是c++實現封裝方式的必要手段,用類將對象的屬性和方法結合在一塊,讓對象更加完善,再通過訪問訪問限定符選擇性的將其接口提供給外部的用戶使用。

那具體是啥意思,訪問限定符有哪些?look

訪問限定符,用來限定類外是否能訪問類里面的成員,訪問限定符分為三類,public(公有)protected(保護)和private(私有)

訪問限定符說明:

1.public表示類外是可以訪問類里面被public修飾的成員,

2.protected和private表示類外不能直接訪問類里面被這兩個修飾的成員,他們是類似的,但還是有區別的,在繼承階段會講解。

3.訪問權限作用域從該訪問限定符出現的位置開始知道下一個訪問限定符出現時為止,如果后面沒有訪問限定符,作用域就到 } 即類結束

4.上面我們提到了class和struct是有區別,對于class,其成員的默認訪問權限為private,而struct為public(想想,雖然在c++中struct升級成了類,但依然支持C語言struct的用法,而C語言的struct成員都是可以直接被訪問的,不需要限定符。所以在這里struct的默認訪問權限是public)

注意:訪問限定符只在編譯時有用,當數據映射到內存后,沒有任何訪問限定符上的區別?

3.2封裝?

對于對象而言,無非就三大特性:封裝、繼承、多態。繼承和多態后面的章節再說,在類和對象階段,主要說的就是封裝,那么什么是封裝呢?

封裝:將數據和操作數據的方法進行有機結合,隱藏對象的屬性和實現細節,僅對外公開接口來和對象進行交互,封裝本質上是一種管理,讓用戶更方便實用類。說白了,不就是用類將他們包起來,再通過訪問限定符是否對外提供接口來實現交互。

在現實中,這個道理也是一樣的,像電腦,里面的零件都是被封裝的,然后提供一個USB接口給用戶使用。

四、類的作用域

在上面,我們提到并給出了代碼,類中的函數長的聲明和定義要分離,分離定義的函數怎么與類中聲明的函數進行聯系呢?跟命名空間一樣,類也是一個作用域,類的所有成員在類的作用域中,類外定義成員時,需要使用::作用域操作?符指明成員屬于那個類域

?

#include <iostream>
using namespace std;class Person
{
public://打印基本信息void printpersoninfo();
public:char _name[20];char _gender[3];int _age;
};//指明函數屬于哪個類域
void Person::printpersoninfo()
{cout << _name << "-" << _gender << "-" << _age << endl;
}int main()
{Person s;strcpy(s._name , "李四");strcpy(s._gender , "女");s._age = 19;s.printpersoninfo();return 0;
}

運行結果:

?

五、類的實例化

?僅僅有一個類不能對里面的內容進行訪問,而是要創建一個對象,通過對象來對類中的成員進行訪問。用類類型創建對象的過程,稱為類的實例化。

類是對對象進行描述的,是一個模型一樣的東西,限定類有那些成員,定義出一個類并沒有分配實際的內存空間來存儲它。必須實例化出對象才占有實際的物理空間,存儲類成員變量。且一個類可以實例化出多個對象。

比如:房子設計圖,就是一個類,設計圖描述了房子的各種信息,但是并沒有為這些信息建造出一個實際的空間。而只有建造出了真正的房子才是實例化,才分配了空間,且通過設計圖,也可以建造出多個房子,即實例化多個對象。再通過代碼來比較。

#include <iostream>
using namespace std;class Person
{
public://顯示基本信息void printpersoninfo();
public:char _name[20];char _gender[3];int _age;
};//指明函數屬于哪個類域
void Person::printpersoninfo()
{cout << _name << "-" << _gender << "-" << _age << endl;
}int main()
{Person s;//創建對象,且實例化多個對象Person t;Person u;strcpy(s._name , "李四");strcpy(t._name , "王五");strcpy(u._name , "李六");strcpy(s._gender, "女");strcpy(t._gender, "男");strcpy(u._gender, "男");s._age = 18;t._age = 19;u._age = 20;s.printpersoninfo();t.printpersoninfo();u.printpersoninfo();return 0;
}

運行結果:

?

六、類的對象大小的計算

6.1如何計算對象的大小

對于類中既有成員函數和成員變量,那么計算大小時是不是他們都要算呢?look

#include <iostream>
using namespace std;
class A
{
public:void PrintA(){cout << _a << endl;}
private:char _a;
};int main()
{A a1;cout << sizeof(a1);//猜猜多大return 0;
}

??運行結果:

通過結果發現,大小只有1,只計算了成員變量_a 的大小。這是怎么回事呢?且看下面分析

6.2類對象的存儲方式?

以正常的理解,當一個類實例化多個對象時,對象與對象之間的成員變量、成員函數是互不干擾的,但每個對象都會保存一份該成員函數的代碼,相同的代碼保存了多份,實例化了多份,就會形成空間的浪費。

所以給了對象的新的存儲方式,對象中只保存成員變量,而對于成員函數,只保存一份放在公共代碼區,每個對象調用該函數時,其實就是調用公共代碼區的該函數。所以在上述計算大小時,就不會算上成員函數。

再來看幾個例子:

空類:

#include <iostream>
using namespace std;class A3
{};int main()
{cout << sizeof(A3);return 0;
}

?運行結果:

為什么也是1呢,不應該是0嗎?明明是一個空類。

?OK,空類其實也可以實例化對象,這是前提。既然可以實例化對象,那么對象就會在內存分配空間,該對象有地址存在,只不過不存儲任何數據。如果,空類的大小為0表示該類沒有任何成員或對象。這意味著該類不占用內存空間。所以,為了標識該對象存在,編譯器規定該空類的大小為1

類中僅有成員函數:

#include <iostream>
using namespace std;class A2 {
public:void f2() {}
};int main()
{cout << sizeof(A2);return 0;
}

運行結果:

?

大小也是1,根據上面的解釋成員函數是放在公共代碼區,它們在內存中獨立存在,所以在計算大小時,?相當于計算空類的大小。

注意:計算類的大小依然遵守結構體內存對齊規則 !!!

七、類成員函數的this指針

7.1this指針的引出

#include <iostream>
using namespace std;class Date
{
public:void Init(int year = 2023, int month = 11, int day = 23)//給缺省值{_year = year;_month = month;_day = day;}void print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};int main()
{Date d1, d2;d1.Init(2023, 1, 11);d2.Init(2023, 1, 12);d1.print();d2.print();return 0;
}

運行結果:

對于上述類,實例化兩個對象,在表面上兩個對象都調用了Init函數時,但是在深層,函數怎么識別是誰調用?

c++引入了this指針來識別對象的調用:c++編譯器給每個“非靜態的成員函數”增加了一個隱藏的指針參數,讓該指針指向當前對象(函數運行時調用該函數的對象),在函數體中所有“成員變量”的操作,都是通過該指針去訪問。但要注意的是,this指針的所有操作對于程序員而言是透明的,即this指針不需要程序員來傳遞,編譯器會自動完成(可以認為是祖師爺設定好的)。

?7.2this指針的特性

1.this指針的類型:類類型* const,例如Date* const this。由于this被const修飾,所以在成員函數中,不能給this指針賦值。

2.只能在“成員函數”的內容使用

3.this指針本質上是“成員函數”的形參,當對象調用成員函數時,將對象地址作為實參傳遞給this形參。所以對象本身不存儲this指針。

4.this指針是“成員函數”第一個隱含的指針形參,一般情況由編譯器通過ecx寄存器自動傳遞,不需要用戶傳遞。

其實上述代碼的本質就是第二張圖的形式,對應著上述規則。注意的是在調用函數傳實參、以及函數的形參中不能顯示的將this指針寫出來,否則會報錯,但在成員函數中,可以顯示的寫出。

【面試題】?

1.this指針存在哪里?

將上述代碼,轉到反匯編后,可以發現,d1對象和d2對象存放到rcx寄存器。由此this指針在VS下存放在棧區的寄存器中。

2.this指針可以為空嗎 ?

先看代碼1:下面運行結果? A、編譯報錯 B、運行崩潰 C、正常運行

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

運行結果:

答案是正常運行,疑問的是p是空指針,為什么可以訪問Print函數?

其實這是一個誤區,想想前面講解的,首先成員函數不是放在對象中,而是在公共代碼區,其次在這里p并沒有去訪問Print函數,但是,如果沒有訪問該函數,那又是怎么尋得該函數。在C語言中,就知道了,是在編譯、鏈接階段通過符號表來尋找函數名字,所以在這里p的作用就是告訴編譯器它屬于A類,然后在編譯階段,A類中尋找是否存在Print函數。

代碼2:下面運行結果? A、編譯報錯 B、運行崩潰 C、正常運行

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

運行結果:

運行結果是崩潰的,對比第一個代碼,修改了成員函數的輸出,且成員變量并沒有初始化。本質上就是this->_a,這個就是空指針訪問了成員變量,而空指針并不指向任何有效的對象,因此其中的成員變量也是未定義的。?

綜上所述,雖然成員函數的代碼本身放在公共代碼區,但成員函數通常涉及對對象中的成員變量進行操作,這可能導致問題。因此最好不要讓this指針為空,以避免潛在的未定義行為。

end~

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

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

相關文章

【Docker】從零開始:7.Docker命令:容器命令及參數詳解

【Docker】從零開始&#xff1a;7.幫助啟動類命令 一、幫助啟動類命令啟動Docker停止Docker重啟Docker查看Docker狀態開機啟動查看docker概要信息查看docker總體幫助文檔查看docker命令幫助文檔 二、鏡像命令列出本地主機上的鏡像運行示例返回說明操作參數 搜索倉庫里的某個鏡像…

Python-Django的“日志功能-日志模塊(logging模塊)-日志輸出”的功能詳解

01-綜述 可以使用Python內置的logging模塊來實現Django項目的日志記錄。 所以與其說這篇文章在講Django的“日志功能-日志模塊-日志輸出”&#xff0c;不如說是在講Pthon的“日志功能-日志模塊-日志輸出”&#xff0c;即Python的logging模塊。 下面用一個實例來進行講解。 …

2023年亞太杯數學建模A題水果采摘機器人的圖像識別功能(免費思路)

中國是世界上最大的蘋果生產國&#xff0c;年產量約為 3500 萬噸。同時&#xff0c;中國也是世界上最大的蘋果出口國&#xff0c;世界上每兩個蘋果中就有一個出口到國。世界上每兩個蘋果中就有一個來自中國&#xff0c;中國出口的蘋果占全球出口量的六分之一以上。來自中國。中…

保護服務器免受攻擊:解析攻擊情境與解決之道

在數字化時代&#xff0c;服務器安全問題日益突出&#xff0c;因為它們是企業和個人網絡活動的核心。服務器被攻擊可能引發一系列問題&#xff0c;理解攻擊的不同情境以及采取相應的解決方法變得至關重要。 DDoS 攻擊&#xff08;分布式拒絕服務攻擊&#xff09; 情境&#xff…

基于51單片機超聲波測距汽車避障系統

**單片機設計介紹&#xff0c; 基于51單片機超聲波測距汽車避障系統 文章目錄 一 概要二、功能設計設計思路 三、 軟件設計原理圖 五、 程序六、 文章目錄 一 概要 基于51單片機的超聲波測距汽車避障系統是一種用于幫助汽車避免碰撞和發生事故的設備&#xff0c;以下是一個基本…

Visual Studio 2022安裝教程(千字圖文詳解),手把手帶你安裝運行VS2022以及背景圖設置

VS2022最新最全安裝教程 很高興你打開了這篇博客&#xff0c;接下來我們一起安裝并且使用VS2022吧 文章目錄 VS2022最新最全安裝教程一.官網下載二.安裝啟動三.項目測試1.創建新項目2.選擇我們使用的模板&#xff08;C空項目&#xff09;&#xff0c;繼續沖&#xff01;3.進入…

docker的使用方法

文章目錄 為什么要用dockerdocker安裝docker工作原理docker命令docker搭建練習docker可視化docker鏡像docker容器數據卷DockerFiledocker全流程Docker網絡原理docker composedocker swarm 為什么要用docker 官網&#xff1a;https://www.docker.com文檔地址&#xff1a;https:…

Stream流開發常用場景

一、Stream流 1.將某個集合中的金額字段相加 Bigdecimal amount list.stream().map(TransInfoEntity::getAmount).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); 2.將集合中某兩個字段組合成一個map Map<Long, String> map list.stream().collect(Collectors.t…

ImgUtil.scale會在某些圖片壓縮之后自動旋轉90度

需要的jar依賴&#xff1a; <dependency> <groupId>com.drewnoakes</groupId> <artifactId>metadata-extractor</artifactId> <version>2.16.0</version> <!-- 請檢查最新版本 …

ceph編譯報錯解決

1.執行./install-deps.sh提示deb無法安裝 解決方案&#xff1a; 直接使用dpkg -i來安裝deb&#xff0c;會提示缺失3個庫&#xff0c;庫名如下&#xff1a; cython python dh-systemd 其中cython和python手動安裝好后&#xff0c;也會依舊報錯。此時修改debian/control文件&…

系列五、聲明式事務(xml方式)

一、概述 聲明式事務(declarative transaction management)是Spring提供的對程序事務管理的一種方式&#xff0c;Spring的聲明式事務顧名思義就是采用聲明的方式來處理事務。這里所說的聲明&#xff0c;是指在配置文件中聲明&#xff0c;用在Spring配置文件中聲明式的處理事務來…

搞笑的客服日常,今天遇到的曬白客服,你遇到過嗎?

大家看看這個 https://blog.csdn.net/a871923942/article/details/129778391?utm_mediumdistribute.pc_feed_404.none-task-blog-2 今天遇到傻逼審核 開頭第一句就使用快捷話術 客服感覺自己發錯了 然后就撤回消息 第二次繼續發話術&#xff0c;說違規&#xff0c;審核不通過…

案例015:Java+SSM+uniapp基于微信小程序的校園防疫系統

文末獲取源碼 開發語言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 數據庫&#xff1a;mysql 5.7 開發軟件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序開發軟件&#xff1a;HBuilder X 小程序…

互聯網上門洗鞋店小程序

上門洗鞋店小程序門店版是基于原平臺版進行增強的&#xff0c;結合洗鞋行業的線下實際運營經驗和需求&#xff0c;專為洗鞋人和洗鞋店打造的高效、實用、有價值的管理軟件系統。 它能夠幫助洗鞋人建立自己的私域流量&#xff0c;實現會員用戶管理&#xff0c;實現用戶與商家的點…

【SpringCloud】微服務架構設計模式

一、聚合氣微服務設計模式 最常見、最簡單的設計模式&#xff0c;效果如圖所示&#xff1a; 聚合器調用多個服務實現應用程序所需的功能 它可以是一個簡單的 Web 頁面&#xff0c;將檢索到的數據進行處理并展示&#xff0c;也可以是一個更高層次的組合微服務&#xff0c;對…

記錄ruoyi-plus-vue部署的問題

ruoyi-vue-plus5.x 后端 ruoyi-vue-plus5.x 前端 前端本地啟動命令 # 克隆項目 git clone https://gitee.com/JavaLionLi/plus-ui.git# 安裝依賴 npm install --registryhttps://registry.npmmirror.com# 啟動服務 npm run dev# 構建生產環境 yarn build:prod # 前端訪問地址…

[C++歷練之路]vector的介紹以及底層模擬實現

W...Y的主頁 &#x1f60a; 代碼倉庫分享 &#x1f495; &#x1f354;前言&#xff1a; 我們學習了STL中的string以及其所有重要接口并進行了模擬實現&#xff0c;但是STL中包含的內容不止于此。學習了string之后繼續學習STL中的vector&#xff0c;學習成本會大大降低&#…

產品經理面試必看!To B和To C產品的隱秘差異,你了解多少?

大家好&#xff0c;我是小米&#xff0c;一位對技術充滿熱情的產品經理。最近在和小伙伴們交流中發現一個熱門話題&#xff1a;To B&#xff08;面向企業&#xff09;和To C&#xff08;面向消費者&#xff09;的產品經理究竟有何異同&#xff1f;這可是我們產品經理面試中的經…

互動直播項目 之 靜音邏輯 梳理

目錄 一、老師靜音學生 1、主播點擊聲音按鈕后 2、主進程收到后,調用接口,通知給學生