C++筆記

C++筆記

最基本的語法和撰寫注意事項,像是Operator Overloading這類比較進階而且集大成的部分可以直接看書的Ch. 11,關於Template、Exception Handling、File Processing、struct、STL等可以看課本

語法

基本

  • Basic Class
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
      # include <iostream>
      using namespace std;
    
      class GradeBook
      {
          public:
              void displayMessage()
              {
                  cout << "Welcome to the Grade Book!" << endl;
              }
      };
    
      int main()
      {
          GradeBook myGradeBook;
          myGradeBook.displayMessage();
      }
    
  • if(Conditional Operator):
    1
      cout << (grade >= 60 ? "Passed" : "Failed")
    
  • For Loop
    1
    2
      for (int counter = 1; counter <= 10; counter++>)
          count << counter << endl;
    
  • new例子
    1
    2
    3
    4
    5
    6
    7
      dataType variableName = new (class object/array/dataType(value))
      Time *timePtr = new Time;
      delete timePtr;
      double *ptr = new double(3.14);
      int *gradeArray = new int[10];
      int gradeArray[] = new int[10];
      delete [] gradeArray;
    

Reference

和Pointer不一樣,是完全兩個不同的東西,所以&不是取位址,以下例子代表y永遠參考(綁定)x,所以改y就會改道x,因為實際的記憶體中只有一份data,y也不是一個獨立的空間。使用refernce之前要初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int &y = x;

// 例子
int squareByValue(int num){
    return num *= num;
}

void squareByRefernce(int &numRef){
    numRef *= numRef;
}

int main(){
    int x = 2, z = 4;

    cout << x << endl; // 2
    cout << squareByValue(x) << endl; // 4
    cout << x << endl; // 2

    cout << z << endl; // 4
    cout << squareByRefernce(z) << endl; // 16
    cout << z << endl; // 16->已經改變了
}

Array

type arrayName[ arraySize ];,代表array中所有的element都是同樣的data type,如果想要什麼type都能放,要用vector搭配Variant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# include <Variant>
# include <vector>
using Var = std::variant<int, double, std::string>;

std::vector<Var> v;
v.push_back(10);
v.push_back(3.14);
v.push_back("hello");

// 如果要存取
for(auto& x:v){
    std::visit([](auto&& value){
        std::cout<< value << "\n";
    }, x);
}

另外,array也可以當作argument傳入function

1
2
3
4
void modifyArray(int b[], int sizeOfArray){
    for (int k = 0; k < sizeOfArray; k++)
        b[k] *= 2;
}

多維的表示方式: int a[2][3];

vector

是一種class template,所以要使用template notation的寫法。一般來說使用vector的頻率會比array多很多,因為大部分時候不確定大小,如果大小固定並且追求極致效能,那再用array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# include <vector>
vector<int> v1(7);
vector<int> v2(10);

// 可以動態改變大小
v1.push_back(10);
v2.push_back(20);

// 比Array安全
v1.size()
v2.at() // 檢查border,自動釋放直覺copy

// STL直接支援 vector
std::sort(v1.begin(), v1.end());
std::find(v2.begin(), v2.end(), 3);

Pointer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dataType *variableName; // int *yPtr;
variableName = &addressVariable; // yPtr = &y;

// Pass by Pointer Argument
void cubeByReference(int *);
int main(){
    int number = 5;
    cubeByReference(&number);
}

void cubeByReference(int *nPtr){
    *nPtr = *nPtr * *nPtr * *nPtr;
}

// Array of Pointer
const char * const suit[4] = {"Hearts", "Diamonds", "Clubs", "Spades"}
// 有一個suit這個array,並有4個element,*const代表每個element都是const pointer

Pointer 搭配 Const的應用

  Pointer本身 被指向的對象
int *Ptr;
const int *Ptr;
int * const Ptr = &x;
const int *const Ptr = &x;

Pointer和Array的關係

1
2
3
4
5
6
7
// Basic Declaration
int b[5];
int *bPtr;

(bPtr = b) == (bPtr = &b[0]); // 直接把Array b的地址給Ptr
b[3] == *(bPtr + 3) == *(b + 3) // 描述地址 + 3再取值 = b[3]的值
&b[3] == bPtr + 3 // 取第三個element的地址

Function

ArgumentName前面可以加static/const/static const

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Function that take no argument
dataType functionName();

// Inline function
inline dataType functionName( dataType argumentName )

// Pass by reference
dataType functionName( dataType &objectName ){
    objectName.classMemberFumction(); // 假設argumentName是一個object,想要使用/存取其中的member要用句點,因為是reference
}

// Function with default argument
dataType functionName( dataType argumentName = value )

// Array argument
dataType functionName( dataType argumentName[] )

// Vector as argument
dataType functionName( vector<dataType> argumentName & )

// Function Template
template<class T>
T className( T argumentName, T argumentName2, ){
    T variableName = value;
}

// Pass by reference with pointer
dataType functionName( dataType *objectName ){
    objectName->classMemberFumction(); // 假設argumentName是一個object,想要使用/存取其中的member要用箭頭,因為是pointer
}

dataType functionName( const dataType *argumentName ) // 代表*argumentName指的對象不能改,但argumentName本身可以改指其他memory

dataType functionName( dataType * const argumentName ) // 代表argumentName本身不能改,但是指到的對象可以

// Function pointer
dataType functionName( FunctionDataType (*) (ArgumentDataType) ) // 看Argument有多少就要有幾個

Function Template

是Function Overloading的推演,既然所有logic都一樣,只有data type不同,那就設計一個邏輯通用的template,讓compiler自行推導該用什麼data type(實際使用的時候)

1
2
3
4
5
6
7
8
9
template < class T >
T maximum( T value1, T value2, T value3){
    T maximumValue = value1;
    if (value2 > maximumValue) maximumValue = value2;

    if (value3 > maximumValue) maximumValue = value3;

    return maximumValue;
}

T可以帶入int, double, float, char之類的

const Member Function

1
2
3
4
5
// 意思是保護return value不能改
const dataType memberFunctionName();

// 意思是保證member function內部不會修改object的任何data member,常用在get/print function
dataType memberFunctionName() const;

Class

static/const/static const可以用在object,都有不同的效果,但不能用在class之前;另外,member function 和data member也都可以加static/const/static const

1
2
3
4
5
6
7
8
9
10
11
12
// 基本範例
class className{
public:
    // Constructor w/ default argument
    className( dataType = value );

    // deconstructor
    ~className ();

    // Const member function(保護return value不能改,在cpp的實作也要加const)
    dataType functionName() const;
}

friend Keyword 範例

1
2
3
4
5
6
7
8
9
10
// className.h
class className{
    friend dataType outsideFunctionName( className &); // Class A授權外部function可以使用class A中的成員
};

// className.cpp
#include "className.h"
using namespace std;

dataType outsideFunctionName ( className &objectName ){ cout << objectName.a;}

Function Lifetime

  • Global Function
  • Static Function in main()
  • Static Function in Local
  • Function in main()
  • Function in Local

初始化const data member

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Increment.h
#ifndef INCREMENT_H
#define INCREMENT_H
#include "Date.h"

class Increment{
    public:
        void Increment(int c, int i, const Date &, const Date &, const string &);
    private:
        int count;
        const int increment;
        const Date birthDate; // 創立兩個Date的object並且是const,他是其他class(Date)的object constructor但同時也是Increment.h的member
        string firstName; // string的邏輯也一樣,firstName也是string的object
};

#endif

// Increment.cpp
#include <iostream>
#include "Increment.h"
using namespace std;

// 這是唯一的寫法,用list的方式分別傳入`c`, `i`, `dateOfBirst`, `first`的值給
// `count`, `increment`, `birthDate`, `firstName`, ,尤其是`increment`和`birthDate`是const,不能用直接傳入的方法
Increment::Increment(int c, int i, const Date &dateOfBirst, const string &first):
    count(c), increment(i), birthDate(dateOfBirst), firstName(first)
    {}

Enable Cascaded Function Calls

意思就是可以讓function進行串接,達到function的return直接傳遞給下一個function當作argument input,例如: t. setHour(18).setMinute(30).setSecond(22);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// className.h
class className{
    public:
        className& memberFuntionName( dataType ); // 代表可以做到Cascaded member function / method chaining

    private:
        int a;
        const int b;
};

// className.cpp
#include "className.h"
using namespace std;

// 回傳物件本身,實現cascaded function / method chaining
className &className::memberFuntionName( dataType  variableName)
{return *this;}

OOP(Object-Oriented Programming)

只要先記住,繼承就是class可以不用從頭開始撰寫,也可以直接沿用別人的class再往下開發;多型則是分成overriding(覆寫)/overloading(多載)兩種;封裝則是設定class中各個成員的存取權限,例如Public/Private/Protected等

Inheritance(繼承)

Base-class member access specifier\Type of inheritance public inheritance protected inheritance private inheritance
public public protected private
protected protected protected private
private Hidden Hidden Hidden
1
2
3
4
// Inheritance.h
class derivedClass:public baseClass{
    ...
};

Polymorphism(多型)

  • Overriding 是「不同 class、同函式、執行期決定」
  • Overloading 是「同一 scope、同名不同參數、編譯期決定」

Overriding

Derived class 重新定義 base class 的 virtual function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 初始定義,derivedClass是繼承basedClass的更新版本
basedClass basedObject();
basedClass *basedObjectPtr = 0;

derivedClass derivedObject();
derivedClass *derivedObjectPtr = 0;

// 很直觀: basedClass的print function印出basedClass object的data
basedObjectPtr = &basedObject;
basedObjectPtr->print();

// 很直觀: derivedClass的print function印出derivedClass object的data
derivedObjectPtr = &derivedObject;
derivedObjectPtr->print();

// 非常不直觀
basedObjectPtr = &derivedObject;
basedObjectPtr->print();

詳解第三個例子: 在書中的例子中,print() member function是baseObject的print(),但顯示的卻是derivedClass的object data,會使用derivedClass的object data很直觀,因為目前的ptr指向derivedClass object,而data又是屬於object所以當然會印出derivedClass object的data,而使用basedClass的print member function是因為basedObjectPtr初始化的時候就是被assigned basedObject,所以compiler會直接在編譯期間就binding,除非使用virtual才會強制讓compiler在執行期間才決定使用哪一個object的print

  • Virtual 為了避免compiler事先static binding,就要使用virtual這個keyword,讓compiler在執行的時候再決定要用哪一個method(dynamic binding/late binding)
    1
      virtual dataType memberFunctionName() const;
    

Pure virtual function

如果basedClass中,有一個function是很多derivedClass都必須要客製化,不能事先定義的話,那就要特別使用這個pure virtual function,也就是先定義interface,實作的部分交給各個derivedClass

1
2
3
4
5
6
7
8
9
10
11
12
13
// Employee.h
class Employee{
    public:
        virtual double earnings() const = 0; // 重點是等於零
};

// SalariedEmployee.h
#include "Employee.h"

class SalariedEmployee : public Employee{
    public:
        virtual double earnings() const; // 實際定義出屬於SalariedEmployee的earnings function
}

例如課本中提到的Employee Class(Based Class)中,有earnings function,被很多derivedClass繼承之後,會衍生出其他的earning function,但計算的方式都不一樣

Class Name 計算方式
Employee = 0
Salaried-Employee weeklySalary
Hourly-Employee (hours <= 40 ? (wage * hours) : ((40 * wage) + ((hours - 40) * wage * 1.5)))
Commission-Employee commissionRate * grossSales
BasePlus-Commission-Employee baseSalary + (commissionRate * grossSales)

Encapsulation(封裝)

修飾詞 Class內 子類 Class外
public
protected
private
  • 如果要讓derived class能使用data member但又不想要class以外的scope能夠存取,那麼就要用protected

開發注意事項

Header 可以寫什麼

  • class/struct的宣告
    1
    2
    3
    4
    5
    6
    7
    8
    9
      // foo.h
      class Foo {
      public:
          Foo();          // 建構子宣告
          void bar();     // member function 宣告
    
      private:
          int x;
      };
    
  • 常數、型別定義
    1
    2
    3
    4
    5
      // config.h
      constexpr int MAX_SIZE = 100;
    
      using ID = unsigned int;
      typedef unsigned long ulong;
    
  • Template: 實作也要寫在header file中,和一般的class不一樣
    1
    2
    3
    4
    5
      // max.h
      template<typename T>
      T max(T a, T b) {
          return a > b ? a : b;
      }
    

Header 和 CPP 要分開

  • header 負責「說有什麼」,source 負責「怎麼做」,也就是前者只負責宣告 Member Variable 和Member Functions,但把實作寫在後者,
  • 因為假設把兩者都寫在同一個cpp,那麼其他cpp需要用到某個function就無法使用,也不可以把實作直接寫在header file中

否則:

  1. 增加編譯時間 一般來說使用者會拿到.h + (.dll or a),前者類似給使用者的說明書,讓他知道可以用什麼以及怎麼用,然後include之後實際使用的program則是會放在.dll.a中,編譯的時候會直接link過去,好處是可以把實作的細節褒裝起來不讓其他人輕易看到,以及要修改header file的實作細節時,所有include的cpp不需要再重新編譯
  2. 破壞封裝(Encapsulation) 封裝的核心是:用的人只知道「能做什麼」,不知道「怎麼做到」,如果 header 暴露實作:
    • 私有資料結構被看見
    • 未來很難改內部實作
    • 使用者可能「依賴細節」
  3. 降低可維護性與可讀性: 實作混進來後: Header 又長又亂、找介面變困難、新人難上手

++a VS a++

前者是先+1再往後使用,後者則是相反

1
2
3
4
5
6
7
c = 5;
cout << c++ << endl; // 5
cout << c << endl; // 6

c = 5;
cout << ++c << endl; // 6
cout << c << endl; // 6

Function Overloading

可以定義名稱相同的function,只要return/argument的 data type不同,signature就會不一樣,那compiler就會視為不同的東西

Recusive VS. Iterative Function

前者是重複動作並呼叫自己的function,後者則是利用for-loop, while-loop, do-while-loop之類的counter-controlled方式進行重複動作,兩者的terminate方式也不同