• 对象抽象
  • 对象实例

    [1.x] 类

  • 为了方便后续说明,在头部先放置一个不怎么规范的模型: ```cpp

    include

    include

    include

    using namespace std;

// ———————————————————————————

ifndef Backpack_Class

define Backpack_Class

class Backpack{ private: long long money = 0; // 钱 vector< Item > items; // 其他物品 public: Backpack(){ items.clear(); } ~Backpack(){} updateMoney(long long deltaMoney){ this->money += deltaMoney; } getThings(Item newItem){ this->items.push_back(newItem); } // 其他方法 };

endif

// ———————————————————————————

ifndef Human_Class

define Human_Class

class Human{ private: int age; // 年龄 string name; // 名字 Backpack backpack; // 背包 public: Human(); Human(string name,int age); ~Human(); string getName(); int getAge(); // 其他方法 }; Human::Human():age(0),name(“Hugh”){} Human::Human(string name, int age = 0){ this->name = name; } Human::~Human(){} string Human::getName(){ return this->name; } int Human::getAge(){ return this->age; }

endif

// ———————————————————————————

int main(){ Human Isshiki(“Isshiki Hugh”); return 0; }

  1. <a name="HXfGr"></a>
  2. ## [1.1.x] 定义
  3. | <br />- `class`和`struct`都可以定义**类**且语法基本相同,但是在访问**权限**上有所不同<br />```cpp
  4. class 类名{
  5. // 默认为私有变量/函数
  6. public:
  7. // 公有变量/函数
  8. private:
  9. // 私有变量/函数
  10. };
  1. struct 类名{
  2. // 默认为公有变量/函数
  3. public:
  4. // 公有变量/函数
  5. private:
  6. // 私有变量/函数
  7. };


- 除了这两处不同,其他完全一样
| | —- |

[1.2.x] 成员变量

| 成员变量的定义/声明和C中struct相同,需要注意的是变量的初始化过程
在OOP语境下,我们将成员变量的含义扩展到对象,类存在成员对象
没有任何初始化手段的成员变量会变成不确定量
- 成员变量的初始化方法:(按执行先后排列)
- 通过构造函数(C’tor)的参数表(eg : model[49行])
- 在定义时直接赋值(是上面那个方案的语法糖)(eg : model[13行])
- 通过构造函数(C’tor)的函数体(eg : model[51行])
- 对于不同变量,在前两个方案中主要取决于哪个变量声明得早
- 对于同一变量,按照执行顺序排列
在当前类中没有定义初始化的成员对象,会调用其自己的默认构造器,如果不存在默认构造器就会报错
image.png image.png[2.x] 类&对象 - 图3
特殊成员:this
- this是一个指向当前对象的指针
| | —- |

[1.3.x] 成员函数

| 所有成员函数中比较特殊的就是构造函数 C’tor **(constructor)析构函数 D’tor (destructor)
1.构造函数 C’tor (eg : model[43&50行])
- 形式上,我们要求
构造函数名类名相同,且不定义函数返回值类型
- 一个构造函数会在对象被创建的时候被隐式调用,以对其进行初始化构造,但不可以被显式调用
- 我们称不需要参数的构造函数为 Default C’tor
- 当一个构造函数都不声明时,存在一个
没有任何行为的 “Auto” Default C’tor(即我们视任何C++对象必须存在一个构造函数)
- 由于某些问题,我们希望每一个函数都存在一个 Default C’tor (即不管怎么样先写一个Default C’tor)
-
拷贝构造函数
- 假设现在有一个class叫A,则其A(const A &r){}为其
拷贝构造函数s
- 拷贝构造发生在形如<Class Name> <Object Name 1> = <Object Name 2>;的情况下,如A a1 = a2;,其中包括传参为对象的函数形参构造(即直接将对象当做参数传入函数时,会在函数内部复制一个该对象的局部变量,此时发生拷贝构造)
- 当你没有主动写入拷贝构造函数时,发生拷贝构造会采用默认策略,即直接复制所有成员。但这会带来错误,尤其是对于指针,它会指向上一个对象的指针指向的成员,而非我们希望它指向的这个对象的某个成员
image.png
- 将拷贝构造函数设置为private可以禁止拷贝构造,但这会让你的程序变得很麻烦
[2.x] 类&对象 - 图5
上述情况中,会执行先利用"abc"构造一个PathName类对象,然后再做复制,但这种情况下拷贝构造可能会与重载的赋值运算产生冲突,所以可以引入explicit关键字可以只允许这种构造但不允许在赋值中触发
[2.x] 类&对象 - 图6
2.析构函数 D’tor (eg : model[44&53行])
-
析构函数不能有任何参数
- 一个构造函数会在对象被销毁(生命周期结束)的时候被隐式调用
- 我们总是希望析构函数都是virtual
- “通常我们在析构函数里做的事情是处理(消除)内存以外的其他资源。”
| | —- | | 一般函数的写法有两种:
1. 直接在类内定义
(eg : model[20~25行])
- 🔗这一类函数会默认被inline修饰
- 一般我们不采用这种定义方法
2. 在类内声明,在类外定义
(eg : model[46&55行])
- 在类外定义时,我们需要使用
范围解析运算符(resolver)::(eg : model[55行])
- <Class Name>::<Function Name>表示这个<Function Name>
不自由**的,属于<Class Name>
- 一般我们采用这种方式,将声明放在类名.h文件中,将body放在类名.cpp文件中
|

[1.4.x] 权限控制Access Control

| C++中通过标签来控制之后的部分的权限,作用范围类似于switch语句中的case:
- public 公有
- private 私有
- protected 对派生类开放,对外界封闭 理解可以参考这个博客
- friend (实在听不清,引用🔗xxjj的文章)
- 显然,由于权限控制的性质,是不是friend由被访问的一方决定
> 对象不是私有的边界,类才是私有的边界

| | —- |

[2.x] STL容器

[2.1.x] string类

| 1.构造```cpp string s1(); // s1 = “” string s2(“abc”); // s2 = “abc” string s3(3,’k’); // s3 = “kkk” 即’k’重复3次,这里的第二个参数必须是字符类型 string s4(“abcdef”,2,4); // s4 = “bcde” 即”abcdef”第2位往后一共4位的字符串

// 或者直接使用 = 运算符

  1. **2.**`**.assign()**````cpp
  2. // 基本结构与构造类似
  3. s1.assign(); // s1 = ""
  4. s2.assign("abc"); // s2 = "abc"
  5. s3.assign(3,'k'); // s3 = "kkk" 即'k'重复3次,这里的第二个参数必须是字符类型
  6. s4.assign("abcdef",2,4); // s4 = "bcde" 即"abcdef"第2位往后一共4位的字符串

3.**.length()**``**.size()**
返回字符串长度

4.`.append()````cpp string s(“abc”),s1(“123”),s2(“123”),s3(“123”); s1.append(s); // s1 = “123abc” s2.append(s,1,2); // s1 = “123bc” s3.append(3,’K’); // s1 = “123KKK”

// 或者直接使用 + 或者 += 运算符

  1. **5.**`**.compare()**````cpp
  2. // 返回值小于0表示s1更小
  3. // 返回值等于0表示相同
  4. // 返回值大于0表示s1更大
  5. string s1("hello"), s2("hello, world");
  6. int n = s1.compare(s2);
  7. n = s1.compare(1,2,s2,0,3); //比较s1 (1 ~ (1+2))的子串和s2(0 ~ (0+3))的子串
  8. n = s1.compare(0,2,s2); //比较s1 (0 ~ (0+2))的子串和s2

6.**.substr()**
str.substr(a,b)返回字符串stra位开始往后共b位的子串,超出部分则忽略
7.**.swap()**
a.swap(b)等效于swap(a,b),其中ab都必须是字符串变量
8.**.find()**
string类有一系列类.find()的操作
- 找得到则返回查找内容的位置
- 找不到则返回string::npos,是一个静态常量


- find()从前往后查找子串或字符出现的位置
- rfind从后往前查找子串或字符出现的位置
- find_first_of()从前往后查找何处出现另一个字符串中包含的字符
- find_last_of()从后往前查找何处出现另一个字符串中包含的字符
- find_first_not_of()从前往后查找何处出现另一个字符串中没有包含的字符
- find_last_not_of()从后往前查找何处出现另一个字符串中没有包含的字符


9.**.replace()**
替换子串
10.**.erase()**
删除子串
11.**.insert()**
插入串 | | —- |