C++基礎從0到1入門編程(四)類和對象

系統學習C++
方便自己日后復習,錯誤的地方希望積極指正
往期文章:
C++基礎從0到1入門編程(一)
C++基礎從0到1入門編程(二)
C++基礎從0到1入門編程(三)
參考視頻:
1.黑馬程序員匠心之作|C++教程從0到1入門編程,學習編程不再難
2.系統化學習C++


1 從結構體到類

面向對象編程,一切都是對象,對象用類來描述
類:成員變量(屬性)和函數(操作方法)

class 類名
{
public:成員1的數據類型 成員名1;成員2的數據類型 成員名2;...成員n的數據類型 成員名n;
}

用類定義一個類的變量:創建(或實例化)一個對象
對象的成員變量和成員函數的作用域和生命周期與對象的作用域和生命周期相同

#include <iostream>
using namespace std;
class CGirl
{
public:string name;int    age;void setvalue(string name, int age);void show(){cout << "name = " << name << endl << "age = " << age << endl;}
};
void CGirl::setvalue(string name, int age)
{this->name = name;this->age = age;
}
int main()
{CGirl girl;girl.setvalue("BigDavid", 23);girl.show();return 0;
}

2 類的訪問權限

類的成員訪問權限:public、private、protected
在類的內部,無論成員聲名為何種權限,都可以訪問
在類的外部(定義類的代碼之外的代碼),只能訪問public成員

#include <iostream>
using namespace std;
class CGirl
{
private:string name;int    age;
public:void setvalue(string name, int age);
};
void CGirl::setvalue(string name, int age)
{this->name = name;this->age = age;
}
int main()
{CGirl girl;girl.setvalue("BigDavid", 23); // 可以通過return 0;
}

(1)在一個類定義中,private和public可以出現多次
(2)結構體成員缺省為public,類成員缺省為private
(3)private的意義在于隱藏類的數據和實現,把需要向外暴露的成員聲名為public

3 簡單使用類

(1)類的成員函數可以直接訪問該類其他的成員函數(可以遞歸)

#include <iostream>
using namespace std;
class CGirl
{
private:string name;int    age;int    times = 0;
public:void setvalue(string name, int age){this->name = name;this->age = age;show();}void show(){if (times++ > 10) return;cout << name << ' ' << age << endl;show();}
};int main()
{CGirl girl;girl.setvalue("BigDavid", 23);return 0;
}

(2)類的成員函數可以重載,可以用默認參數
(3)類的成員可以是任意數據類型(類中枚舉

class CGirl
{
private:string name;int    age;int    times = 0;enum sex {girl = 1, boy = 2};
public:void setvalue(string name, int age){this->name = name;this->age = age;show();}void show(){if (times++ > 10) return;cout << name << ' ' << age << endl;show();}
};

(4)可以為類的成員指定缺省值
(5)類可以創建對象數組
(6)對象可以作為實參傳遞給函數,一般傳引用
(7)用new動態創建對象,用delete釋放對象
(8)在類的外部,一般不直接訪問(讀和寫)對象的成員,而是用成員函數。數據隱藏是面向對象編程的思想
(9)對象一般不用memset()清空成員變量,可以寫一個專用于清空成員變量的成員函數

class CGirl
{
private:string name;int    age;int    times = 0;enum sex {girl = 1, boy = 2};
public:void setvalue(string name, int age){this->name = name;this->age = age;show();}void initdata(){name.clear();age = 0;}void show(){if (times++ > 10) return;cout << name << ' ' << age << endl;show();}
};

(10)對類和對象用sizeof運算意義不大,一般不用
(11)用結構體描述純粹的數據,用類描述對象
(12)在類的聲明中定義的函數都將自動成為內聯函數;在類的聲明之外定義的函數如果使用了inline限定符,也是內聯函數
(14)區分類的成員變量和成員函數的形參,把成員變量名加m_前綴或_后綴,如 m_name, name_
(15)類分文件編寫

4 構造函數和析構函數

構造函數:在創建對象時,自動的進行初始化工作
語法:類名(){...}

可以有參數,可以重載,可以有默認參數
創建對象只會自動調用一次,不能手工調用

析構函數:在銷毀對象前,自動完成清理工作
語法:~類名(){...}

沒有參數,不能重載
銷毀對象前只會自動調用一次,但可以手工調用

構造函數的細節
(1)如果沒有提供構造/析構函數,編譯器將提供空實現的構造/析構函數
(2)如果提供了構造/析構,編譯器將不提供空的構造/析構函數
(3)創建對象時,如果重載了構造函數,編譯器根據實參匹配相應的構造函數。沒有參數的構造函數也叫默認構造函數
(4)創建對象的時候不要在對象名后面加空的圓括號,編譯器誤認為是聲明函數。(如果沒有構造函數、構造函數沒有參數、構造函數的參數都有默認參數)
(5)在構造函數名后面加括號和參數不是調用構造函數,是創建匿名對象
(6) 接受一個參數的構造函數允許使用賦值語法將對象初始化為一個值(可能會導致問題)

CGirl girl = 10;

(7)第一行代碼構造函數和析構函數調用一次,下面兩行調用兩次

CGirl girl = CGirl("西施"20);  // 顯式創建對象。
CGirl girl;                   // 創建對象。
girl = CGirl("西施"20);        // 創建匿名對象,然后給現有的對象賦值

(8)用new/delete創建/銷毀對象時,也會調用構造/析構函數
(9)不建議在構造/析構函數中寫太多的代碼,可以調用成員函數。
(10)除了初始化,不建議讓構造函數做其他工作
(11)C++11支持使用統一的初始化列表

CGirl girl = {"BigDavid", 8};
CGirl girl {"BigDavid", 8};
CGirl* girl = new CGirl{"BigDavid", 8};

(12)如果類的成員也是類,創建對象的時候,先構造成員類;銷毀對象的時候,先析構自身,再析構成員類

5 拷貝構造函數

用一個已經存在的對象創建新的對象,不會調用(普通)構造函數,會調用拷貝構造函數。
如果類中沒有定義拷貝構造函數,編譯器將提供一個拷貝構造函數,它的功能是把已存在對象的成員變量賦值給新對象的成員變量

類名 新對象名(已存在的對象名)
類名 新對象名 = 已存在的對象名

拷貝構造函數的語法:
類名(const 類名& 對象名){...}
Tip:
(1)訪問權限必須是public
(2)函數名必須與類名相同
(3)如果類中定義了拷貝構造函數,編譯器將不提供默認的拷貝構造函數
(4)以值傳遞的方式調用函數時,如果實參為對象,會調用拷貝構造函數
(5)函數以值的方式返回對象時,可能會調用拷貝構造函數(VS會調用,Linux不會,g++編譯器做了優化)
(6)拷貝構造函數可以重載,可以有默認參數
類名(......,const 類名& 對象名,......){......}
(7)如果類中重載了拷貝構造函數卻沒有定義默認的拷貝構造函數,編譯器也會提供默認的拷貝構造函數

6 淺拷貝和深拷貝

在這里插入圖片描述

(1)如果一個對象修改了內存中的數據,會影響另一個對象
(2)其中一個對象釋放了內存,另一個對象的指針就成了野指針

#include <iostream>
using namespace std;class CGirl
{
public:string m_name;int    m_age;int*   m_ptr;CGirl(){m_name.clear();m_age = 0;m_ptr = nullptr;cout << "gouzaohanshu\n";}CGirl(const CGirl& gg){m_name = gg.m_name;m_age = gg.m_age;m_ptr = gg.m_ptr;}~CGirl(){delete m_ptr;m_ptr = nullptr;cout << "123\n";}void show(){cout << m_name << ' ' << m_age << ' ' << *m_ptr << endl;}
};int main()
{CGirl g1;g1.m_name = "BigDavid";g1.m_age = 23;g1.m_ptr = new int(3);g1.show();CGirl g2(g1);*g2.m_ptr = 8;g1.show();g2.show();
}

在這里插入圖片描述

指針A指向一塊內存,重新分配一塊相同大小的內存,讓指針B指向新內存。再把指針A指向的內存中的數據拷貝到新內存中

CGirl(const CGirl& gg)
{m_name = gg.m_name;m_age = gg.m_age;m_ptr = gg.m_ptr;
}

修改上面代碼

CGirl(const CGirl& gg)
{m_name = gg.m_name;m_age = gg.m_age;// 分配內存m_ptr = new int;// *m_ptr = *gg.m_ptr; // 拷貝數據memcpy(m_ptr, gg.m_ptr, sizeof(int)); // 拷貝數據
}

7 初始化列表

構造函數的執行分為兩個階段:初始化階段和計算階段
初始化階段先于計算階段執行
構造函數除了有形參列表和函數體之外,還可以有初始化列表。
初始化列表的語法:
類名(形參列表):成員1(值),成員2(值),...,成員n(值) {...}
Tip:
(1)如果成員已經在初始化列表中,則不應該在構造函數中再次賦值,會覆蓋之前初始化列表的值
(2)初始化列表中的括號可以是具體值,也可以是構造函數的形參名,還可以是表達式
(3)初始化列表與賦值有本質的區別,如果成員是類,使用初始化列表調用的是成員類的拷貝構造函數,而賦值則是先創建成員類的對象(將調用成員類的普通構造函數),然后再賦值
(4)成員是類,初始化列表對性能略有提升
(5)如果成員是常量和引用,必須使用初始列表,因為常量和引用只能在定義的時候初始化
(6)如果成員是沒有默認構造函數的類,則必須使用初始化列表
(7)拷貝構造函數也可以有初始化列表
(8)類的成員變量可以不出現在初始化列表中
(9)構造函數的形參先于成員變量初始化

#include <iostream>
using namespace std;class CBoy
{
public:string m_xm;CBoy(){m_xm.clear();cout << "CBoy()\n";}CBoy(string xm){m_xm = xm;cout << "CBoy(string xm)\n";}CBoy(const CBoy& bb){m_xm = bb.m_xm;cout << "CBoy(const CBoy &bb)\n";}
};
class CGirl
{
public:string m_name;const int    m_age;CBoy  m_boy;CGirl(string name, int age, CBoy &boy):m_name(name), m_age(age) , m_boy(boy){//m_boy.m_xm = boy.m_xm;cout << "CGirl(name,age,boy)\n";}void show(){cout << m_name << ' ' << m_age << ' ' << m_boy.m_xm << endl;}
};int main()
{CBoy boy("BigDavid");CGirl g1("qwe", 18, boy);g1.show();
}

8 對象和類 - const修飾成員函數

在類的成員函數后面加const關鍵字,表示在成員函數中保證不會修改調用對象的成員變量
(1)mutable可以突破const限制,被mutable修飾的成員變量,將永遠處于可變的狀態
(2)非const函數可以調用const和非const函數
(3)const成員函數不能調用非const成員函數
(4)非const對象可以調用const修飾的成員函數和非const修飾的成員函數
(5)const對象只能調用const修飾的成員函數,不能調用非cosnt修飾的成員函數
Tip:

const CGirl g1("asd", 18);
g1.show();

常對象只能訪問加了const的成員函數
創建g1時,上面代碼相當于訪問了構造函數,但是構造函數沒有加const,也沒有報錯,如果給構造函數加了const,報錯了,原因是構造函數或析構函數不允許使用類型限定符

9 this指針

如果類的成員函數涉及多個對象,在這種情況下需要使用this指針
this指針存放了對象的地址,被作為隱藏參數傳遞給了成員函數,指向調用成員函數的對象(調用者對象)

每個成員函數(包括構造函數和析構函數)都有一個this指針,可以用它訪問調用者對象的成員。可以解決成員變量名和函數形參名相同的問題

int aa;
void func(int aa)
{this->aa = aa;
}

*this可以表示對象
如果在成員函數的括號后面使用const,那么將不能通過this指針修改成員變量

#include <iostream>
using namespace std;class CGirl
{
public:string m_name;int    m_yz;CGirl(const string &name, int yz){m_name = name;m_yz = yz;}void show() const{cout << m_name << " beautiful\n";}const CGirl& pk(const CGirl& g) const{if (g.m_yz < m_yz) return g;return *this;}
};int main()
{CGirl g1("a", 5);CGirl g2("b", 6);CGirl g3("c", 1);const CGirl& g = g1.pk(g2).pk(g3); // c beautifulg.show();
}

10 類的靜態成員

類的靜態成員:靜態成員變量靜態成員函數
靜態成員可以實現多個對象之間的數據共享,比全局變量更安全
static關鍵字把類的成員變量聲名為靜態,表示在程序中(不僅是對象)是共享的

靜態成員變量不會在創建對象的時候初始化,必須在程序的全局區用代碼清晰的初始化(用范圍解析運算符 ::)
靜態成員使用類名加范圍解析運算符 :: 就可以訪問,不需要創建對象。
如果把類的成員聲明為靜態的,就可以把它與類的對象獨立開來(靜態成員不屬于對象)

靜態成員變量在程序中只有一份(生命周期與程序運行期相同,存放在靜態存儲區的),不論是否創建了類的對象,也不論創建了多少個類的對象

靜態成員函數,只能訪問靜態成員,不能訪問非靜態成員
非靜態成員函數可以訪問靜態成員

靜態成員函數沒有this指針
const靜態成員變量可以在定義類的時候初始化

#include <iostream>
using namespace std;class CGirl
{static int m_age;
public:string     m_name;CGirl(const string& name, int age){m_name = name;m_age = age;}void show(){cout << "name: " << m_name << endl;}static void showage(){cout << m_age << endl;}
};int CGirl::m_age = 22;int main()
{CGirl g1("a", 21), g2("b", 23), g3("c", 24);g1.show(); g1.showage();g2.show(); g2.showage();g3.show(); g3.showage();CGirl::showage();
}

11 簡單對象模型

C語言本身沒有支持數據和函數之間的關聯性,數據和處理數據的操作是分開的。

C++用類描述抽象數據類型(ADT),在類中定義了數據和函數,把數據函數關聯起來

對象維護了多個指針表,表中存放了成員和地址的對應關系
在這里插入圖片描述
數據成員:非靜態、靜態
函數成員:非靜態、靜態、virtual

對象內存的大小:
(1)所有非靜態數據成員的大小
(2)由內存對齊而填補的內存大小
(3)支持virtual成員而產生的額外負擔

靜態成員變量屬于類,不計算在對象的大小之內
成員函數是分開存儲的,不論對象是否存在都占用存儲空間,在內存中只有一個副本,也不計算在對象大小之內
用空指針可以調用沒有用到this指針的非靜態成員函數(如果成員函數中沒有用到非靜態成員變量,就可以用空指針調用它)
在沒有創建對象的情況下,訪問非靜態成員變量就是訪問空指針

#include <iostream>
#include <cstring>
using namespace std;class CGirl
{
public:char       m_name[3];int        m_bh;static int m_age;CGirl(){memset(m_name, 0, sizeof(m_name)); m_age = 0;}~CGirl() {}void showname(){//if (this == nullptr) return;cout << "asd" << endl;}void showage(){cout << m_age << endl;}
};int CGirl::m_age;
int aaa;
void func() {}int main()
{CGirl g;CGirl* g1 = nullptr;g1->showname(); // asd
}

對象的地址是第一個非靜態成員變量的地址,如果類中沒有非靜態成員變量,編譯器會隱含的增加一個1字節的占位成員

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

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

相關文章

Spring boot @Bean

Bean 是 Spring 框架中的一個注解&#xff0c;它的作用是將一個方法標記為 Spring 容器中的一個 Bean。具體來說&#xff0c;當你在一個類中使用 Bean 注解修飾一個方法時&#xff0c;這個方法將會在 Spring 容器中執行&#xff0c;并且返回的對象也會被 Spring 容器管理。 Be…

香蕉派BPI-M4 Zero單板計算機采用全志H618,板載2GRAM內存

Banana Pi BPI-M4 Zero 香蕉派 BPI-M4 Zero是BPI-M2 Zero的最新升級版本。它在性能上有很大的提高。主控芯片升級為全志科技H618 四核A53, CPU主頻提升25%。內存升級為2G LPDDR4&#xff0c;板載8G eMMC存儲。它支持5G WiFi 和藍牙, USB接口也升級為type-C。 它具有與樹莓派 …

23. 深度學習 - 多維向量自動求導

Hi, 你好。我是茶桁。 前面幾節課中&#xff0c;我們從最初的理解神經網絡&#xff0c;到講解函數&#xff0c;多層神經網絡&#xff0c;拓樸排序以及自動求導。 可以說&#xff0c;最難的部分已經過去了&#xff0c;這節課到了我們來收尾的階段&#xff0c;沒錯&#xff0c;生…

Android定位make有哪些target $ mgrep “build“ |grep target |grep image

環境 $ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 85 model name : Intel(R) Xeon(R) Gold 5122 CPU 3.60GHz stepping : 4 microcode : 0x2006e05 cpu MHz : 1200.086 cache…

大模型訓練效率提升至2.6倍,騰訊Angel機器學習框架升級

在算力緊缺的背景下&#xff0c;如何提升大模型訓練和推理的效率&#xff0c;并降低成本&#xff0c;成為業界關注的焦點。 11月23日&#xff0c;騰訊披露&#xff0c;騰訊混元大模型背后的自研機器學習框架Angel再次升級&#xff0c;大模型訓練效率提升至主流開源框架的2.6倍…

SQL Server刪除重復數據只保留一條

介紹 最近在導入數據庫數據, 有時候給的數據源文件,存在重復數據, 需要清除但是還需要保留一條記錄的需求. 本文將介紹如何使用SQL Server來實現這個需求。 流程 下面是實現刪除重復數據的流程&#xff0c;我們可以用表格展示每個步驟&#xff1a; 步驟 描述 步驟一 先…

算法通關村第十二關-白銀挑戰字符串經典題目

大家好我是蘇麟 , 今天帶來字符串相關的題目 . 大綱 反轉問題字符串反轉K個一組反轉僅僅反轉字母反轉字符串中的單詞 反轉問題 字符串反轉 描述 : 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 s的形式給出。 題目 : LeetCode 344. 反轉…

webshell之擴展免殺

由于很多企業為了防止源碼泄露&#xff0c;都會使用加密擴展將代碼進行加密&#xff0c;那么我們就可以就將計就計&#xff0c;將webshell也利用擴展加密&#xff0c;將特征消除&#xff0c;從而達到免殺的效果 1.php-beast 擴展地址 下載dll&#xff0c;并添加至ext中 在php…

優化數據分析——理解與運用各類指標

寫在開頭 數據分析在當今信息時代扮演著至關重要的角色&#xff0c;而指標則是我們理解數據、揭示模式、支持決策的關鍵工具。本文將深入討論各類指標的應用場景和解讀方法&#xff0c;以幫助更全面、深入地理解數據。 1. 中心趨勢指標 1.1 均值&#xff1a;更深層次的理解 …

MySQL中自增id用完怎么辦?

MySQL中自增id用完怎么辦&#xff1f; MySQL里有很多自增的id&#xff0c;每個自增id都是定義了初始值&#xff0c;然后不停地往上加步長。雖然自然數是沒有上限的&#xff0c;但是在計算機里&#xff0c;只要定義了表示這個數的字節長度&#xff0c;那它就有上限。比如&#…

【2023持續更新】網絡安全工程師常用工具集合

文章目錄 SQL注入檢測 SQL注入檢測 https://github.com/r0oth3x49/ghauri

python數據結構與算法-15_堆與堆排序

堆(heap) 前面我們講了兩種使用分治和遞歸解決排序問題的歸并排序和快速排序&#xff0c;中間又穿插了一把樹和二叉樹&#xff0c; 本章我們開始介紹另一種有用的數據結構堆(heap)&#xff0c; 以及借助堆來實現的堆排序&#xff0c;相比前兩種排序算法要稍難實現一些。 最后我…

Linux開發工具(含gdb調試教程)

文章目錄 Linux開發工具&#xff08;含gdb調試教程&#xff09;1、Linux 軟件包管理器 yum2、Linux開發工具2.1、Linux編輯器 -- vim的使用2.1.1、vim的基本概念2.1.2、vim的基本操作2.1.3、vim正常模式命令集2.1.4、vim末行模式命令集 2.2、vim簡單配置 3、Linux編譯器 -- gcc…

HIVE SQL取整函數匯總

目錄 int()round(double a)round(double a,int d)floor()ceil() int() 向零取整&#xff0c;即向接近零的方向取整。 int(5.6)輸出&#xff1a;5 int(-5.6)輸出&#xff1a;-5 round(double a) 四舍五入取整 select round(5.6)輸出&#xff1a;6 select round(-5.6)輸出&…

關于前端處理后端輪詢的操作 (總結)

使用場景&#xff1a;前端首次發起請求獲取數據&#xff0c;若失敗則每隔1s發起一次知道成功獲取數據為止解決方案&#xff1a; 使用輪詢操作&#xff0c;涉及定時器的使用和關閉 &#xff08;使用vue2代碼為例) data() {return {pollingResult_en: null, // 處理輪詢結果bizI…

redis之cluster集群

1、redis-cluster集群&#xff1a;redis3.0引入的分布式存儲方案 2、集群&#xff1a;由多個node節點組成&#xff0c;redis數據分布在這些節點之中 &#xff08;1&#xff09;在集群之中也分主節點和從節點 &#xff08;2&#xff09;自帶哨兵模式 3、redis-cluster集群的…

騰訊云 小程序 SDK對象存儲 COS使用記錄,原生小程序寫法。

最近做了一個項目&#xff0c;需求是上傳文檔&#xff0c;文檔類型多種&#xff0c;圖片&#xff0c;視頻&#xff0c;文件&#xff0c;doc,xls,zip,txt 等等,而且文檔類型可能是大文件&#xff0c;可能得上百兆&#xff0c;甚至超過1G。 騰訊云文檔地址&#xff1a;https://c…

Java接口自動化測試系列[V1.0.0][概述]

基礎知識 在TCP/IP中&#xff0c;HTTP屬于傳輸層協議&#xff0c;該協議采用的是Request-Response的模式&#xff0c;且該協議是無狀態的&#xff0c;也就是后續如果要用到前面的信息必須重新請求重新獲取&#xff1b;HTTP通過SSL/TSL加密成為HTTPS&#xff0c;與HTTP相比HTTP…

PC端頁面進去先出現加載效果

自定義指令v-loading&#xff0c;只需要綁定Boolean即可 v-loading“loading” <el-table :data"list" border style"width: 100%" v-loading"loading"><el-table-column align"center" label"序號" width"5…

開發板啟動進入系統以后再掛載 NFS 文件系統, 這里的NFS文件系統是根據正點原子教程制作的ubuntu_rootfs

如果是想開發板啟動進入系統以后再掛載 NFS 文件系統&#xff0c;開發板啟動進入文件系統&#xff0c;開發板和 ubuntu 能互相 ping 通&#xff0c;在開發板文件系統下新建一個目錄 you&#xff0c;然后執行如下指令進行掛載&#xff1a; mkdir mi mount -t nfs -o nolock,nfsv…