# 侯捷cpp-oop(上) 笔记

# 头文件与类声明

Classes 两个分类

* Class without pointer member(s): complex
    
* Class with pointer member(s): string
    

也可以分为

* Object-Based vs Object Oriented
    

# 构造函数

## inline函数

函数如果在 `class` 内定义完成，则自动成为 `inline` 候选人.

也可以在`class` 外部定义，方法需要增加`inline` 关键字。

## constructor(ctor, 构造函数)

```cpp
class complex {
public:
    // default argument r, i
    explicit complex(double r = 0, double i = 0) : re(r), im(i) { // initialization list(初值列)

    }
private:
    double re, im;
};
```

* 使用`初值列`, 可以在初始化类成员变量时赋予值，而不是在构造函数内**多余**使用`=`进行`赋值(assignment)`操作。
    
* 不带指针的类，多半不需要写`析构函数`
    

# 参数传递与返回值

## ctors 放在 private 区

单例(Singleton)模式

```cpp
// https://en.wikipedia.org/wiki/Singleton_pattern

#include <iostream>

class Singleton {
public:
  static Singleton& get() {
    static Singleton instance;
    return instance;
  }
  int getValue() {
    return value;
  }
  void setValue(int value_) {
    value = value_;
  }
private:
  Singleton() = default;
  ~Singleton() = default;
  int value;
};

int main() {
  Singleton::get().setValue(42);
  std::cout << "value=" << Singleton::get().getValue() << '\n';
}
```

## const member functions

当不修改`成员变量(class memeber)`时, 使用 `const` 修饰成员函数。

```cpp
class complex {
public:
    double real() const {return re;}
    double imag() const {return im;}
private:
    double re, im;
};
```

如果去掉 `imag()` 的 `const` 关键字，调用时就会报错。

```plaintext
    const complex comp;
    comp.imag();
    // 'this' argument to member function 'imag' has type 'const complex', but function is not marked const
```

所以，const的类对象，只可以调用 const member function.

## friend(友元)

```cpp
class complex {
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;
}
```

`friend` 进行函数声明，表明什么样的函数可以访问 `private` 成员变量。

* 相同 `class` 的各个 `object` 互为 `friend(友元)`。
    

```cpp
class complex {
public:
    int func(const complex& param) {
        return param.im + param.re; // it's ok
    }
private:
    double re, im;
};

complex c1(2, 1);
complex c2;

c2.func(c1); // it's ok
```

## class body 外的各种定义(defintions)

什么情况下 **return by reference** ?

当返回的变量不需要在函数内部申请新的内存空间时，返回`reference`.

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

返回的需要声明新的内存空间时，返回`value`， 进行值拷贝操作。

```cpp
inline complex operator+ (const complex& a, const complex& b) {
    return complex(a.re+b.re, a.im+b.im);
}
```

这里的 `operator+()` 函数即申请了新的内存空间，用于返回，因此时返回`value`， 而非`reference`. 否则会导致函数退出后，在栈上申请的内存空间被释放。

# 操作符重载和临时对象

## 操作符重载-1 成员函数(this)

所有`member function` 都有一个隐藏的参数`this`.

```cpp
complex& complex::operator += (const complex& r) {
    return __doapl(this, r); // do assign plus
}
```

## reference 语法分析

**传递者** 无需知道 **接收者** 是以 `reference` 的形式接收。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897135403/d7d06e24-dd22-415f-bad2-ab4b8a42f8ff.png align="center")

在重载运算符时，运算符的返回值，只需要返回值，而不需要在意后面是`值传递` 还是 `引用传递`

## 操作符重载-1 非成员函数(no this)

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

## class body 外的 definition

```cpp
complex& complex::operator+() {
    std::cout << "just positive" << std::endl;
    return *this;
}
```

这个成员函数代表正号(一元操作符)，而不是加号(二元操作符)，编译器通过参数来确定。

# Big Three: copy ctor(拷贝构造), copy assignment op(拷贝复制), dtor(析构)

只要 class 内有指针，就要自己实现 `Big Three`.

```cpp
class String {
    String(const char* cstr = 0);
    // copy ctor
    String(const String& str);
    // copy assignment operator
    String& operator=(const String& str);
    // destructor
    ~String();
};
```

[https://www.cs.odu.edu/~zeil/cs361/latest/Public/big3/index.html#moving-data-l-values-and-r-values](https://www.cs.odu.edu/~zeil/cs361/latest/Public/big3/index.html#moving-data-l-values-and-r-values)

> class with pointer members 必须有 copy ctor 和 copy op=

## copy ctor

```plaintext
    MyString s1;
    MyString s2(s1); // copy ctor here
    MyString s3 = s1; // copy ctor here
    s3 = s2; // copy op= here
```

使用一个对象初始化构造另一个对象，无论形式是 `obj2(obj1)` 还是 `OBJ obj2 = obj1` 都是调用 `copy ctor`

## copy assignment operator

```cpp
MyString& MyString::operator=(const MyString &str) {
    std::cout << "copy op= here" << std::endl;
    // IMPORTANT: avoid self assignment
    if (&str == this) {
        return *this;
    }
    // delete origin char array
    delete[] this->m_data;
    // allocate a new char array
    this->m_data = new char[strlen(str.m_data) + 1];
    // copy data
    strcpy(this->m_data, str.m_data);
    return *this;
}
```

一定要在 `copy assignment operator` 内检查 `self-assignment`.

# Stack, Heap and Memory management

* `stack object`, 其生命在作用域(scope)结束之际结束，又叫`auto object`, 因为它会被**自动**清理。
    
* `static object`, 其生命在作用域(scope)结束之后仍然存在，直到整个程序结束。
    

```cpp
{
    static Complex c2(1, 2);
}
```

* `global object`, 其生命在作用域(scope)结束之后仍然存在，直到整个程序结束。也可以视为一种 `static object`, 其作用域是 **整个程序**。
    
* `heap object`
    

## new: 先分配 memory, 再调用 ctor

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897156463/1a328353-6fd0-4d72-b114-f10f6c75a644.png align="center")

## delete: 先调用 dtor,再释放 memory

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897162985/a04f2a5b-5da1-42a7-b762-e04f5ec2eb8a.png align="center")

## 动态分配获得的内存块(memory block)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897173880/e879db7f-4425-427f-99bb-e22192af1725.png align="center")

对于 拥有两个 `double(4Bytes)` 的 `Complex`, 真正分配的绿色部分(8Bytes), release模式(右边) 和 debug模式(左边), 都会在内存块的起始和终止位置增加红色部分的`cookie`。

`cookie`的值包含内存块的大小信息, 供 `malloc()` 和 `free()` 使用。 最后一位`0`或`1`, 表示该块内存对于`os`来说是分配出去(`1`)，还是已经被回收(`0`).

## 动态分配得到的 array

`new tt[]` 读作 **array new**. `delete[] tt` 读作 **array delete**.

> `array new` 一定要搭配 `array delete`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897217688/a7bdb402-3c02-4d93-a082-31f63179eb4e.png align="center")

数组的内存空间会被释放，**不会**发生内存泄露。

但是数组内元素只会调用一次`dtor()`, 对象中动态分配的内存就不会被释放掉。

如果是`Complex`这种`class with pointer members`, 使用`delete` 来释放`Complex[]`也不会有问题。

# 拓展补充: 类模版，模版函数及其他

## static

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897228436/56a780e5-daad-45ee-bbda-b6d567ca62f6.png align="center")

`data members` 加上 `static` 修饰后，类的所有实例化对象共享**一份**。

`member functions` 加上 `static` 修饰后，仍然在内存中只有一份。但是，`static member function` 调用时没有 `this pointer`， 只能访问 `static data members`。

```cpp
class Account {
public:
    static double m_rate;
    static void set_rate(const double& x);
};

// Non-const static data member must be initialized out of line
double Account::m_rate = 8.0; // definition

int main() {
    // call static member function by class name;
    Account::set_rate(5.0);

    Account a;
    // call static member function by object;
    a.set_rate(10);
    return 0;
}
```

## class template, 类模版

```cpp
template <typename T>
class TComplex {
private:
    T re, im;
};
```

## function template, 函数模版

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

`class template` 使用时必须明确指定绑定的类型，

```cpp
complex<int> a;
```

`function template` 不必明确指出，编译器会自动进行`argument deduction`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897241838/f14e9e3a-1642-4d83-96a6-892f47a007d1.png align="center")

## namespace

有以下三种方法使用

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897253061/e9716db9-29bf-4e16-964e-2f4277ae0ba9.png align="center")

## 更多细节

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897263987/79137c0f-ba8e-4ea2-899e-0b7b0fe39485.png align="center")

# Object Oriented Design (OOP -&gt; OOD)

* Inheritance(继承)
    
* Composition(复合)
    
* Delegation(委托)
    

## Composition(复合), 表示 has-a

一个类中包含另一个类。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897274770/1269a9ce-d687-4cdb-92a3-50db03d11390.png align="center")

从内存的角度来看，对象间是**直接包含**的关系(而非包含引用或指针)。 两个对象的**生命周期**是一致的。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897284370/8507c2c8-933e-4e25-a888-a14c62b3490d.png align="center")

## Composition 下的构造和析构

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897297416/2c0a1ccd-34ec-4e81-8228-0e840ed65793.png align="center")

## Delegation(委托): Composition by reference.

> Composition by reference

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897308876/228fa54f-722d-4a5e-a385-9bc3f73b060d.png align="center")

和`Composition`相比，`Delegation`的**生命周期**，内外部对象**不一致**。

这种设计模式，也叫`pointer to implication(pimpl)`. 也叫**编译防火墙**，Handle部分不需要再编译，只需要编译Body部分。

## Inheritance(继承), 表示 is-a

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897316576/383d5804-94f2-4105-8471-6e3c5cb406b7.png align="center")

## Inheritance 下的构造和析构

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897326837/d4c7d36a-d78b-4bdc-811b-98215fbae5d2.png align="center")

# 虚函数和多态

## Inheritance(继承) with virtual function(虚函数)

**成员变量**的继承可以从**内存**角度理解。

**成员函数**的继承不可以从内存角度理解，继承的是**调用权**。派生类可以调用基类的函数，就是继承了基类的成员函数。

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897338249/7fb32a27-e83e-42a9-8031-f1e57497bffa.png align="center")

* 虚函数子类可以不重写，纯虚函数子类必须重写
    
* 带纯虚函数的类叫抽象类，这种类不能直接生成对象，而只有被继承，并重写其虚函数后，才能使用。
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897346460/8af8bacd-a202-45b1-847e-35371483cf81.png align="center")

父类`CDocument`中的`Serialize()`是虚函数，在实际调用时，调用的是子类`CMyDoc`实现的`Serialize()`函数。

```cpp
class Base {
public:
    virtual void func() {
        cout << this << endl;
        cout << "in Base::func()" << endl;
    }

    void hi() {
        cout << this << endl;
        cout << "in Base::hi()" << endl;
        func();
    }
};

class Derived : public Base {
public:
    void func() override {
        cout << this << endl;
        cout << "in Derived::func()" << endl;
    }
};

int main() {
    Derived d;
    d.hi(); // d -> Base::hi() -> Derived::func()
    return 0;
}
```

输出

```plaintext
0x16fccf518
in Base::hi()
0x16fccf518
in Derived::func()
```

这个也印证了，调用成员函数的时候，会隐式传入`this`指针，调用时查虚函数表，也是`this`的`vptr_table`.

这种把父类的成员函数 **“延缓”** 子类实现的做法，叫做设计模式中的`Template Method`.

## Inheritance + Composition 关系下的构造和析构

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897360177/90d34ba5-b14e-4165-ad19-c41d67f0ca53.png align="center")

```cpp

class Component {
public:
    Component() {
        cout << "Component()" << endl;
    }
    ~Component() {
        cout << "~Component()" << endl;
    }
};

class Base1 {
public:
    Base1() {
        cout << "Base1()" << endl;
    }
    ~Base1() {
        cout << "~Base1()" << endl;
    }
};

class Derived1 : public Base1 {
public:
    Derived1() {
        cout << "Derived1()" << endl;
    }
    ~Derived1() {
        cout << "~Derived1()" << endl;
    }
private:
    Component c;
};

int main() {
    Derived1 d1;
    return 0;
}
```

```plaintext
Base1()
Component()
Derived1()
~Derived1()
~Component()
~Base1()
```

第一种方式, 先构建`Base`part, 再构建`Component`part. 析构时顺序相反。

第二种方式, 先构建`Base` part 中的 `Componment` part, 再构建`Base`Part, 这一点从内存布局上就能看出来。析构的时顺序相反。

## Delegation(委托) + Inheritance(继承)

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897369105/60745341-c4a6-4a6a-b776-fa58c90f8df5.png align="center")

## 委托相关设计

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1691897374323/12931f47-e2f2-4ce4-969f-cd68a432a426.png align="center")
