C++ 基礎 候捷老師(上)筆記

注意:這是 bilibili 上侯捷老師的 C++ 基礎 上 部分,自己做的筆記,以供複習。

侯捷老師 C++ 視頻

C++ 學習筆記

01 基礎知識點

1.1 學習目地

  • 培養正規的、大氣的編程習慣

  • 以良好的方式編寫 C++ class  Object Based (基於對象)

    • 沒有指針的類
    • 帶指針的類
  • 學習 class 之間的關係     Object Oriented (面向對象)

    • 繼承 inheritance
    • 複合 composition
    • 委託 delegation

面向對象是一種觀念
Object Based: 面對的是單一 class 設計
Object Oriented: 面對的是多重 class 的設計,存在 class 和 class 之間的關係


1.2 c++ 基礎說明

  • C
    • Data - Functions
  • C++
    • class, struct
    • Data Members - Member Function
  • class 兩大分類
    • 不帶指針
    • 帶指針
  • 一些基本書籍
    • C++ Primer
    • The C++ Programming Language
    • Effective C++ (55 個有效做法)
    • The C++ Standard Library
    • STL 源碼剖析 候捷

02 構造一個 complex 類

2.1 頭文件與類的聲明

2.1.1 C++ programs 代碼的基本形式

  • .h (header files)
  • .cpp
  • .h (Standard Library)

2.1.2 如何編寫一個頭文件

必須按照下面格式聲明  guard (防禦式聲明)

// complex.h
#ifndef __COMPLEX__
#define __COMPLEX__

...

#endif

2.1.3 Header (頭文件) 的佈局

  • forward declarations  前置聲明
  • class declarations   類-聲明
  • class definition     類-定義
// complex.h
#ifndef __COMPLEX__
#define __COMPLEX__

// forward declarations(前置聲明)
class ostream;
class complex;

complex&
__doapl(complex* ths, const complex& r);

// class declarations(類 - 聲明)
class complex
{
    ...
}

// class definition(類 - 定義)
// 有沒有 complex 就知道是 成員函數 還是 全域 函數
complex::fuction ...

#endif

2.1.4 clsee 類

  • class head
  • class body
class complex       // class head
{
public:             // class body
    ...             // 有些函數在 body 內直接定義
                    // 有些函數在 body 外定義
private:
    ...

};

2.1.5 class template(模板)簡介

模板: 類型不確定,使用的時候才確定

template<typename T>    // 模板
class complex
{
public:
    ...

private:
    T re, im;           // 模板
    ...

};

2.2 如何編寫構造函數

2.2.1 inline(內聯)函數

若類中的函數在 class 中的 boday 中定義,則函數爲 inline
inline 定義的函數具體的實現還要看編譯器能否實現 inline


2.2.2 訪問級別

  • public: 外部可以使用
  • 函數
  • private: 外部不可以使用
  • 數據
  • 函數
  • protected:

在類的定義中,public 同 private 隨處都可以,不一定非要是兩段


2.2.3 創建一個類的三種方式

{
    complex c1(2, 1);               // 棧中分配
    complex c2 = complex(2, 2);     // 棧中分配
    complex* pc3 = new complex(4);  // 堆中分配

    delete pc3;
    ...
}

2.2.4 constructor(ctor, 構造函數)

  • 函數名稱和類的名稱相同
  • 函數可以有參數
  • default argument 默認實參
  • 沒有返回值類型 和 返回
complex (double r = 0, double i = 0)
:re (r), im (i)                 // initialization list (初始列,初始值) 只構造函數纔有這種語法
{
    NULL;
}

初始化和賦值的區別:initiallzation and assignments
一個數據的數值設定有兩個階段,一個是初始化,一個是賦值階段,如上所示代碼,雖然同在大括號內的賦值結果是一樣的,但是效率不一樣,已經慢了。


2.2.5 ctor(構造函數)可以有很多個 - overloading(重載)

// 取值
double real() 
const { 
    return re;
}

// 賦值
void real(double r)
{
    re = r;
}

函數重載: 所謂的函數重載,就是同名函數,但是類型卻不相同,整體上說,即使有多個同名的函數,但是當調用函數的輸入唯一時,也可以確定唯一的使用哪個函數。
對於編譯器而言,沒有相同的兩個函數


2.2.6 ctors 放在 private 中

A::getInstance().setup();   // 使用
// Singleton 設計模式
class A
{
public:
    static A& getInstance();
    void setup(void)
    {
        std::cout << "生成類成功" << std::endl;
    }
private:
    A(void)
    {
        std::cout << "構造 A 成功" << std::endl;
    }
    // A(const A& rhs);
};
    
A& A::getInstance()
{
    static A a;
    n++;
    std::cout << "第 " << n << " 次調用 getInstance" << std::endl;
    return a;
}

{
    A::getInstance().setup();   // 使用
}

2.2.7 const member functions(常量成員函數)

函數的後面是否需要加 const
不改變類中的數據 就應該加
否則就不加
當該加沒加時,定義爲 const 的類變量 調用該函數會報錯

// 加 和 沒加 const
class complex
{
public:
    complex (double r = 0, double i = 0)
        : re (r), im (i)
    { }

    // 加沒加 const
    double real()
    const
    {
        return re;
    }
    double real()
    {
        return re;
    }

private:
    double re, im;
}

// 使用 加不加 const 都可以
{
    complex c1(2, 1);
    count << c1.real();
}

// 使用 不加 const 會出錯,函數必須加 const 纔可以
{
    complex c1(2, 1);
    count << c1.real();
}

2.2.8 參數傳遞: pass by value vs. pass by reference(to const)

// pass by value
complex(double r, double i);

// pass by reference
complex(double& r, double& i);

// pass by reference(to const)
complex(const double& r, const double& i);

儘量所有的參數傳遞傳引用
根據是否改變該參數,決定加不加 const


2.2.9 返回值傳遞: pass by value vs. pass by reference(to const)

返回值的傳遞儘量傳 reference
儘量是在可以的情況下

return by refence 語法分析

傳遞者 無需知道 接收者 是以 refence 形式接收
好處是,返回值設置爲 refence

注意

  • 返回值的問題 reference
  • 當連續使用 操作符 的時候,返回值該如何設計

2.2.10 friend(友元)

可以直接拿類的私有數據

class complex
{
public:
    complex { double r = 0, double i = 0 }
        : re (r), im (i)
    { }
    complex& operator += (const complex)
    {
        return __doapl(this, r);
    }
    double real() const 
    {
        return re;
    }
    double imag() const
    {
        return im;
    }
private:
    double re, im;

    friend complex& __doapl(complex*, const complex&);
};

inline complex& __doapl(complex* ths, const complex& r)
{
    ths->re += r.re;
    ths->im += r.im;
    return *ths;
}

{
    complex c1(2, 3);
    complex c2(3, 4);
    
    c1 += c2;
}

2.2.11 相同 class 的各個 object 互爲 friends(友元)

class complex
{
public:
    complex (double r = 0, double i = 0)
        : re (r), im (i)
    { }

    int func(const complex& param)
    {
        return param.re + param.im;
    }

private:
    double re, im;
};

{
    complex cl(2, 1);
    complex c2;

    c2.func(cl);
}

2.2.11 class body 外的各種定義 (definitions)

編寫一個類

  • 數據一定放在 private
  • 參數以 reference 來傳 要不要加 const,看狀況
  • 返回值也儘量以 reference 來傳
  • 在類的本體的函數,該加 const 就要加,不加有可能報錯
  • 構造函數特殊賦初值語法

2.3 操作符重載

2.3.1 operator overloading(操作符重載 - 1,成員函數)this

每個成員函數都有 this 不需要定義,可以直接用

// do assignment plus
inline complex&
__doapl(complex* ths, const complex& r)
{
    ths->re + r.re;
    ths->im + r.im;

    return *ths;
}

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

2.3.2 operator overloading(操作符重載 - 2,非成員函數)(無 this)

什麼時候需要

其實其相應的功能也可以通過成員函數實現,但是隻能二存一
有的時候可能有問題,比如下面的 7 + c1,將函數編寫爲成員函數,7 將無法調用,會有問題

{
    complex c1(2, 1);
    complex c2;

    c2 = c1 + c2;
    c2 = c1 + 5;
    c2 = 7 + c1;
}

符號重載注意

  • 要根據情況將符號重載爲 成員函數 或者 類全局函數
  • 如果操作符 第一個 參數的對象不是該類型的對象,則該函數一定要重載爲 類全局函數
  • 具體的需要後面深入學習

2.3.3 temp object(臨時對象)typename();

下面這些函數返回值不可以是 return by reference

下面這三個函數 return 後面的內容時一種特殊的語法,創建了一個臨時對象,但是沒有名字,因爲是直接返回的。
注意臨時對象只能返回 vaule 不能返回 reference

// 空格問題
inline complex
operator + (const complex& x, const complex& y)
{
    return complex (real(x) + real(y),
                    imag(x) + imag(y));
}

inline complex
operator + (const complex& x, double y)
{
    return complex (real(x) + y, imag(x));
}

inline complex
operator + (double x, const complex& y)
{
    return complex (x + real(y), imag(y));
}

2.4 構造 complex 小結

  • 頭文件的格式
  • 防禦式聲明
  • 構造函數
  • 初始化值
  • 加不加 const
  • 參數的傳遞儘量考慮 reference
  • return 的時候可以的話 reference
  • 數據放在 private
  • 函數主要放在 public
  • 局部函數
  • 全局函數
  • 各種細節語法

03 構造一個 string 類

class with pointer member 必須要有 copy ctor 和 copy cp=

三大函數

  • 析構函數
  • 拷貝構造函數
  • 拷貝賦值函數

3.1 析構函數

// 析構函數                                                   
inline String::~String()
{
    delete[] m_data;
    std::cout << "m_data 已經釋放" << std::endl;
}

3.2 copy ctor 拷貝構造

// 拷貝構造函數                                     
inline String::String(const String& str)            
{                                                   
    m_data = new char[strlen(str.m_data) + 1];      
    strcpy(m_data, str.m_data);                     
}                                                   

3.3 copy op= 拷貝賦值

// 拷貝賦值                                            
inline String& String::operator = (const String& str)  
{                                                      
    if (this == &str)                                  
    {                                                  
        return *this;                                  
    }                                                  
                                                       
    delete[] m_data;                                   
    m_data = new char[strlen(str.m_data) + 1];         
    strcpy(m_data, str.m_data);                        
    return *this;                                      
}

04 內存管理

4.1 new

new: 先分配 memory,再調用 ctor

Complex* pc = new Complex(1, 2);

// 編譯器轉化
1 void* mem = operator new(sizeof(Complex)); // 分配內存
  // 其內部調用 malloc(n)
2 pc = static_cast<Complex*>(mem);           // 轉型
3 pc->Complex::Complex(1, 2);                // 構造函數
  Complex::Complex(pc, 1, 2);
                   this

4.2 delete

delete: 先調用 dtor,再釋放 memory

String* ps = new String("Hello");
...
delete ps;

// 編譯器轉化
String::~String(ps);        // 析構函數
operator delete(ps);        // 釋放內存
// 其內部調用 free(ps)

4.3 動態分配所得的內存塊(memeory block),in VC

  • 調試模式下會有 36 個字節用於調試,數據區上面有 32 個字節,下面有 4 個字節
  • 整個內存區域開始和結束各有 4 個字節的標識
  • 整體內存大小爲 16 的倍數
  • 非調試模式下,沒有那 36 個字節
  • 最開始和最後的數字 是整個內存的大小
  • 64 對應 41h 1 表示內存分配出去了
  • 數組相對於單個對象只是多了對象的內存 和 四個字節的存放數組的大小



4.4 array new 一定要搭配 array delete

String* p = new String[3];
...
delete[] p; // 喚起 3 次 dtor

String* p = new String[3];
...
delete p; // 喚起 1 次 dtor
內存泄露圖解

對於沒有 指針 的 new[ ] 使用 delete 也不會造成泄露
但是養成匹配使用的好習慣


05 補充

5.1 static

  • static 必須在類外面定義
  • static 函數沒有 this,只能處理靜態數據
  • 調用靜態函數的方式,通過 object 和 通過 class name 兩種方式調用
class Account                                             
{                                                         
public:                                                   
    static void set_rate(const double& x)                 
    { m_rate = x;}                                        
    void print(void)                                      
    {                                                     
        std::cout << "m_rate = " << m_rate << std::endl;  
    }                                                     
private:                                                  
    static double m_rate;                                 
};                                                        

// 沒有下面這行會報錯,這行是定義
double Account::m_rate;
// double Account::m_rate = 3.0;

5.2 class template 類模板

class complex
{
public:
    complex(T r = 0, T i = 0)
        : re(r), im(i)
    { }
    T real() const {
        return re;
    }
    T imag() const {
        return im;
    }
private:
    T re, im;
};

{
    complex<double> c1(2.5, 1.5);
    complex<int> c2(3, 4);
    // test 類模板的使用
}

5.3 函數模板

class complex
{
public:
    complex(double r = 0, double i = 0)
        : re(r), im(i)
    { }
    double real() const {
        return re;
    }
    double imag() const {
        return im;
    }

private:
    double re, im;
};

bool operator < (const complex& a, const complex& b)
{
   return a.real() < b.real();
}

template <class T>
inline
const T& min(const T& a, const T& b)
{
    return b < a ? b : a;
}

{
    complex c1(2.5, 1.5);
    complex c2(3.2, 4.2);

    complex a = min(c1, c2);
}
// test 函數模板的使用

5.4 namespace

// 使用格式
namespace std
{
    ...
}

// 三種使用方式

// 1
using namespace std;

cin << ..;
cout << ..;

// 2
using std::cin;

std::cout ...;
cin ..;

// 3
std::cout ...;
std::cin ...;

5.5 更多深入細節

  • operator type() const;
  • explicit complex(…) : initialization list { }
  • opinter-like object
  • function-like object
  • Namespace
  • template specializaton
  • Standard Library
  • variadic template (since C++11)
  • move ctor (since C++11)
  • Rvalue reterence (since C++11)
  • auto (since C++11)
  • lambda (since C++11)
  • rang-base for loop (since C++11)
  • unordered containers (since C++11)

06 Object Oriented Programming, Object Oriented Design OOP, OOD

  • Inheritance(繼承)
  • Compositon(複合)
  • Delegation(委託)

6.1 Compositon(複合),表示 is-a

  • Adapte 設計模式,適配模式,可以通過複合實現
  • 包含一個對象 真正意義的包含
  • 構造由內而外
  • 析構由外而內
  • 生命一致

6.2 Delegation(委託)Composition by reference

  • Handle / Body (pImpl)
  • 包含一個對象的指針 形式上的包含
  • 生命不一致

6.3 Inheritance(繼承)表示 is-a

  • 構造由內而外
  • 析構由外而內
  • 如果類可能被繼承,將析構函數設置爲 virtual

6.4 Inheritance with virtual functions

  • non-virtual 函數:你不希望 derived class 重新定義(override 覆寫)它。
  • virtual 函數:你希望 derived class 重新定義(override, 覆寫)它,它已有默認定義。
  • pure virtual 函數:你希望 derived class 一定要重新定義(override,覆寫)它,你對它沒有定義。
  • Inheritance with virtual 實現 Template Method 模式
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章