#	对象和类

## 目录
- [访问控制](#访问控制)
- [作用域运算符](#作用域运算符)
- [类的六大特殊成员函数](#类的六大特殊成员函数)
  - [RAII](#RAII)
  - [move语义](#move)
    - [右值引用](#右值引用)
  - [初始化列表](#初始化列表)
- [const 成员函数](#const成员函数)
- [this指针](#this指针)
- [作用域为类的常量](#作用域为类的常量)
- [作用域内枚举](#作用域内枚举)
- [友元](#友元)
- [类的自动转换和强制类型转换](#类的自动转换和强制类型转换)
  - [转换函数](#转换函数)
- [继承](#继承)
  - [继承方式](#继承方式)
  - [虚函数](#virtual)
- [设计理念](#)
  - [is a](#)
  - [AbstactBaseClass](#ABC)	

##	访问控制	

```cpp	
class demo {
public	:		// 公有接口
private:	// 私有成员
protected:	// 保护,对外部是私有
};
```

##	作用域运算符(::)

可用于在类体外指出函数所属的类(命名空间)

**成员函数的参数名不可与类成员相同**

##	类的六大特殊成员函数(未定义时编译器提供默认版本)

```cpp
demo::demo();							// 默认构造函数
demo::~demo();							// 默认析构函数
demo::demo(const demo&);				// 复制构造函数
demo& demo::operator = (const demo&);	// 赋值构造函数
demo::demo(demo&&);						// 移动复制构造函数
demo& demo::operator = (demo&&);		// 移动赋值构造函数
```

`demo::demo() = default;`显式声明为默认
`demo::demo() = delete;`显式声明为禁用


### RAII

在类的构造函数中申请堆内存,在析构函数中释放,这样可以保证类失效时内存被释放

### move

移动语义的核心思想是转移而非复制。当一个对象被移动时，其资源（如动态分配的内存、文件句柄等）被转移到目标对象，而源对象被置于一种有效但未定义的状态。这种状态通常是清空的，以确保源对象的资源在其生命周期内不会被重复释放

```cpp
MyString(MyString&& other) noexcept : data(other.data) { // 移动构造函数
    other.data = nullptr;
}
MyString& operator=(MyString&& other) noexcept { // 移动赋值运算符
    if (this != &other) {
        delete[] data; // 释放当前对象的资源
        data = other.data;
        other.data = nullptr;
    }
    return *this;
}
```

`std::move` 是一个函数模板，用于将左值强制转换为右值。它本身并不执行移动操作，而是通过返回一个右值引用，触发移动构造函数或移动赋值运算符

`MyString str2 = std::move(str1); // 触发移动构造函数`

#### 右值引用
分为两类
> 普通右值引用：直接声明的T&&，用于移动语义
> 转发引用：模板函数中声明的T&&，用于完美转发

- [函数引用重载](./func#重载)

### 初始化列表

初始化列表是一种在构造函数中初始化类成员变量的机制

1.  初始化常量成员变量：常量成员变量必须在构造函数的初始化列表中初始化，不能在构造函数体内赋值。
2.  初始化引用成员变量：引用成员变量必须在构造函数的初始化列表中初始化，不能在构造函数体内赋值。
3.  调用基类的构造函数：如果类继承自基类，需要在初始化列表中显式调用基类的构造函数。
4.  优化性能：对于一些复杂的成员变量（如类对象），使用初始化列表可以避免默认构造后再赋值，从而提高效率。

> example
`MyClass(int a, int b, int c) : Base(a), myConst(b), myRef(c), myValue(10) {}`

##	const成员函数	适合的成员函数要尽可能用，以帮助规避错误

`void show() const;`			声明
`void demo::show() const;`		定义
表明函数不会修改调用对象

##	this指针
	成员函数引用整个调用对象，可以使用 *this

##	作用域为类的常量(无法用const)
	因为声明类只是描述了对象的形式，没有创建对象
+	在类中声明一个枚举
```cpp
	class const_demo {
	private:
		enum {Pi = 3.1415);
		double s = 2*Pi;
	};
```
+	使用关键字 static
	与其他静态常量存储在一起
`	class demo {static const int Months=12};`

##	作用域内枚举
	`enum class egg {small, large};`
	使用后需要通过枚举名限定枚举量
	`egg demo1 = egg::large;`
	并且关闭了隐式转换的特性

##	友元
+	友元函数
+	友元类
+	友元成员函数
将函数声明放在类体内,public private 内都无所谓
在声明前面加上friend关键字

##	类的自动转换和强制类型转换
当类有仅一个参数的构造函数时
遇到合适的会进行自动转换
explicit 	禁止单个参数构造函数导致的隐式自动类型转换
仍然可以使用显式强制转换(当不存在二义性时)

###	转换函数
`int aaa = int(demo);`
operator typeName();
+	必须是类成员
+	不能有参数
+	不能指定返回类型

## 继承

基类对象需要在进入派生类的构造函数之前被创建,通常使用初始化列表解决
`demo::demo() : base();`
### 继承方式
1. public
  + 基类的公有成员在派生类中仍然是公有的。
  + 基类的保护成员在派生类中仍然是保护的。
  + 基类的私有成员在派生类中不可访问。 
  + 派生类*对象*可以访问基类的公有成员。
  + 派生类*对象*不能直接访问基类的*私有成员*

2. protected
  + 基类的公有成员和保护成员在派生类中都变成保护的。
  + 基类的私有成员在派生类中仍然不可访问。
  + 派生类*对象*不能直接访问基类的*所有成员*

3. private
  + 基类所有成员变成私有
  + 派生类*对象*不能直接访问基类的*所有成员*

__保护成员在派生类中的访问性__
无论继承方式,派生类都可以访问基类的 `protected` 这是因为保护成员的设计初衷就是允许派生类访问，但不允许*派生类的对象*访问

### virtual
虚函数是通过在基类中使用关键字 virtual 声明的成员函数

虚函数的主要作用是实现动态绑定或运行时多态.当通过基类指针或引用调用虚函数时，程序会根据对象的实际类型（运行时类型）来调用相应的函数版本

为了实现动态绑定，C++ 编译器会在每个具有虚函数的类的对象中隐式添加一个指针，指向一个虚函数表（V-Table）。V-Table 是一个函数指针数组，存储了类中所有虚函数的地址。当通过指针或引用调用虚函数时，程序会通过 V-Table 查找并调用正确的函数版本

**虚函数的特点**  
+ 必须是成员函数
+ 派生类继承基类的虚函数，但可以重写它
+ 覆盖:派生类中的虚函数与基类中的虚函数具有相同的签名
+ 多态:通过基类指针或引用调用虚函数时，会根据对象的实际类型调用相应的函数版本

**纯虚函数与抽象类**
如果一个虚函数在基类中没有实现，而是通过在函数声明后加上 = 0 来表示，那么这个函数称为纯虚函数。包含纯虚函数的类称为抽象类

`virtual void display() = 0; // 纯虚函数`

## 设计理念

### is-a
### ABC
即使用[纯虚函数](#virtual)构造的抽象类

无法构建对象,只能用于构造其他派生类

用于提取一系列对象的共性以共用

即抽象类设计理念,把一系列对象的共性提取出来,创建一个*抽象*的类
