主頁 > 軟體設計 > C++程式開發技巧

C++程式開發技巧

2023-06-05 08:16:57 軟體設計

引言

類(class)的使用分為兩種——基于物件(object Based)和面向物件(object oriented)

基于物件是指,程式設計中單一的類,和其他類沒有任何關系

單一的類又分為:不帶指標的類(class without pointer members)和帶指標的類(class with pointer members)

面向物件則是類(class)中涉及了類之間的關系:復合(composition)、委托(delegation)、繼承(inheritance)

1.頭檔案的防御式宣告

#ifndef xxx
#define xxx
...
#endif

在撰寫頭檔案時應該有這樣的一種習慣

目的是避免多次重復包含同一個頭檔案,否則會引起變數及類的重復定義

2.使用初始化串列的好處

  • 只有建構式這類函式具有“初始化串列”這一特性
  • 從結果上來看,建構式時使用初始化串列和在類內賦值是一樣的,但我們都知道一個變數必須先初始化然后才被賦值,而初始化串列顧名思義是只執行初始化這一步,在類內賦值時就要先初始化再被賦值,所以從執行效率上講,使用初始化串列會更快,也更簡潔

3.設計模式:singleton(單例類)

  • demo:
class A {
public:
    static A & getInstance();
    setup() {...}
private:
    A();
    A(const A & rhs);
    ...
}

A & A::getInstance()
{
    static A a;
    return a;
}

...
//外部介面
A::getInstance().setup();
  • 原理:將建構式設定為私有屬性,同時設定一個靜態函式介面回傳一個該類物件

  • 作用:保證每一個類僅有一個實體,并為它提供一個全域訪問點

  • 單例模式(Singleton)的主要特點不是根據用戶程式呼叫生成一個新的實體,而是控制某個型別的實體唯一性,它擁有一個私有建構式,這確保用戶無法通過new直接實體它,除此之外,該模式中包含一個靜態私有成員變數instance與靜態公有方法Instance(),Instance()方法負責檢驗并實體化自己,然后存盤在靜態成員變數中,以確保只有一個實體被創建,

    這種模式主要有以下特征或條件:

    1. 有一個私有的無參建構式,這可以防止其他類實體化它,而且單例類也不應該被繼承,如果單例類允許繼承那么每個子類都可以創建實體,這就違背了Singleton模式“唯一實體”的初衷,
    2. 單例類被定義為sealed,就像前面提到的該類不應該被繼承,所以為了保險起見可以把該類定義成不允許派生,但沒有要求一定要這樣定義,
    3. 一個靜態的變數用來保存單實體的參考,
    4. 一個公有的靜態方法用來獲取單實體的參考,如果實體為null即創建一個,
  • 參考:

    設計模式詳解:Singleton(單例類)_singleton類_p_帽子戲法的博客-CSDN博客

    單例模式(Singleton)的6種實作 - JK_Rush - 博客園 (cnblogs.com)

4.常成員函式的重要性

  • 如果一個成員函式不改變類的資料成員時,就把它宣告為常函式,這是一個好的習慣

  • 當實體化一個常物件時,常物件要求不能改變資料成員,如果成員函式不加const,將無法呼叫此成員函式,編譯器不會通過,即使此函式確實沒有改變資料成員;同時,即使成員函式被宣告為了常函式,實體化一個普通物件時依然可以呼叫,

  • 簡單來說,不宣告為常成員函式可能不會有問題,但宣告為常成員函式能確保一定不出問題

5.如何解釋成員函式接收同類物件引數...

  • 問:如何解釋一個類的成員函式在接收同類物件的引數(比如拷貝建構式)可以直接呼叫該物件的任何成員,明明既不是友元也不是嵌套?

  • 答:相同class的各個objects互為friends(友元)

6.設計一個類要考慮什么

  • 目的:高效、安全、簡潔、嚴密
  • 1.資料成員私有
  • 2.引數傳遞和回傳值優先考慮用參考(傳遞的是地址值,這樣不管傳遞的資料記憶體占用多大,依然固定傳入四個位元組,即使當傳遞字符這樣小于四個位元組時用值傳遞確實比參考或指標傳遞更快一些,但不必考慮這些細枝末節)
  • 3.建構式優先去使用初始化串列
  • 4.能宣告為常成員就宣告為常成員

7.回傳值加不加參考

  • 取決于回傳的值是否要改變、是否可以改變,前者由我們決定,后者由語法限制

兩個案例

class A
{
    int value;
    ...
};

...
    
A& fun1(A* x, const A& y)
{
    x.value += y.value; //第一引數會改變,第二引數不會改變
    return *x
}
class B
{
    int value;
    ...
};

...

B fun2(const B& x, const B& y)
{
	//第一引數和第二引數都不會改變
	return B(x.value + y.value);
}

8.運算子多載成員函式的思考

e.x.

inline complex&
_doapl(comlex* ths, const complex& r)
{
	...
	return *ths;
}

inline complex&
complex::operator += (const comlex& r)
{
	return _doapl(this, r);
}

...

comlex c1(2,1), c2(3), c3;
//c2 += c1;
//c3 += c2 += c1;

當多載一個二元運算子為成員函式時,我們知道多載函式除了右運算元是我們傳遞的,函式還會默認用一個this指標,來接收左運算元

那么可能會有疑問,我們想要改變的是左運算元,而且由于傳遞的是指標,函式內也確實可以改變,那回傳值又有什么用呢,宣告為空不就行了,

當我們使用多載運算子時只是像被注釋的第一行代碼一樣,那么回傳值確實不重要,但是當我們使用的形式像被注釋的第二行代碼時,那么回傳值就很重要,因為c2.+=(c1) 這個函式的回傳值就是 c3 += () 函式的引數

9.temp object(臨時物件)

  • 語法: typename ();
  • 生存期:僅宣告那一行
  • 回傳值是臨時物件時不難return by reference

10.<<多載的一些注意點

  • 只能多載為非成員函式

  • 左運算元固定為系統定義的 ostream 型別,且為非常量參考

    • 非常量:向流寫入內容其實就是改變了它的狀態
    • 參考:無法復制一個 ostream 物件
    • 注:ostream 類與 istream 類一樣,它的的拷貝建構式和賦值函式都是保護型別的,所以 ostream 是不允許拷貝或者賦值的,所以它也不能直接作為回傳型別和引數傳遞,很多時候需要使用參考來進行傳遞,
  • 最好加回傳值且為參考,原因前面已經說明,且我們對于連續呼叫<<的頻率要大得多

  • 連續呼叫時的呼叫順序

    complex c1, c2;
    cout << c1 << c2;
    //先執行 <<(cout, c1) 函式
    //回傳的 ostream型別的cout的參考 又作為<<(ostream &, c2)的第一引數 
    
  • 在語法上我們當然也可以多載為成員函式,只要左運算元為自定義型別即可,但這樣并不符合我們通常的書寫習慣

11.Big Three(三位一體原則)

  • 三大件:拷貝構造、拷貝賦值、解構式

  • 解釋:當一個類需要我們去主動設計解構式時,那它很大概率也需要一個拷貝建構式和賦值運算子多載成員函式

  • 應用:當一個類具有指標成員時(class with point member)或者說當我們設計了一個有動態記憶體管理的類時

  • 原因:

    • 解構式角度:默認解構式會僅洗掉指向物件的指標,而洗掉一個指標不會釋放指標指向物件占用的記憶體,最侄訓導致記憶體泄露

    • 拷貝構造角度:默認的建構式是淺拷貝,復制的只是指標也就是地址值,這樣導致兩個物件共享一個記憶體空間,這是十分危險的,當其中一個物件被洗掉后,解構式將釋放那片共享的記憶體空間,接下來對這片已經釋放了記憶體的任何參考都將會導致不可遇見的后果,

    • 賦值運算角度:

      賦值相比于拷貝構造要考慮更多

      首先是自我賦值判斷,如果不判斷,當左右運算元指向的是同一個地址時,會造成將左運算元物件的元素洗掉并釋放其占用的記憶體,同時由于左右運算元指向同一物件,導致右運算元同時被洗掉,但接下來還要將右操作物件復制,這會造成不可預知的結果,這也被稱為證同測驗,

      其次是進行三步必要操作:

      • 釋放已有記憶體
      • 開辟新的記憶體
      • 內容賦值
  • 代碼示例:

#ifndef __MYSTRING__
#define __MYSTRING__

class String
{
public:                                 
   String(const char* cstr=0);//建構式
   String(const String& str);//拷貝建構式           
   String& operator=(const String& str);//多載=運算子 
   ~String();//解構式                               
   char* get_c_str() const { return m_data; }//成員函式,回傳指向字符陣列首地址的指標
private:
   char* m_data;//字符陣列指標
};

#include <cstring>

//建構式
inline
String::String(const char* cstr)
{
   //開辟記憶體、計算長度、內容拷貝
   if (cstr) {
      m_data = https://www.cnblogs.com/BlickWinkel/p/new char[strlen(cstr)+1];
      strcpy(m_data, cstr);
   }
   else {   
      m_data = new char[1];
      *m_data ='\0';
   }
}

//解構式
inline
String::~String()
{
   delete[] m_data;//釋放指標指向空間
}

//多載=
inline
String& String::operator=(const String& str)
{
   //檢測自我賦值(self assignment)
   if (this == &str)
      return *this;

   delete[] m_data;
   m_data = https://www.cnblogs.com/BlickWinkel/p/new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
   return *this;
}

//建構式
inline
String::String(const String& str)
{
   m_data = new char[ strlen(str.m_data) + 1 ];
   strcpy(m_data, str.m_data);
}

#include 
using namespace std;

//多載<<
ostream& operator<<(ostream& os, const String& str)
{
   os << str.get_c_str();
   return os;
}

#endif

12.new和建構式,delete和解構式

  • 當我們使用new創建了一個指向類的物件的指標時

    這里的new干了三件事:

    • 呼叫 operator new 函式,這個函式內部又呼叫了malloc函式來分配記憶體
    • operator new 函式回傳的是空指標,顯式轉換為型別別指標后賦值給我們創建的指標
    • 呼叫指標指向物件的建構式

  • 當我們使用delete釋放一個指向物件的指標時
    • 首先呼叫物件的解構式,這里該類的解構式又用delete釋放了類內動態分配的陣列指標
    • 然后再釋放這個指向物件的指標
    • 結合本例來看,就是delete ps,先delete它指向的成員,再delete它自己

13.malloc()動態分配記憶體的結構

  • 紅色部分是 cookie ,記錄記憶體分配的總大小,就是圖中的41,其最低位用于表示是否已分配(1表示已分配,0表示已回收),之所以最低位可以變,是因為分配的記憶體總空間一定是16的倍數,其16進制表示時最低位一定為0,也就是說這個位置是空出來的,剛好用來表示記憶體狀態,每一個 new 的物件都會有上下兩個 cookie,來預先申請一塊記憶體池,然后供物件實體化,

  • 綠色部分是呼叫malloc()時向系統申請的記憶體,該函式回傳時,也會回傳這塊區域開頭的指標,

  • 綠色部分上下兩塊 gap 預先被填充為了0xfdfdfdfd,用來分隔客戶可以使用的記憶體區和不可使用的記憶體區,同時,當這塊記憶體被歸還時,編輯器也可以通過下gap的值區判斷當前記憶體塊是否被越界使用了

  • 從gap向上連續的7個記憶體空間共同組成了debug header,從上向下標號為1-7

    • 1、2兩塊空間保存了兩根指標,目的是使多個記憶體塊連接成鏈表,
    • 3空間保存了申請本記憶體塊的檔案名
    • 4空間保存了申請本記憶體塊的代碼行數
    • 5空間記錄了本記憶體塊中實際可以被用戶使用的記憶體空間的大小
    • 6空間記錄了當前記憶體塊的流水號,即是鏈表中的第幾個,從1開始
    • 7空間記錄了當前記憶體塊被分配的形式
  • 填補區pad

參考:

https://zhuanlan.zhihu.com/p/492161361

https://www.cnblogs.com/zyb993963526/p/15682014.html#_label2

https://blog.csdn.net/qq_61500888/article/details/122170203

14.array new 搭配 array delete

動態分配陣列時要注意的:

  • 其記憶體區域相比上面所提到的多了一個記憶體塊用來記錄陣列長度(分配物件數量)
  • 當申請記憶體后,回傳的指標指向資料開始處,而使用 delete[] 釋放時,指標會指向它的上一塊,也就是記錄陣列長度的那一塊,從而可以根據物件的數量呼叫相應次數的解構式,如果使用 delete 釋放的話,它不會去獲取物件的長度,而是只呼叫指標指向的那一個物件的解構式,
  • 如果物件的型別是內置型別或者是無自定義的解構式的型別別,是可以使用 delete 來釋放 new[] 物件的,但是,如若不然,使用 delete 來釋放物件,物件所分配的記憶體空間雖然會照樣全部釋放,但是只會呼叫第一個物件的解構式,這就導致記憶體泄漏,所以,養成良好的習慣,new [] 必 delete []

15.this指標

類的每一個非靜態成員函式(包括建構式、拷貝構造等)都隱含著一個指標形參名為this,當物件呼叫成員函式時就會隱含傳遞該物件的地址給它,這也是為什么一個類的成員函式雖然只有一份但也會根據接收的訊息不同產生不同的行為,而靜態成員函式不隱含this指標,所以即使呼叫它的物件不同維護的依然是同一段代碼

16.namespace

三個案例:

  • 使用using引入一個命名空間的全部
  • 使用using引入一個命名空間的個體
  • 不用using,使用時手動引入

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/554294.html

標籤:面向對象

上一篇:設計模式詳解之抽象工廠模式--企業的運行模式

下一篇:返回列表

標籤雲
其他(160346) Python(38201) JavaScript(25475) Java(18185) C(15236) 區塊鏈(8269) C#(7972) AI(7469) 爪哇(7425) MySQL(7231) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5873) 数组(5741) R(5409) Linux(5346) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4582) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2434) ASP.NET(2403) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1981) 功能(1967) HtmlCss(1952) Web開發(1951) C++(1928) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1879) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • C++程式開發技巧

    ## 引言 > 類(class)的使用分為兩種——基于物件(object Based)和面向物件(object oriented) > > 基于物件是指,程式設計中單一的類,和其他類沒有任何關系 > > 單一的類又分為:不帶指標的類(class without pointer members)和帶指 ......

    uj5u.com 2023-06-05 08:16:57 more
  • 設計模式詳解之抽象工廠模式--企業的運行模式

    # 前言 本文主要講述設計模式中的**抽象工廠模式**,文中使用通俗易懂的案例,使你更好的學習本章知識點并理解原理,做到有道無術。 # 一.什么是抽象工廠模式 抽象工廠是23種設計模式中**創建型模式**的一種,抽象工廠是由多個工廠組合而成。 上一章我們提到的工廠模式只存在一個抽象角色,而抽象工廠是 ......

    uj5u.com 2023-06-02 11:52:47 more
  • 微服務架構基本原理學習筆記(二)

    上一篇:微服務架構基本原理學習筆記(一) 三、微服務架構 從一個已有的單體架構的應用程式開始進行微服務架構的重構往往是一個不錯的選擇。隨著業務量和功能的增加,我們可以考慮使用微服務架構來擴充應用程式中原有的功能,或者每次添加新功能時,都為其創建一個新的微服務。這比從一開始就選擇使用微服務架構進行設計 ......

    uj5u.com 2023-06-01 14:53:04 more
  • 微服務架構基本原理學習筆記(二)

    上一篇:微服務架構基本原理學習筆記(一) 三、微服務架構 從一個已有的單體架構的應用程式開始進行微服務架構的重構往往是一個不錯的選擇。隨著業務量和功能的增加,我們可以考慮使用微服務架構來擴充應用程式中原有的功能,或者每次添加新功能時,都為其創建一個新的微服務。這比從一開始就選擇使用微服務架構進行設計 ......

    uj5u.com 2023-06-01 14:37:11 more
  • Java設計模式之單例模式詳解--獨一無二的事物

    # 前言 本文主要講述**單例模式**,文中使用通俗易懂的案例,使你更好的學習本章知識點并理解原理,做到有道無術。 # 一. 什么是單例模式 單例模式是23種設計模式中**創建型模式**的一種,通過單例模式的方法創建的類在當前行程或者執行緒中只有一個實體。單例模式有兩種比較常見的實作方式:**餓漢式* ......

    uj5u.com 2023-06-01 09:25:15 more
  • 03.外觀模式

    ### 外觀式定義 為子系統中的一組介面提供一個一致的界面,Facade 模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。 #### 界面 在這里提到的界面,主要指的是從一個組件外部來看這個組件,能夠看到什么,這就是這個組件的界面,也就是所說的外觀。 #### 介面 在這里提到的介面,主 ......

    uj5u.com 2023-06-01 09:25:11 more
  • 解釋器模式(Interpreter Pattern)

    ### 解釋器模式(Interpreter Pattern) #### 一、定義 解釋器模式(Interpreter Pattern)提供了評估語言的語法或運算式的方式,它屬于行為型模式。這種模式實作了一個運算式介面,該介面解釋一個特定的背景關系。這種模式被用在 SQL 決議、符號處理引擎等。 給定一 ......

    uj5u.com 2023-06-01 09:23:50 more
  • Java設計模式之單例模式詳解--獨一無二的事物

    # 前言 本文主要講述**單例模式**,文中使用通俗易懂的案例,使你更好的學習本章知識點并理解原理,做到有道無術。 # 一. 什么是單例模式 單例模式是23種設計模式中**創建型模式**的一種,通過單例模式的方法創建的類在當前行程或者執行緒中只有一個實體。單例模式有兩種比較常見的實作方式:**餓漢式* ......

    uj5u.com 2023-06-01 09:23:36 more
  • 03.外觀模式

    ### 外觀式定義 為子系統中的一組介面提供一個一致的界面,Facade 模式定義了一個高層介面,這個介面使得這一子系統更加容易使用。 #### 界面 在這里提到的界面,主要指的是從一個組件外部來看這個組件,能夠看到什么,這就是這個組件的界面,也就是所說的外觀。 #### 介面 在這里提到的介面,主 ......

    uj5u.com 2023-06-01 09:23:32 more
  • 解釋器模式(Interpreter Pattern)

    ### 解釋器模式(Interpreter Pattern) #### 一、定義 解釋器模式(Interpreter Pattern)提供了評估語言的語法或運算式的方式,它屬于行為型模式。這種模式實作了一個運算式介面,該介面解釋一個特定的背景關系。這種模式被用在 SQL 決議、符號處理引擎等。 給定一 ......

    uj5u.com 2023-06-01 09:22:28 more