// ———————————————————————————
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; }
<a name="HXfGr"></a>
## [1.1.x] 定义
| <br />- `class`和`struct`都可以定义**类**且语法基本相同,但是在访问**权限**上有所不同<br />```cpp
class 类名{
// 默认为私有变量/函数
public:
// 公有变量/函数
private:
// 私有变量/函数
};
struct 类名{
// 默认为公有变量/函数
public:
// 公有变量/函数
private:
// 私有变量/函数
};
- 除了这两处不同,其他完全一样
|
| —- |
[1.2.x] 成员变量
| 成员变量的定义/声明和C中struct
相同,需要注意的是变量的初始化过程
在OOP语境下,我们将成员变量的含义扩展到对象,类存在成员对象
没有任何初始化手段的成员变量会变成不确定量
- 成员变量的初始化方法:(按执行先后排列)
- 通过构造函数(C’tor)的参数表(eg : model[49行])
- 在定义时直接赋值(是上面那个方案的语法糖)(eg : model[13行])
- 通过构造函数(C’tor)的函数体(eg : model[51行])
- 对于不同变量,在前两个方案中主要取决于哪个变量声明得早
- 对于同一变量,按照执行顺序排列
在当前类中没有定义初始化的成员对象,会调用其自己的默认构造器,如果不存在默认构造器就会报错
特殊成员: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;
,其中包括传参为对象的函数形参构造(即直接将对象当做参数传入函数时,会在函数内部复制一个该对象的局部变量,此时发生拷贝构造)
- 当你没有主动写入拷贝构造函数时,发生拷贝构造会采用默认策略,即直接复制所有成员。但这会带来错误,尤其是对于指针,它会指向上一个对象的指针指向的成员,而非我们希望它指向的这个对象的某个成员
- 将拷贝构造函数设置为private
可以禁止拷贝构造,但这会让你的程序变得很麻烦
上述情况中,会执行先利用"abc"
构造一个PathName
类对象,然后再做复制,但这种情况下拷贝构造可能会与重载的赋值运算产生冲突,所以可以引入explicit
关键字可以只允许这种构造但不允许在赋值中触发
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位的字符串
// 或者直接使用 = 运算符
**2.**`**.assign()**````cpp
// 基本结构与构造类似
s1.assign(); // s1 = ""
s2.assign("abc"); // s2 = "abc"
s3.assign(3,'k'); // s3 = "kkk" 即'k'重复3次,这里的第二个参数必须是字符类型
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”
// 或者直接使用 + 或者 += 运算符
**5.**`**.compare()**````cpp
// 返回值小于0表示s1更小
// 返回值等于0表示相同
// 返回值大于0表示s1更大
string s1("hello"), s2("hello, world");
int n = s1.compare(s2);
n = s1.compare(1,2,s2,0,3); //比较s1 (1 ~ (1+2))的子串和s2(0 ~ (0+3))的子串
n = s1.compare(0,2,s2); //比较s1 (0 ~ (0+2))的子串和s2
6.**.substr()**
str.substr(a,b)
返回字符串str
第a
位开始往后共b
位的子串,超出部分则忽略
7.**.swap()**
a.swap(b)
等效于swap(a,b)
,其中a
和b
都必须是字符串变量
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()**
插入串 |
| —- |