C++ 再整理

This commit is contained in:
Estom
2021-04-14 15:45:25 +08:00
parent ebfe8e0679
commit cdc586d735
37 changed files with 2232 additions and 707 deletions

View File

@@ -13,6 +13,7 @@
"xlocbuf": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xstring": "cpp"
"xstring": "cpp",
"xtree": "cpp"
}
}

View File

@@ -1,4 +1,3 @@
## 0 编译工具
### Linux编译工具

View File

@@ -1,311 +0,0 @@
# 变量和基本类型
## 0 类型树
- 简单类型
- 算术类型
- 整型 int、long、bool、char
- 浮点型 float、double
- 复合类型
- 引用&a
- 指针\*pointer
- 数组 a[]
- 字符串"hello world"
- 类类型
- string
- vector
## 1 基本内置类型
> 数据类型主要包含内置数据类型算数类型整型浮点型指针类型自定义数据类型vector=C 数组类型string=C 字符串类型)
### 算术类型
- 布尔型
bool 1
- 字符型
char 1
wchar_t 2
char16_t
char32_t
- 整型
short 2
int 4
long 4
long long 8
- 浮点型
float 4
double 8
long double 16
### 类型修饰符-signed 与 unsigned
> 常见的类型修饰符signed、unsigned、long、short、const、static、extern、auto、register、mutable、thread_local、volatile、restrict
- signed 有符号
- unsigned 无符号
### 类型转换
- 强制类型转换
可能会丢失精度。
- 隐式类型转换
默认情况下会向上转换,即向位数更多的位置转换。
- 避免无符号和有符号的混用。带符号的数会自动转换成无符号的数。
### 字面值常量
- 整型字面常量
不同进制指的是字面格式其转化为存储格式时都会变成二进制存储。不必担心十进制与二进制之间的运算。字面常量只是一种数字的表示方法。24,024,0x24。会自动选用尺寸最小的整型进行存储负号是对字面值取相反数不是字面值的一部分。
- 浮点型字面值常量
默认情况下使用 double 存储。3.14,3.14E0,10e4
- 字符和字符串的字面常量
'a'"adbd"字符串的字面值是由字符数组构成的。编译器会在每个字符串常量的末尾添加一个'\0'
- 转义序列
\n\t\a\v\\\b\"\?\'\r\f
\7\0\12\115
- 可以指定字面值常量的类型。字面值会有默认的常量类型。
常见前缀 uUnicode 16Uunicode32 L宽字符 u8utf-8
常见后缀 u/Uunsigned,l/Llong,ll/LLlonglong,f/Ffloat,
- bool 字面值falsetrue
- 指针字面值nullptr
## 2 变量
> 关于 C++中常见的描述总结:对象 object存储数据并具有某种类型的内存空间。已经命名的对象叫变量。变量和对象通常通用。值表示只读的数据。变量和对象是可变的数据。
### 变量定义
- 类型说明符+名称+初始化。
- 初始值:可以直接初始化、复制初始化。不等同于赋值。
- 直接初始化:使用=进行初始化。
- 列表初始化:使用{}进行初始化。
- 默认初始化:变量没有被显示初始化。
### 初始化和赋值
- 初始化和赋值是两个不同点操作。使用等号能够表示赋值,也能够实现初始化。
```
int d = 1;//直接初始化
int e = {2};//直接初始化
int f{3};//花括号列表初始化
int g(4);//括号列表,列表初始化
```
### 变量的声明(extern)
- 分离式编译机制:声明和定义可以分离。声明使得程序可以被程序所知道,定义负责创建与名字关联的实体(变量空间)。多个文件可以独立编译。声明并没有申请对象空间(变量空间)。
- extern 可以声明,而不进行定义。变量可以被声明很多次,但只能被定义一次。不能再函数体中对 extern 声明的变量进行修改extern 声明的变量只能是外部变量。
- 多次声明必须兼容,类型一致。
```
extern int i;//声明,但没有申请对象空间。
int j;//声明并定义了,申请了兑现该控件
```
### 静态类型语言
- 编译阶段,进行类型检查。
### 标识符与关键字。
![](image/2021-03-03-17-45-17.png)
### 作用域
- 名字在所有花括号之外,则成为全局作用域。
- 名字在其他作用与内,则成为块作用域。
- 内层作用域,外层作用域。
## 3 复合类型
### 引用
- 由基本类型组合而来。不能单独存在。
- int &ref=a.表示对 a 的引用。引用本身并非对象,只是已经存在的对象的别名
- 引用必须在定义的时候被初始化。
- 引用是变量的别名,具有同一个变量地址(变量空间)。
- 允许多个连续的引用
int &r=i,r2=i2.表示 r 是 i 的引用,但 r2 是 int
- 连续定义必须都加引用符号。
int &r=i,&r2=i2;
> 编译过程解释:一般初始化的时候(变量定义),编译器会将一个值拷贝到新建的变量空间中,然后与变量绑定。引用变量在初始化的时候,不进行拷贝,而是将原来的变量空间与新的对象绑定在一起。
### 指针
- 实现了间接访问。本身是一个对象。与引用不同,指针有自己的变量空间。
- 允许对指针进行复制和 copy。
- int\* dp,dp1.其中 dp 是 int 指针dp1 是 int 类型。
- 不能定义指向引用的指针,因为引用不是对象,没有内存空间。
- 连续定义,必须都加\*号
### 指针值的四种状态
- 指向一个对象;
- 指向紧邻对象所占空间的下一个位置;
- 空指针意味着指针没有指向任何对象;
- 无效指针,指针指向的位置被释放。
### 指针访问
- 利用指针运算符访问指针指向的对象(\*p
- 空指针的生成方法
```
int *p1 = nullptr;
```
### void\*
- 可以存放任意对象的地址。
### 复合类型的声明
int \*p1,p2;
p1 是指向 int 的指针p2 是 int 类型的。
### 指向指针的指针
int i=0;
int \*p1=&i;
int \*\*p2 = &p1
## 4 const 限定符
### 特点
- 它的值不能被改变。
- 仅在文件内有效。多个文件出现了同名的 const 变量时,等同于在不同文件中分别定义了独立变量。
### 使用
- const 的值是常量表达式,则直接在头文件中定义`const a = 3*5`include 这个头文件就能使用 const a 的值。
- const 的初始值通过函数计算得出不是常量表达式const 变量需要在不同的文件间共享,需要在头文件中 extern 声明,在 cpp 文件中进行定义。
![](image/2021-03-04-10-42-02.png)
### const 引用
- const 引用,可以引用一个常量、变量,生成一个只读的变量。
```
int i =1;
const int &r2 =i;
```
### const 与指针
- “指向常量的指针”
> 指针指向的值不能修改。
```
const double * cptr = π
```
![](image/2021-03-04-10-48-02.png)
- 指向非常量的“指向常量的指针”
```
double a = 3;
const double *b = &a
```
- 指针常量。
> 指针本身的地址不能被修改。
```
int n = 0;
int *const nptr = &n;
```
- 顶层 const 表示指针本身是个常量。
- 底层 const 表示指针所指向的对象是一个常量。
### 常量表达式
- 常量表达式值不会改变,并且在编译过程中就能得到计算结果的表达式。
- 字面值是常量表达式
- 用常量表达式初始化 const 变量也是常量表达式。
```
const int max = 20;//是
const int limt = max +1;//是
int a =27//不是,左值不是常量表达式
const int sz = get_size();//不是,右值不是常量表达式
```
## 5 处理类型
### 类型别名——typedef
- typedef 使用最后一个名词作为名字
```
typedef double hello;
typedef double* p;
typedef int h[81];
typedef struct tag
{
int iNum;
long lLength;
}MyStruct;//定义了结构体typedef struct tag MyStruct
```
### 类型别名——using
- 类型别名和类型等价
```
using SI = Sales_item;
```
### auto 类型说明符
- 编译器自动分析类型
```
auto item = val1 +val2
```
## 6 自定义数据结构
### 定义一个类型。
```
struct data{
std::string bookNo;
unsigned sold = 0;
double revenue = 0.0;
};
```
### 类成员
- 数据成员
- 成员函数
### 定义类的对象
```
data a,b;
```
### 访问类对象
```
a.sold =1;
```
## 7 常用限定符说明
### 存储类别
* auto 默认存储类别自动变量int i = 3;
* static 静态存储类别的变量静态变量static int m = 5;
* register 寄存器变量register n = 6;
* extern 声明外部链接属性
### 类型限定
* const限定制度变量、常变量 const int i = 6;必须直接初始化,避免对重要手进行错误修改。
* volatile限定隐式存取变量。

View File

@@ -0,0 +1,146 @@
# 类型
> 类是一种类型,是对象的模板,而不是变量或对象。它内部本身不包含对象。只是声明了这种对象的存在。只有创建对象的时候,才会真正的创建对象内部的对象。
## 1 类型
- 简单类型
- 整型 `int、short、long、longlong、bool、char、size_t`
- 浮点型 `float、double、long double`
- 复合类型
- 引用`&a`
- 指针`*pointer`
- 数组 `a[]`
- 字符串`"hello world"`
- 自定义类型
-`class`
- 结构体`struct`
- 枚举`enum`
- 联合体`union`
## 2 字面值常量
> 常量主要包括字面值常量和自定义常量。
### 整型字面常量
* 不同进制指的是字面格式其转化为存储格式时都会变成二进制存储。不必担心十进制与二进制之间的运算。字面常量只是一种数字的表示方法。24,024,0x24。会自动选用尺寸最小的整型进行存储负号是对字面值取相反数不是字面值的一部分。
```C
85 // 十进制
0213 // 八进制
0x4b // 十六进制
30 // 整数
30u // 无符号整数
30l // 长整数
30ul // 无符号长整数
```
### 浮点型字面值常量
* 默认情况下使用 double 存储。3.14,3.14E0,10e4
```C
3.14159 // 合法的
314159E-5L // 合法的
510E // 非法的:不完整的指数
210f // 非法的:没有小数或指数
.e55 // 非法的:缺少整数或分数
```
### 字符和字符串的字面常量
* 'a'"adbd"字符串的字面值是由字符数组构成的。编译器会在每个字符串常量的末尾添加一个'\0'
### 转义序列
```C
\n\t\a\v\\\b\"\?\'\r\f
\7\0\12\115
```
### 可以指定字面值常量的类型。字面值会有默认的常量类型。
* 常见前缀 `uUnicode 16Uunicode32 L宽字符 u8utf-8`
* 常见后缀 `u/Uunsigned,l/Llong,ll/LLlonglong,f/Ffloat`
### bool字面值常量
```C
falsetrue
```
### 指针字面值常量
```C
NULL//C中是NULL
nullptr//C++中是nullptr
```
### 自定义常量
这些常量都不是字面值常量,而是用户自定义的常量。在 C++ 中,有两种简单的定义常量的方式:
* 使用 #define 预处理器。编译器常量,编译期间使用常量代替原来的值。
* 使用 const 关键字。运行时常量,运行时值不可修改。
## 3 变量
> 关于 C++中常见的描述总结:对象 object存储数据并具有某种类型的内存空间。已经命名的对象叫变量。变量和对象通常通用。值表示只读的数据。变量和对象是可变的数据。
### 变量的声明(extern)
- 分离式编译机制:声明和定义可以分离。声明使得程序可以被程序所知道,定义负责创建与名字关联的实体(变量空间)。多个文件可以独立编译。声明并没有申请对象空间(变量空间)。
- extern 可以声明,而不进行定义。变量可以被声明很多次,但只能被定义一次。不能再函数体中对 extern 声明的变量进行修改extern 声明的变量只能是外部变量。
- 函数不需要使用extern即可实现声明和定义分离。
- 多次声明必须兼容,类型一致。
```
extern int i;//声明,但没有申请对象空间。
int j;//声明并定义了,申请了兑现该控件
```
### 变量的定义和初始化
变量定义的时候,编译器会分配内存空间。并执行初始化函数。
- 默认初始化:变量没有被显示初始化。
- 值初始化:()
- 直接初始化:使用(val)进行初始化。
- 拷贝初始化:使用=进行初始化
- 列表初始化:使用{}进行初始化。
```
int d;//默认初始化
int d();//值初始化
int d(1);//直接初始化
int d = 1;//复制初始化
int e = {2};//复制初始化
int f{3};//花括号列表初始化
```
> 初始化相关的内容参考[初始化的方法](../面试/11.初始化的方法.md)。
>
> 初始化和赋值是两个不同点操作。使用等号能够表示赋值,也能够实现初始化。
>
> C++是静态类型语言。在编译阶段,进行类型检查。
## 4 标识符与关键字。
![](image/2021-03-03-17-45-17.png)
## 5 作用域
### 作用域定义
- 全局作用域:名字在所有花括号之外。
- 块作用域:名字在其他作用域内。
- 函数体作用域
- 类作用域
- 普通块作用域
### 作用域作用
- 内层作用域,外层作用域。
- 控制名称的可见性,防止出现明明冲突。
- 在进行符号查找的时候先查找内部作用域,再查找外层作用域。直到发现符号。
## 6 C++中有两种类型的表达式
* 左值lvalue指向内存位置的表达式被称为左值lvalue表达式。左值可以出现在赋值号的左边或右边。
* 右值rvalue术语右值rvalue指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式也就是说右值可以出现在赋值号的右边但不能出现在赋值号的左边。
* 变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:

View File

@@ -0,0 +1,25 @@
# 基本内置类型
> 数据类型主要包含内置数据类型算数类型整型浮点型指针类型自定义数据类型vector=C 数组类型string=C 字符串类型)
## 1 基本类型
### 布尔型
* bool 1
### 字符型
* char 1
* wchar_t 2
* char16_t
* char32_t
### 整型
* short 2
* int 4
* long 4
* long long 8
### 浮点型
* float 4
* double 8
* long double 16

View File

@@ -0,0 +1,184 @@
# 复合类型
## 1 引用
- 由基本类型组合而来。不能单独存在。
- int &ref=a.表示对 a 的引用。引用本身并非对象,只是已经存在的对象的别名
- 引用必须在定义的时候被初始化。
- 引用是变量的别名,具有同一个变量地址(变量空间)。
- 允许多个连续的引用
int &r=i,r2=i2.表示 r 是 i 的引用,但 r2 是 int
- 连续定义必须都加引用符号。
int &r=i,&r2=i2;
> 编译过程解释:一般初始化的时候(变量定义),编译器会将一个值拷贝到新建的变量空间中,然后与变量绑定。引用变量在初始化的时候,不进行拷贝,而是将原来的变量空间与新的对象绑定在一起。
## 2 指针
### 指针的定义
- 实现了间接访问。本身是一个对象。与引用不同,指针有自己的变量空间。
- 允许对指针进行复制和 copy。
- int\* dp,dp1.其中 dp 是 int 指针dp1 是 int 类型。
- 不能定义指向引用的指针,因为引用不是对象,没有内存空间。
- 连续定义,必须都加\*号
### 指针值的四种状态
- 指向一个对象;
- 指向紧邻对象所占空间的下一个位置;
- 空指针意味着指针没有指向任何对象;
- 无效指针,指针指向的位置被释放。
### 指针访问
- 利用指针运算符访问指针指向的对象(\*p
- 空指针的生成方法
```
int *p1 = nullptr;
```
### `void*`
- 可以存放任意对象的地址。
### 复合类型的声明
```
int *p1,p2;
```
* p1 是指向 int 的指针p2 是 int 类型的。
### 指向指针的指针
```
int i=0;
int *p1=&i;
int **p2 = &p1
```
## 3 数组
### 定义和初始化内置数组
```
int arr[10];//含有10个整型的数组
int *arr[20];//含有20个整形指针的数组。指针数组
int (*ptr)[10];指向含有是个整数的数组的指针。数组的指针(常量的指针和指针类型的常量类似)
string arr[10];//含有10个string对象的数组C++11允许数组为对象类型。
```
### 数组的初始化(仅有两种方法)
```
int arr[3]={1,2,3};
int arr[]={1,2,3,4};
char a3[]="c++"//自动添加表示字符串结束的空字符
```
### 数组的遍历
- C++11 数组也可以使用容器遍历(集合遍历)
```
//下标遍历
int m[5]={1,2,3,4,5};
for(int i=0;i<5;i++){
cout<<m[i]<<endl;
}
//容器遍历
int k[] = {1,2,3};
for (auto l:k){
cout<<l<<endl;
}
//迭代器遍历
int m[5]={1,2,3,4,5};
int* beg =begin(m);
int* last = end(m);
while(beg!=last){
cout<<*beg<<endl;
beg++;
}
```
### 指针和数组
- 数组变量是指向数组第一个元素的指针。
```
string nums[]={"a","b","c"};
string *p2 = &num[0];
string *p3 = num;//相互等价
```
- 数组指针本身也是迭代器。可以使用 begin 函数和 end 函数,实现迭代器遍历。
```
int ia[]={1,2,3};
int *beg = begin(ia);
int *last = end(ia);
```
- 下标访问和指针运算等价
```
int ia[]={1,2,3};
ia[2];
*(ia+2);//相互等价
```
### 多维数组定义和初始化
* 列表初始化支持只初始化局部元素
```
int ia[2][3]={
{1,2,3},
{4,5,6}
};//等价
int ib[2][3]={1,2,3,4,5,6}//等价
```
### 多维数组的引用
```
ia[2][3]
```
### 多维数组的遍历
* 两层for循环处理多维数组
```
int ia[row_cnt][col_cnt]
for(int i=0;i<row_cnt;i++){
for(int j=0;j<col_cnt;j++){
ia[i][j];
}
}
```
* 两层容器遍历,遍历多维数组。
```
int ia[2][3] = {1, 2, 3, 4, 5, 6};
//这里使用引用的原因是,数组在传递时候不能直接赋值!!!!
//如果不加引用将会出现以下情况
//auto row = ia[0]。但是数组没办法赋值。所以生成数组的一个引用
//auto &row =ia[0]。相当于生成ia[0]指针的一个别名。
//书上这么说,外层引用添加的原因,是防止第二层数组被当做指针使用。
for (auto &row : ia)
{
for (auto col : row)
{
cout << col << endl;
}
}
```
![](image/2021-03-04-17-20-26.png)
## 字符串
### C 风格的字符串
> 尽量不要使用 C 风格的字符串.C++风格的字符串可以完全替代 C 风格的字符串,并且可以使用字符数组代替 C 风格的字符串。

View File

@@ -0,0 +1,67 @@
# 自定义数据类型
> 自定义类型的变量,一般称为对象。
## 类类型
### 类型定义
包括以下两类成员
- 数据成员
- 成员函数
```
class Data{
public:
int m;
int n;
int hello(){
return 1;
}
}
```
### 变量对象定义
```
Data a,b;
```
### 变量对象使用
```
a.m;
a.hello();
```
## 2 结构体类型
### 类型定义
```
struct data{
std::string bookNo;
unsigned sold = 0;
double revenue = 0.0;
};
```
### 变量对象定义
### 变量对象使用
## 3 枚举类型
### 类型定义
### 变量对象定义
### 变量对象使用
## 4 联合体类型
### 类型定义
### 变量对象定义
### 变量对象使用

View File

@@ -0,0 +1,241 @@
# 类型的修饰和处理
> 目录
> * [类型转换]()
> * [类型别名]()
> * [类型修饰]()
## 1 类型转换
> [参考](../面试/10.强制类型转换.md)
### 隐式类型转换
### 强制类型转化
* C风格类型转换
* C++风格类型转换。
## 2 类型别名
### typedef
- typedef 使用最后一个名词作为名字
```
typedef double hello;
typedef double* p;
typedef int h[81];
typedef struct tag
{
int iNum;
long lLength;
}MyStruct;//定义了结构体typedef struct tag MyStruct
```
### using
- 类型别名和类型等价
```
using si = Sales_item;//类的别名
```
## 3 类型修饰
> 常见的类型修饰符signed、unsigned、long、short、const、static、extern、auto、register、mutable、thread_local、volatile、restrict
### 简单对比
* auto 默认存储类别自动变量int i = 3;
* static 静态存储类别的变量静态变量static int m = 5;
* register 寄存器变量register n = 6;
* volatile限定隐式存取变量。不会被优化不会从寄存器中读取变量而是直接从内存中读取变量。
* extern 声明外部链接属性
* const制度变量、常变量 const int i = 6;必须直接初始化,避免对重要手进行错误修改。
* mutable可以修改类内部的变量值。
## 3.1 符号signed/unsigned
- signed 有符号
- unsigned 无符号
## 3.2 自动变量auto
- 声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。
```C
auto f=3.14; //double
auto s("hello"); //const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3='r';//错误,必须是初始化为同一类型
auto item = val1 +val2
```
## 3.3 声明修饰符extern
1. extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
2. 当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解extern 是用来在另一个文件中声明一个全局变量或函数。
3. extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候
```C
//文件2
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
//文件1
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
```
## 3.4 静态修饰符static
1. static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
2. static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
3. 在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。
```C
#include <iostream>
// 函数声明
void func(void);
static int count = 10; /* 全局变量 */
int main()
{
while(count--)
{
func();
}
return 0;
}
// 函数定义
void func( void )
{
static int i = 5; // 局部静态变量
i++;
std::cout << "变量 i 为 " << i ;
std::cout << " , 变量 count 为 " << count << std::endl;
}
```
## 3.5 寄存器内存register/volatile
* volatile内存变量。告诉编译器不需要优化volatile声明的变量让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化将内存中的变量值放在寄存器中以加快读写效率。
* register寄存器变量。register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 'register' 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。
```C
{
register int miles;
}
```
## 3.6 线程变量thread_local
* 使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
* thread_local 说明符可以与 static 或 extern 合并。
* 可以将 thread_local 仅应用于数据声明和定义thread_local 不能用于函数声明或定义。
```C
thread_local int x; // 命名空间下的全局变量
class X
{
static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s; // X::s 是需要定义的
void foo()
{
thread_local std::vector<int> v; // 本地变量
}
```
## 3.7 const 限定符
### 特点
- 它的值不能被改变。
- 仅在文件内有效。多个文件出现了同名的 const 变量时,等同于在不同文件中分别定义了独立变量。
### 使用
- const 的值是常量表达式,则直接在头文件中定义`const a = 3*5`include 这个头文件就能使用 const a 的值。
- const 的初始值通过函数计算得出不是常量表达式const 变量需要在不同的文件间共享,需要在头文件中 extern 声明,在 cpp 文件中进行定义。
![](image/2021-03-04-10-42-02.png)
### const引用
- const 引用,可以引用一个常量、变量,生成一个只读的变量。
```
int i =1;
const int &r2 =i;
```
### const指针
- “指向常量的指针”
> 指针指向的值不能修改。
```
const double * cptr = &pi;
```
![](image/2021-03-04-10-48-02.png)
- 指向非常量的“指向常量的指针”
```
double a = 3;
const double *b = &a
```
- 指针常量。
> 指针本身的地址不能被修改。
```
int n = 0;
int *const nptr = &n;
```
- 顶层 const 表示指针本身是个常量。
- 底层 const 表示指针所指向的对象是一个常量。
### 常量表达式
- 常量表达式值不会改变,并且在编译过程中就能得到计算结果的表达式。
- 字面值是常量表达式
- 用常量表达式初始化 const 变量也是常量表达式。
```
const int max = 20;//是
const int limt = max +1;//是
int a =27//不是,左值不是常量表达式
const int sz = get_size();//不是,右值不是常量表达式
```
## 3.8 可变修饰符mutable
* mutable 说明符仅适用于类的对象。它允许对象的成员替代常量。也就是说mutable 成员可以通过 const 成员函数修改。

View File

@@ -79,12 +79,13 @@ using SI = Sales_item;
## 6 auto
### 基本使用
* 编译器自动分析类型
```
auto item = val1 +val2
```
### 原理
* 编译器自动分析类型
## 7 decltype
### 基本使用
@@ -95,4 +96,81 @@ decltype(f()) sum =x;
### 原理
* 根据表达式推断类型,而不会计算表达式。类型推断。
> 略过的constexp,
> 略过的constexp,
## 8 volatile
### 基本使用
```
volatile data-definition;
```
### 原理
关键字 volatile 是与 const 绝对对立的。它指示一个变量也许会被某种方式修改,这种方式按照正常程序流程分析是无法预知的(例如,一个变量也许会被一个中断服务程序所修改)。这个关键字使用下列语法定义:
变量如果加了 volatile 修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。 
volatile应用比较多的场合在中断服务程序和cpu相关寄存器的定义。
## 9 extern "C"
### 使用
```C
//moduleA头文件
#ifndef __MODULE_A_H //对于模块A来说这个宏是为了防止头文件的重复引用
#define __MODULE_A_H
int fun(int, int);
#endif
//moduleA实现文件moduleA.C //模块A的实现部分并没有改变
#include"moduleA"
int fun(int a, int b)
{
return a+b;
}
//moduleB头文件
#idndef __MODULE_B_H //很明显这一部分也是为了防止重复引用
#define __MODULE_B_H
#ifdef __cplusplus //而这一部分就是告诉编译器如果定义了__cplusplus(即如果是cpp文件
extern "C"{ //因为cpp文件默认定义了该宏),则采用C语言方式进行编译
#include"moduleA.h"
#endif
//其他代码
#ifdef __cplusplus
}
#endif
#endif
//moduleB实现文件 moduleB.cpp //B模块的实现也没有改变只是头文件的设计变化了
#include"moduleB.h"
int main()
{
  cout<<fun(2,3)<<endl;
}
```
### 原理
* extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后会指示编译器这部分代码按C语言而不是C++的方式进行编译。由于C++支持函数重载因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中而不仅仅是函数名而C语言并不支持函数重载因此编译C语言代码的函数时不会带上函数的参数类型一般只包括函数名。
这个功能主要用在下面的情况:
1. C++代码调用C语言代码
2. 在C++的头文件中使用
3. 在多个人协同开发时可能有的人比较擅长C语言而有的人擅长C++,这样的情况下也会有用到
## 10 override关键字
* 子类重写父类函数的时候使用override关键字标识。可以省略。
## 11 final关键字
* final类不能被继承。
* final函数不能被子类重写或重定义。

View File

@@ -87,7 +87,7 @@ using namespace
- 使用下表访问 vector 元素的索引
- 不能使用下标形式向 vector 中添加元素
## 4 迭代器
## 4 标准库迭代器 Iterator
### 迭代器的使用
@@ -129,124 +129,3 @@ using namespace
cout<<mid-text.begin()<<endl;
```
## 5 数组
### 定义和初始化内置数组
```
int arr[10];//含有10个整型的数组
int *arr[20];//含有20个整形指针的数组。指针数组
int (*ptr)[10];指向含有是个整数的数组的指针。数组的指针(常量的指针和指针类型的常量类似)
string arr[10];//含有10个string对象的数组C++11允许数组为对象类型。
```
### 数组的初始化(仅有两种方法)
```
int arr[3]={1,2,3};
int arr[]={1,2,3,4};
char a3[]="c++"//自动添加表示字符串结束的空字符
```
### 数组的遍历
- C++11 数组也可以使用容器遍历(集合遍历)
```
//下标遍历
int m[5]={1,2,3,4,5};
for(int i=0;i<5;i++){
cout<<m[i]<<endl;
}
//容器遍历
int k[] = {1,2,3};
for (auto l:k){
cout<<l<<endl;
}
//迭代器遍历
int m[5]={1,2,3,4,5};
int* beg =begin(m);
int* last = end(m);
while(beg!=last){
cout<<*beg<<endl;
beg++;
}
```
### 指针和数组
- 数组变量是指向数组第一个元素的指针。
```
string nums[]={"a","b","c"};
string *p2 = &num[0];
string *p3 = num;//相互等价
```
- 数组指针本身也是迭代器。可以使用 begin 函数和 end 函数,实现迭代器遍历。
```
int ia[]={1,2,3};
int *beg = begin(ia);
int *last = end(ia);
```
- 下标访问和指针运算等价
```
int ia[]={1,2,3};
ia[2];
*(ia+2);//相互等价
```
### C 风格的字符串
> 尽量不要使用 C 风格的字符串.C++风格的字符串可以完全替代 C 风格的字符串,并且可以使用字符数组代替 C 风格的字符串。
### 多维数组定义和初始化
* 列表初始化支持只初始化局部元素
```
int ia[2][3]={
{1,2,3},
{4,5,6}
};//等价
int ib[2][3]={1,2,3,4,5,6}//等价
```
### 多维数组的引用
```
ia[2][3]
```
### 多维数组的遍历
* 两层for循环处理多维数组
```
int ia[row_cnt][col_cnt]
for(int i=0;i<row_cnt;i++){
for(int j=0;j<col_cnt;j++){
ia[i][j];
}
}
```
* 两层容器遍历,遍历多维数组。
```
int ia[2][3] = {1, 2, 3, 4, 5, 6};
//这里使用引用的原因是,数组在传递时候不能直接赋值!!!!
//如果不加引用将会出现以下情况
//auto row = ia[0]。但是数组没办法赋值。所以生成数组的一个引用
//auto &row =ia[0]。相当于生成ia[0]指针的一个别名。
//书上这么说,外层引用添加的原因,是防止第二层数组被当做指针使用。
for (auto &row : ia)
{
for (auto col : row)
{
cout << col << endl;
}
}
```
![](image/2021-03-04-17-20-26.png)

View File

@@ -6,7 +6,7 @@
> - 继承
> - 多态
## 1 定义抽象数据类型(抽象
## 1 类的构成——抽象
### 概念
@@ -14,20 +14,20 @@
- 类的接口包括用户所能执行的操作;类的实现包括类的数据成员、负责接口实现的函数体以及定义类所需要的的各种私有函数。
- 封装实现了类的接口和实现的分离。
### 设计
### 类成员
- 定义包含数据成员成员函数
- 定义包含**数据成员**和**成员函数**
- 定义在类内部的函数是隐式的 inline 函数!
### this 指针
### this指针
- this 指针。用来指明当前绑定的对象。只在依赖对象的函数中添加 this 指针。
### 常量成员函数
### 常量成员
- 常量成员函数。const 关键字放在成员函数的参数列表之后。常量函数的 this 指针指向常量对象。不能对常量对象内数据进行修改。
```
```C++
class A{
const int getM(){//表示返回值是const类型的
@@ -72,12 +72,13 @@ hello{
}
```
## 2 访问控制与封装(封装)
## 2 访问控制——封装
### 访问说明符
- public对外部函数可见。
- private被类成员访问但不能被外部访问。private 部分封装了类的实现细节。
- protect对子类可见
### 定义类

View File

@@ -0,0 +1,64 @@
# 复杂类型
> 除了类之外还有Union、Enum连个特殊的类型。
## Union
### 概念
union即为联合它是一种特殊的类。通过关键字union进行定义一个union可以有多个数据成员。
```
union Token{
char cval;
int ival;
double dval;
};
```
### 用法
1. 互斥赋值。在任意时刻,联合中只能有一个数据成员可以有值。当给联合中某个成员赋值之后,该联合中的其它成员就变成未定义状态了。
```
Token token;
token.cval = 'a';
token.ival = 1;
token.dval = 2.5;
```
2. 访问权限。联合可以为其成员指定public、protected和private等访问权限默认情况下其成员的访问权限为public。
3. 为成员指定长度。联合的存储空间至少能够容纳其最大的数据成员。也可以为联合的成员指定长度。通过冒号操作符来实现成员长度的指定。
```C
union U {
unsigned short int aa;
struct {
unsigned int bb : 7;//(bit 0-6)
unsigned int cc : 6;//(bit 7-12)
unsigned int dd : 3;//(bit 13-15)
};
} u;
```
## Enum
### 定义
* 枚举类型的定义:枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。枚举类型的定义格式为:
* 关键字enum——指明其后的标识符是一个枚举类型的名字。
* 枚举常量表——由枚举常量构成。"枚举常量"或称"枚举成员",是以标识符形式表示的整型量,表示枚举类型的取值。枚举常量表列出枚举类型的所有取值,各枚举常量之间以""间隔,且必须各不相同。取值类型与条件表达式相同。
```
enum <类型名> {<枚举常量表>};
```
### 用法
* 用来声明一个两所有可能的取值。使用标识符表示其种类。
### 应用举例
```
enum color_set1 {RED, BLUE, WHITE, BLACK}; // 定义枚举类型color_set1
enum week {Sun, Mon, Tue, Wed, Thu, Fri, Sat}; // 定义枚举类型week
```

View File

@@ -3,5 +3,6 @@
using namespace std;
int main(){
int b =1;
return 0;
}

View File

@@ -0,0 +1,223 @@
> 目录
> * cstring 字符串和内存复制
> * cstdlib 字符串和内存分配
> * cstdio 输入输出
> * cmath 数学函数
> * ctime 时间函数
## 1 头文件cstring
```
#include<cstring>
```
| 函数 | 描述 |
|---|---|
| void *memchr(const void *str, int c, size_t n)|在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c一个无符号字符的位置。 |
| int memcmp(const void *str1, const void *str2, size_t n)|把 str1 和 str2 的前 n 个字节进行比较。 |
| void *memcpy(void *dest, const void *src, size_t n)|从 src 复制 n 个字符到 dest。 |
| void *memmove(void *dest, const void *src, size_t n)|另一个用于从 src 复制 n 个字符到 dest 的函数。 |
| void *memset(void *str, int c, size_t n)|复制字符 c一个无符号字符到参数 str 所指向的字符串的前 n 个字符。 |
| char *strcat(char *dest, const char *src)|把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 |
| char *strncat(char *dest, const char *src, size_t n)|把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。 |
| char *strchr(const char *str, int c)|在参数 str 所指向的字符串中搜索第一次出现字符 c一个无符号字符的位置。 |
| int strcmp(const char *str1, const char *str2)|把 str1 所指向的字符串和 str2 所指向的字符串进行比较。 |
| int strncmp(const char *str1, const char *str2, size_t n)|把 str1 和 str2 进行比较,最多比较前 n 个字节。 |
| int strcoll(const char *str1, const char *str2)|把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 |
| char *strcpy(char *dest, const char *src)|把 src 所指向的字符串复制到 dest。 |
| char *strncpy(char *dest, const char *src, size_t n)|把 src 所指向的字符串复制到 dest最多复制 n 个字符。 |
| size_t strcspn(const char *str1, const char *str2)|检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。 |
| char *strerror(int errnum)|从内部数组中搜索错误号 errnum并返回一个指向错误消息字符串的指针。 |
| size_t strlen(const char *str)|计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 |
| char *strpbrk(const char *str1, const char *str2)|检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。 |
| char *strrchr(const char *str, int c)|在参数 str 所指向的字符串中搜索最后一次出现字符 c一个无符号字符的位置。 |
| size_t strspn(const char *str1, const char *str2)|检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。 |
| char *strstr(const char *haystack, const char *needle)|在字符串 haystack 中查找第一次出现字符串 needle不包含空结束字符的位置。 |
| char *strtok(char *str, const char *delim)|分解字符串 str 为一组字符串delim 为分隔符。 |
| size_t strxfrm(char *dest, const char *src, size_t n)|根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。 |
## 2 头文件cstdlib
```
#include<cstdlib>
```
### 字符串相关
| 函数 | 描述 |
|---|----|
| double atof(const char *str)|把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| int atoi(const char *str)|把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。 |
| long int atol(const char *str)|把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)。 |
| double strtod(const char *str, char **endptr)|把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| long int strtol(const char *str, char **endptr, int base)|把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)。 |
| unsigned long int strtoul(const char *str, char **endptr, int base)|把参数 str 所指向的字符串转换为一个无符号长整数(类型为 unsigned long int 型)。 |
### 内存分配
| 函数 | 描述 |
|---|----|
| void *calloc(size_t nitems, size_t size)|分配所需的内存空间,并返回一个指向它的指针。 |
| void free(void *ptr)|释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。 |
| void *malloc(size_t size)|分配所需的内存空间,并返回一个指向它的指针。 |
| void *realloc(void *ptr, size_t size)|尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。 |
### 随机数
| 函数 | 描述 |
|---|----|
int rand(void)|返回一个范围在 0 到 RAND_MAX 之间的伪随机数。
void srand(unsigned int seed)|该函数播种由函数 rand 使用的随机数发生器。
### 系统函数
| 函数 | 描述 |
|---|----|
void abort(void)|使一个异常程序终止。
int atexit(void (*func)(void))|当程序正常终止时,调用指定的函数 func。
void exit(int status)|使程序正常终止。
char *getenv(const char *name)|搜索 name 所指向的环境字符串,并返回相关的值给字符串。
int system(const char *string)|由 string 指定的命令传给要被命令处理器执行的主机环境。
## 3 头文件cstdio
```C
#include<cstdio>
```
### 文件函数
* File access:
| 函数 | 描述 |
|---|----|
fclose
Close file (function )
fflush
Flush stream (function )
fopen
Open file (function )
freopen
Reopen stream with different file or mode (function )
setbuf
Set stream buffer (function )
setvbuf
Change stream buffering (function )
* Direct input/output:
| 函数 | 描述 |
|---|----|
fread
Read block of data from stream (function )
fwrite
Write block of data to stream (function )
* File positioning:
| 函数 | 描述 |
|---|----|
fgetpos
Get current position in stream (function )
fseek
Reposition stream position indicator (function )
fsetpos
Set position indicator of stream (function )
ftell
Get current position in stream (function )
rewind
Set position of stream to the beginning (function )
* Error-handling:
| 函数 | 描述 |
|---|----|
clearerr
Clear error indicators (function )
feof
Check end-of-file indicator (function )
ferror
Check error indicator (function )
perror
Print error message (function )
### 格式化输入输出函数
Formatted input/output:
| 函数 | 描述 |
|---|----|
fprintf
Write formatted data to stream (function )
fscanf
Read formatted data from stream (function )
printf
Print formatted data to stdout (function )
scanf
Read formatted data from stdin (function )
snprintf
Write formatted output to sized buffer (function )
sprintf
Write formatted data to string (function )
sscanf
Read formatted data from string (function )
vfprintf
Write formatted data from variable argument list to stream (function )
vfscanf
Read formatted data from stream into variable argument list (function )
vprintf
Print formatted data from variable argument list to stdout (function )
vscanf
Read formatted data into variable argument list (function )
vsnprintf
Write formatted data from variable argument list to sized buffer (function )
vsprintf
Write formatted data from variable argument list to string (function )
vsscanf
Read formatted data from string into variable argument list (function )
### 字符输入输出函数
Character input/output:
| 函数 | 描述 |
|---|----|
fgetc
Get character from stream (function )
fgets
Get string from stream (function )
fputc
Write character to stream (function )
fputs
Write string to stream (function )
getc
Get character from stream (function )
getchar
Get character from stdin (function )
gets
Get string from stdin (function )
putc
Write character to stream (function )
putchar
Write character to stdout (function )
puts
Write string to stdout (function )
ungetc
Unget character from stream (function )
## 5 头文件ctime
```
#include<ctime>
```
| 函数 | 描述 |
|---|----|
char *asctime(const struct tm *timeptr)|返回一个指向字符串的指针,它代表了结构 timeptr 的日期和时间。
clock_t clock(void)|返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。|char *ctime(const time_t *timer)|返回一个表示当地时间的字符串,当地时间是基于参数 timer。|double difftime(time_t time1, time_t time2)|返回 time1 和 time2 之间相差的秒数 (time1-time2)。
struct tm *gmtime(const time_t *timer)|timer 的值被分解为 tm 结构并用协调世界时UTC也被称为格林尼治标准时间GMT表示。
struct tm *localtime(const time_t *timer)|timer 的值被分解为 tm 结构,并用本地时区表示。
time_t mktime(struct tm *timeptr)|把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)|根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。
time_t time(time_t *timer)|计算当前日历时间,并把它编码成 time_t 格式。

View File

@@ -290,54 +290,20 @@ int main()
### 头文件
```
#include<cstring>
#include<string.h>
```
### 方法
| 函数 | 描述 |
|---|---|
| void *memchr(const void *str, int c, size_t n)|在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c一个无符号字符的位置。 |
| int memcmp(const void *str1, const void *str2, size_t n)|把 str1 和 str2 的前 n 个字节进行比较。 |
| void *memcpy(void *dest, const void *src, size_t n)|从 src 复制 n 个字符到 dest。 |
| void *memmove(void *dest, const void *src, size_t n)|另一个用于从 src 复制 n 个字符到 dest 的函数。 |
| void *memset(void *str, int c, size_t n)|复制字符 c一个无符号字符到参数 str 所指向的字符串的前 n 个字符。 |
| char *strcat(char *dest, const char *src)|把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 |
| char *strncat(char *dest, const char *src, size_t n)|把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。 |
| char *strchr(const char *str, int c)|在参数 str 所指向的字符串中搜索第一次出现字符 c一个无符号字符的位置。 |
| int strcmp(const char *str1, const char *str2)|把 str1 所指向的字符串和 str2 所指向的字符串进行比较。 |
| int strncmp(const char *str1, const char *str2, size_t n)|把 str1 和 str2 进行比较,最多比较前 n 个字节。 |
| int strcoll(const char *str1, const char *str2)|把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 |
| char *strcpy(char *dest, const char *src)|把 src 所指向的字符串复制到 dest。 |
| char *strncpy(char *dest, const char *src, size_t n)|把 src 所指向的字符串复制到 dest最多复制 n 个字符。 |
| size_t strcspn(const char *str1, const char *str2)|检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。 |
| char *strerror(int errnum)|从内部数组中搜索错误号 errnum并返回一个指向错误消息字符串的指针。 |
| size_t strlen(const char *str)|计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 |
| char *strpbrk(const char *str1, const char *str2)|检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。 |
| char *strrchr(const char *str, int c)|在参数 str 所指向的字符串中搜索最后一次出现字符 c一个无符号字符的位置。 |
| size_t strspn(const char *str1, const char *str2)|检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。 |
| char *strstr(const char *haystack, const char *needle)|在字符串 haystack 中查找第一次出现字符串 needle不包含空结束字符的位置。 |
| char *strtok(char *str, const char *delim)|分解字符串 str 为一组字符串delim 为分隔符。 |
| size_t strxfrm(char *dest, const char *src, size_t n)|根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。 |
### 头文件
```
#include<cstdlib>
#include<cstdio>
```
### 方法
| 函数 | 描述 |
|---|----|
| double atof(const char *str)|把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| int atoi(const char *str)|把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。 |
| long int atol(const char *str)|把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)。 |
| double strtod(const char *str, char **endptr)|把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| long int strtol(const char *str, char **endptr, int base)|把参数 str 所指向字符串转换为一个长整数(类型为 long int 型)。 |
| unsigned long int strtoul(const char *str, char **endptr, int base)|把参数 str 所指向字符串转换为一个无符号长整数(类型为 unsigned long int 型)。 |
| void *calloc(size_t nitems, size_t size)|分配所需的内存空间,并返回一个指向它的指针。 |
| void free(void *ptr)|释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。 |
| void *malloc(size_t size)|分配所需的内存空间,并返回一个指向它的指针。 |
| void *realloc(void *ptr, size_t size)|尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。 |
| 名称 | 函数 & 目的 |
|---|---|
| strcpy(s1, s2); | 复制字符串 s2 到字符串 s1。 |
| strcat(s1, s2); | 连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号,例如:string str1 = "runoob";string str2 = "google";string str = str1 + str2; |
| strlen(s1); | 返回字符串 s1 的长度。 |
| strcmp(s1, s2); | 如果 s1 和 s2 是相同的,则返回 0如果 s1<s2 则返回值小于 0如果 s1>s2 则返回值大于 0。 |
| strchr(s1, ch); | 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
| strstr(s1, s2); | 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
## 7 字符串分割的两种处理方式

View File

@@ -0,0 +1,138 @@
```C
/*
* C++ string 类的实现
* 1. 构造函数和析构函数
* 2. 字符串长度
* 3. 重载=运算符
* 4. 重载+=运算符
* 5. 重载<< >> 运算符
* 6. 重载比较运算符
* 7. 重载[]下标运算符
*/
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
private:
char * str;
int length;
public:
// 长度
int size ()const {
return length;
};
char* getstr()const{
return str;
}
// 默认构造函数
MyString();
// 字符串构造函数
MyString(const char*);
// 复制构造函数
MyString(const MyString& b);
// 重载等号运算符
MyString& operator=(const MyString &b);
// 重载+=运算符
MyString& operator+=(const MyString &b);
// 重载比较运算符
bool operator<(const MyString &b);
// 重载下标运算符
char& operator[](const int &index) const ;
// 重载输入输出操作
friend ostream& operator<<(ostream& ,const MyString &b);
~MyString();
};
MyString::MyString()
{
str = new char[1];
str[0]='\0';
length = 0;
}
MyString::MyString(const char* b){
if(b){
length = strlen(b);
str = new char[length+1];
strcpy(str,b);
}
else{
MyString();
}
}
MyString::MyString(const MyString&b){
length = b.size();
if(length>0)
str = new char[length+1];
else
MyString();
}
MyString& MyString::operator=(const MyString &b){
if(&b == this){
return *this;
}
delete[] str;
length = b.size();
str = new char[length + 1];
strcpy(str,b.getstr());
return *this;
}
MyString& MyString::operator+=(const MyString&b){
if(b.size()==0){
return *this;
}
char* temp = new char[length+b.length+1];
strcpy(temp,str);
strcat(temp,b.getstr());
delete[] str;
str = temp;
return *this;
}
char& MyString::operator[](const int &index)const {
if(index>length)return str[length];
return str[index];
}
bool MyString::operator<(const MyString &b){
for(int i=0;i<length;i++){
if(i>b.size())return false;
if(b[i]>str[i])return true;
if(b[i]<str[i])return false;
}
return true;
}
MyString::~MyString()
{
delete[] str;
}
// 外部定义一个函数,内部声明为友元
ostream& operator<<(ostream &out,const MyString&b){
out<<b.getstr();
return out;
}
int main()
{
// 测试函数
MyString s1,s2="123",s3,s4="456";
s3=s2;
s1=s2;
s1+=s1;
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
cout<<(s3<s4)<<endl;
cout<<endl;
return 0;
}
```

View File

@@ -1,22 +0,0 @@
# 操作重载与类型转换
## 1 基本概念
### 基础
* 运算符时具有特殊名字的函数由关键字operator和气候定义的运算符共同组成。
* 可以被重载的运算符
![](image/2021-03-06-23-22-55.png)
## 2 输入输出运算符
定义重载运算符。
```C++
ostream &operator<<(ostream &os , const Sales data &item) {
os << 工 tem . isbn() << " " << item. units sold << " " << item. revenue << " " << item.avg_price ();
return os;
}
```

View File

@@ -0,0 +1,505 @@
# 运算符重载的两种方式
## 1 基本概念
### 基础
* 运算符时具有特殊名字的函数由关键字operator和气候定义的运算符共同组成。
* 可以被重载的运算符
![](image/2021-03-06-23-22-55.png)
### 方式
1. 将运算符重载为类的**成员函数**。
2. 重载运算符函数,并声明为类的**友元**。
### 规则
1. 重载后的运算符必须至少有一个操作数是用户定义的类型,这是为了防止程序员为标准类型重载运算符,可以确保程序正确运行。
2. 不能修改运算符的优先级不能违反运算符原本的运算规则。例如在重载加号时不能提供两个形参友元除外例如下面的这种重载方式就是不被允许的因为加法是一种双目运算符。在重载为类的成员函数后加法运算的第一项应该是调用函数的一个对象。所以在运算符重载时参数表中的参数数目应该是重载运算符的操作数减1。
```
Time operator+(const Time &t1const Time &t2) const;
```
3. 不能创造新的运算符例如不能定义operator**()运算符;
4. 不能重载以下运算符;
```C
.
.*
::
?:
siezofsizeof运算符
```
5. 很多运算符可以通过成员或者非成员函数进行重载,但是以下四种只能通过成员函数进行重载;
```C
=
( )
[ ]:
->:
```
6. 自增运算符(++)与自减运算符(--)由于自增和自减运算符是单目运算符,在重载时应该是没有参数的,但是又有前置与后置之分,例如++i与i++。为了隽星区分C++做了规定;
```C
Time operator++() //前置
Time operator++(int) //后置
```
## 2 成员函数重载
### 成员函数重载格式
* 将操作符重载为成员函数。
* 此时类的对象作为操作符的第一个操作数。流输入输出运算符无法通过这样的方式重载,因为对象本身应该作为第二个操作数,只能通过友元函数的方式对操作进行重载。
```C++
class MyString{
public:
// 重载等号运算符
MyString& operator=(const MyString &b);
// 重载+=运算符
MyString& operator+=(const MyString &b);
}
```
## 3 友元重载
### 友元的格式
 C++中友元有三种,分别是友元函数,友元类,友元成员函数,这里介绍的是友元函数。
1. 创建友元函数的第一步是声明友元函数的声明放在类的声明中并且在前面加上friend关键字。例如
```C
friend ostream& operator<<(ostream &os, const Time &t);
```
2. 第二步是定义友元友元可以直接在声明的时候进行定义即内联的定义。也可以定义在类外定义在类外时不需要加类作用域运算符也不需要有friend关键字。
```C
//友元在类外定义的时候不需要添加friend
ostream & operator<<(ostream &os, const Time &t)
{
os << "hours:" << t.hours << " " << "mintues:" << t.mintues << " ";
return os;
}
```
### 友元使用
与普通的运算符重载成员函数一样,友元也可以直接调用
```C
cout<<time1<<time2;//等价于
operator<<(operator<<(cout,time1),time2);
```
## 4 实例——MyString的实现
```C++
/*
* C++ string 类的实现
* 1. 构造函数和析构函数
* 2. 字符串长度
* 3. 重载=运算符
* 4. 重载+=运算符
* 5. 重载<< >> 运算符
* 6. 重载比较运算符
* 7. 重载[]下标运算符
*/
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
private:
char * str;
int length;
public:
// 长度
int size ()const {
return length;
};
char* getstr()const{
return str;
}
// 默认构造函数
MyString();
// 字符串构造函数
MyString(const char*);
// 复制构造函数
MyString(const MyString& b);
// 重载等号运算符
MyString& operator=(const MyString &b);
// 重载+=运算符
MyString& operator+=(const MyString &b);
// 重载比较运算符
bool operator<(const MyString &b);
// 重载下标运算符
char& operator[](const int &index) const ;
// 重载输入输出操作
friend ostream& operator<<(ostream& ,const MyString &b);
~MyString();
};
MyString::MyString()
{
str = new char[1];
str[0]='\0';
length = 0;
}
MyString::MyString(const char* b){
if(b){
length = strlen(b);
str = new char[length+1];
strcpy(str,b);
}
else{
MyString();
}
}
MyString::MyString(const MyString&b){
length = b.size();
if(length>0)
str = new char[length+1];
else
MyString();
}
MyString& MyString::operator=(const MyString &b){
if(&b == this){
return *this;
}
delete[] str;
length = b.size();
str = new char[length + 1];
strcpy(str,b.getstr());
return *this;
}
MyString& MyString::operator+=(const MyString&b){
if(b.size()==0){
return *this;
}
char* temp = new char[length+b.length+1];
strcpy(temp,str);
strcat(temp,b.getstr());
delete[] str;
str = temp;
return *this;
}
char& MyString::operator[](const int &index)const {
if(index>length)return str[length];
return str[index];
}
bool MyString::operator<(const MyString &b){
for(int i=0;i<length;i++){
if(i>b.size())return false;
if(b[i]>str[i])return true;
if(b[i]<str[i])return false;
}
return true;
}
MyString::~MyString()
{
delete[] str;
}
// 外部定义一个函数,内部声明为友元
ostream& operator<<(ostream &out,const MyString&b){
out<<b.getstr();
return out;
}
int main()
{
// 测试函数
MyString s1,s2="123",s3,s4="456";
s3=s2;
s1=s2;
s1+=s1;
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
cout<<(s3<s4)<<endl;
cout<<endl;
return 0;
}
```
## 5 实例——complex的实现
```C++
#include <iostream>
using namespace std;
class complex{
public:
complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ };
public:
friend complex operator+(const complex & A, const complex & B);
friend complex operator-(const complex & A, const complex & B);
friend complex operator*(const complex & A, const complex & B);
friend complex operator/(const complex & A, const complex & B);
friend istream & operator>>(istream & in, complex & A);
friend ostream & operator<<(ostream & out, complex & A);
private:
double m_real; //实部
double m_imag; //虚部
};
//重载加法运算符
complex operator+(const complex & A, const complex &B){
complex C;
C.m_real = A.m_real + B.m_real;
C.m_imag = A.m_imag + B.m_imag;
return C;
}
//重载减法运算符
complex operator-(const complex & A, const complex &B){
complex C;
C.m_real = A.m_real - B.m_real;
C.m_imag = A.m_imag - B.m_imag;
return C;
}
//重载乘法运算符
complex operator*(const complex & A, const complex &B){
complex C;
C.m_real = A.m_real * B.m_real - A.m_imag * B.m_imag;
C.m_imag = A.m_imag * B.m_real + A.m_real * B.m_imag;
return C;
}
//重载除法运算符
complex operator/(const complex & A, const complex & B){
complex C;
double square = A.m_real * A.m_real + A.m_imag * A.m_imag;
C.m_real = (A.m_real * B.m_real + A.m_imag * B.m_imag)/square;
C.m_imag = (A.m_imag * B.m_real - A.m_real * B.m_imag)/square;
return C;
}
//重载输入运算符
istream & operator>>(istream & in, complex & A){
in >> A.m_real >> A.m_imag;
return in;
}
//重载输出运算符
ostream & operator<<(ostream & out, complex & A){
out << A.m_real <<" + "<< A.m_imag <<" i ";;
return out;
}
int main(){
complex c1, c2, c3;
cin>>c1>>c2;
c3 = c1 + c2;
cout<<"c1 + c2 = "<<c3<<endl;
c3 = c1 - c2;
cout<<"c1 - c2 = "<<c3<<endl;
c3 = c1 * c2;
cout<<"c1 * c2 = "<<c3<<endl;
c3 = c1 / c2;
cout<<"c1 / c2 = "<<c3<<endl;
return 0;
}
```
## 6 实例——MyTime的实现
```C++
//------mytime.h
#ifndef MYTIME_H
#define MYTIME_H
#include <iostream>
using namespace std;
class Time
{
//----------私有成员,类中的成员默认是私有的
private:
int hours;
int mintues;
//----------共有成员
public:
Time(); //默认构造函数
Time(int h, int m = 0); //显式构造函数
Time(const Time &); //拷贝构造函数
~Time(); //析构函数
void AddMin(int m);
void AddHour(int h);
void reset(int h = 0, int m = 0);
//------展示函数show()
void Time::show() const
{
cout << "hours:" << hours << " " << "mintues:" << mintues << " ";
}
Time operator+(const Time &t) const; //运算符重载
Time operator-(const Time &t) const;
Time operator*(double n) const;
friend Time operator*(double n, const Time &t) //友元;
{
return t*n; //在这里又调用了重载运算符 operator*(double n) const
} //内联形式的定义;
friend ostream & operator<<(ostream &os, const Time &t); //一个双目运算符在重载时,如果是以友元的形式声明的,那么他有两个形参;如果是类的成员函数,那么他只有一个形参;
};
//-------时间重置,内联函数
inline void Time::reset(int h, int m)
{
hours = h;
mintues = m;
}
#endif
//--mytime.cpp
#include <iostream>
#include "mytime.h"
using namespace std;
//-------默认构造函数
Time::Time()
{
hours = mintues = 0;
cout << "调用默认构造函数" << endl;
}
//------显式的构造函数
Time::Time(int h, int m) :hours(h), mintues(m)
{
cout << "调用显式构造函数" << endl;
}
//------拷贝构造函数
Time::Time(const Time &t)
{
hours = t.hours;
mintues = t.mintues;
cout << "调用拷贝构造函数" << endl;
}
//------析构函数
Time::~Time()
{
cout << "调用了析构函数" << endl;
}
//-------小时相加
void Time::AddHour(int h)
{
hours += h;
}
//------分钟相加
void Time::AddMin(int m)
{
mintues += m;
hours += mintues / 60;
mintues %= 60;
}
//------重载+号
Time Time::operator+(const Time &t) const
{
Time sum;
sum.mintues = mintues + t.mintues;
sum.hours = hours + t.hours + sum.mintues / 60;
sum.mintues = sum.mintues % 60;
return sum;
}
//------重载-号
Time Time::operator-(const Time &t) const
{
Time diff;
int time1 = hours * 60 + mintues;
int time2 = t.hours * 60 + t.mintues;
diff.hours = (time1 - time2) / 60;
diff.mintues = (time1 - time2) % 60;
return diff;
}
//-------重载乘号
Time Time::operator*(double n) const
{
Time result;
long totalMintues = n*hours * 60 + n*mintues;
result.hours = totalMintues / 60;
result.mintues = totalMintues % 60;
return result;
}
//-------友元输出操作符
ostream & operator<<(ostream &os, const Time &t) //友元在类外定义的时候不需要添加friend
{
os << "hours:" << t.hours << " " << "mintues:" << t.mintues << " ";
return os;
}
//-----------------------
//main.cpp
//不用先生
//------------------------
#include <iostream>
#include "mytime.h"
using namespace std;
int main()
{
{
Time eat_breakfast(0, 45);
Time eat_lunch(1, 0);
Time eat_dinner(1, 30);
Time swiming(0, 45); //非const对象既可以调用const成员函数也可以调用非const成员。
const Time study(8, 5); //const对象只能调用const成员函数。
// study_cut_swim;
Time study_cut_swim = study - swiming; //调用运算符重载后的Time类的减号
Time Eat_time_day = eat_breakfast + eat_dinner + eat_lunch; //调用了重载以后的加法;
cout << "学习比游泳多花" << study_cut_swim << endl; //调用友元输出运算符<<
cout << "每周吃饭所花费的时间为" << (7 * Eat_time_day) << endl; //调用了友元乘法以及输出运算符;
}
system("pause");
return 0;
}
```

View File

@@ -0,0 +1,50 @@
# 面向对象程序设计
> 参考文献
> * [多态的三种方式](https://blog.csdn.net/qq_41306849/article/details/109081625)
> * [虚继承和虚基类](http://c.biancheng.net/view/2280.html)
> 面向对象的基本概念
> * 数据抽象和封装(在语法基础部分讲解过了)
> * 继承
> * 多态(动态绑定)
## 1 面向对象程序设计
### 核心思想
* 抽象:类的接口与实现分离。
* 封装隐藏内部实现细节。访问控制运算符public/private/protect
* 继承:定义相似的类型,对相似的关系建模。实现代码重用。
* 多态:可以在以一定程度上忽略相似类型的区别。
### 继承概念
> 继承、泛化
* 继承:联系在一起的类构成以中层次关系
* 基类:层次关系的根部
* 派生类:其他类则直接或间接地从基类继承而来。
* 派生类与基类的函数继承:
* 与类型相关的函数。基类与派生类类型不同,需要重写。
* 与类型无关的函数。派生类直接继承,不需要修改。
* 类派生列表:派生类通过类派生列表,明确指出它的基类。
```
class Dog:public Animal{
public:
double price()const override;
}
```
* 派生类可以通过**override关键字**注明改写基类的函数。
### 多态(动态绑定)
* 在运行时选择函数的版本。通过使用动态绑定我们能用同一段代码分别处理Animal和Dog的对象。

View File

@@ -0,0 +1,145 @@
# 继承与派生
## 1 继承
### 定义基类
```
class Quote{
public:
Quote() = default;
Quote(string book,double sales_price):book_no(book),price(sales_price){};
string isbn()const{
return this.book_no;
}
virtual double net_price(int n)const{
return n*price;
}
virtual ~Quote()=default;
private:
string book_no;
protected:
double price;
};
```
### 定义派生类
```
class Bulk_quote:public Quote{
public:
Bulk_quote()=default;
Bulk_quote(string,double,int,double);
double net_price(int n)const override;
private:
int mn_qty = 0;
double discount=0.0;
};
Bulk_quote::Bulk_quote(string book,double p,int qty,double disc):Quote(book,p),min_qty(qty),discount(disc){}//委托基类构造函数
```
### 继承方式
* 单一继承:继承一个父类,这种继承称为单一继承,一般情况尽量使用单一继承,使用多重继承容易造成混乱易出问题
* 多重继承继承多个父类类与类之间要用逗号隔开类名之前要有继承权限假使两个或两个基类都有某变量或函数在子类中调用时需要加类名限定符如c.a::i = 1
* 菱形继承多重继承掺杂隔代继承1-n-1模式此时需要用到虚继承例如 BC虚拟继承于AD再多重继承BC否则会出错
### 继承权限
* 继承权限:继承方式规定了如何访问继承的基类的成员。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限
![](image/2021-03-07-14-26-29.png)
### 注意事项
* 基类的静态成员变量,在整个继承体系中只存在该成员的唯一定义。
* 派生类的声明中包含类名,但不能包含派生列表。
### final关键字
* 使用**关键字final**说明符防止类被继承
* 使用**关键字final**说明符防止函数被重写
```
class A final{
void f1(int) const;
};
```
## 2 访问控制与继承
### 访问控制
* 派生类能够访问公有成员和受保护的成员。
* 派生类不能访问私有成员。
public:
private:
protected:
### 继承类型
* public 公有继承
* private私有继承
### 默认的集成保护级别
* class关键字定义的派生类默认是私有继承
* struct关键字定义的派生类默认是公有继承
## 3 类作用域与继承
### 作用域
* 当存在继承关系是,派生类的作用域嵌套在基类的作用域内。如果一个名字在派生类的作用域内无法解析,编译器在外层的基类作用域中寻找改名字的定义。
### 编译时名字查找
* 引用或指针的静态类型决定了该对象有哪些成员是可见的。一个基类的引用和指针只能访问基类的成员。即是动态对象是其派生类。
### 名字冲突与继承
* 派生了重用定义在直接基类或间接基类中的名字,会屏蔽定义在外层作用域基类中的名字
### 访问隐藏的成员
* 通过作用域运算符来使用一个被隐藏的基类成员。
## 4 类型转换与继承
### 静态类型和动态类型
* 静态类型在编译时已知。是指针或者引用的类型。
* 动态类型表示内存中的对象类型。动态类型直到运行时才可知。
### 派生类到基类的类型转换。
* 把派生类对象当成基类对象来使用。将基类的指针或引用绑定到派生类对象。
### 基类到派生类的类型转换。
* 不存在从基类向派生类的隐式转换。
### 对象之间不存在类型转换
* 所谓的**类型转换只是指针或者引用的类型转换**,对象本身的类型,没有发生改变。
* 但是派生类可以赋值给基类的对象。基类的拷贝构造函数和移动构造函数,接受一个引用对象。将派生类对象赋值给引用对象,实现基类的初始化。实际生成的是一个基类对象。
* 这里并非多态,而是执行拷贝构造函数。创建了一个基类对象
```
class A{
}
class B:public A{
}
B b();
A a = b;//可以赋值给基类对象。执行拷贝构造函数。并非多态。
A aa(b);//直接初始化,执行基类的拷贝构造函数。并非多态。
```
### 转换规则总结
* 从派生类向基类的类型转换只对指针或引用有效。是指针或引用的类型转换,而不是其指向的对象的类型发生改变。
* 基类向派生类不存在隐式类型转换
* 派生类向基类的类型转换也可能会由于访问受限而变得不可行。

View File

@@ -1,119 +1,5 @@
# 面向对象程序设计
> 参考文献
> * [多态的三种方式](https://blog.csdn.net/qq_41306849/article/details/109081625)
> * [虚继承和虚基类](http://c.biancheng.net/view/2280.html)
> 面向对象的基本概念
> * 数据抽象和封装(在语法基础部分讲解过了)
> * 继承
> * 多态(动态绑定)
## 1 OOP概述
### 面向对象程序设计
* 核心思想:
* 数据抽象:类的接口与实现分离。
* 继承:定义相似的类型,对相似的关系建模。实现代码重用。
* 动态绑定:可以在以一定程度上忽略相似类型的区别。
### 继承概念(继承)
* 继承:联系在一起的类构成以中层次关系
* 基类:层次关系的根部
* 派生类:其他类则直接或间接地从基类继承而来。
* 派生类与基类的函数继承:
* 与类型相关的函数。基类与派生类类型不同,需要重写。
* 与类型无关的函数。派生类直接继承,不需要修改。
* 类派生列表:派生类通过类派生列表,明确指出它的基类。
```
class Dog:public Animal{
public:
double price()const override;
}
```
* 派生类可以通过**override关键字**注明改写基类的函数。
### 动态绑定(多态)
* 在运行时选择函数的版本。通过使用动态绑定我们能用同一段代码分别处理Animal和Dog的对象。
## 2 继承与多态详解
## 2.1 继承
### 定义基类
```
class Quote{
public:
Quote() = default;
Quote(string book,double sales_price):book_no(book),price(sales_price){};
string isbn()const{
return this.book_no;
}
virtual double net_price(int n)const{
return n*price;
}
virtual ~Quote()=default;
private:
string book_no;
protected:
double price;
};
```
### 定义派生类
```
class Bulk_quote:public Quote{
public:
Bulk_quote()=default;
Bulk_quote(string,double,int,double);
double net_price(int n)const override;
private:
int mn_qty = 0;
double discount=0.0;
};
Bulk_quote::Bulk_quote(string book,double p,int qty,double disc):Quote(book,p),min_qty(qty),discount(disc){}//委托基类构造函数
```
### 继承方式
* 单一继承:继承一个父类,这种继承称为单一继承,一般情况尽量使用单一继承,使用多重继承容易造成混乱易出问题
* 多重继承继承多个父类类与类之间要用逗号隔开类名之前要有继承权限假使两个或两个基类都有某变量或函数在子类中调用时需要加类名限定符如c.a::i = 1
* 菱形继承多重继承掺杂隔代继承1-n-1模式此时需要用到虚继承例如 BC虚拟继承于AD再多重继承BC否则会出错
### 继承权限
* 继承权限:继承方式规定了如何访问继承的基类的成员。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限
![](image/2021-03-07-14-26-29.png)
### 注意事项
* 基类的静态成员变量,在整个继承体系中只存在该成员的唯一定义。
* 派生类的声明中包含类名,但不能包含派生列表。
### final关键字
* 使用**关键字final**说明符防止类被继承
* 使用**关键字final**说明符防止函数被重写
```
class A final{
void f1(int) const;
};
```
## 2.2 多态
## 1 多态
### 多态分类
@@ -196,48 +82,7 @@ int main(){
//参考虚函数。
## 2.3 类型转换
### 静态类型和动态类型
* 静态类型在编译时已知。是指针或者引用的类型。
* 动态类型表示内存中的对象类型。动态类型直到运行时才可知。
### 派生类到基类的类型转换。
* 把派生类对象当成基类对象来使用。将基类的指针或引用绑定到派生类对象。
### 基类到派生类的类型转换。
* 不存在从基类向派生类的隐式转换。
### 对象之间不存在类型转换
* 所谓的**类型转换只是指针或者引用的类型转换**,对象本身的类型,没有发生改变。
* 但是派生类可以赋值给基类的对象。基类的拷贝构造函数和移动构造函数,接受一个引用对象。将派生类对象赋值给引用对象,实现基类的初始化。实际生成的是一个基类对象。
* 这里并非多态,而是执行拷贝构造函数。创建了一个基类对象
```
class A{
}
class B:public A{
}
B b();
A a = b;//可以赋值给基类对象。执行拷贝构造函数。并非多态。
A aa(b);//直接初始化,执行基类的拷贝构造函数。并非多态。
```
### 转换规则总结
* 从派生类向基类的类型转换只对指针或引用有效。是指针或引用的类型转换,而不是其指向的对象的类型发生改变。
* 基类向派生类不存在隐式类型转换
* 派生类向基类的类型转换也可能会由于访问受限而变得不可行。
## 3 虚函数
## 2 虚函数
### 虚函数的定义
* 虚函数:基类希望它的派生类自定义适合自身的版本。为了实现多态
@@ -319,45 +164,7 @@ int a 继承A的成员
int b B成员
```
## 4 访问控制与继承
### 访问控制
* 派生类能够访问公有成员和受保护的成员。
* 派生类不能访问私有成员。
public:
private:
protected:
### 继承类型
* public 公有继承
* private私有继承
### 默认的集成保护级别
* class关键字定义的派生类默认是私有继承
* struct关键字定义的派生类默认是公有继承
## 5 继承中的类作用域
### 作用域
* 当存在继承关系是,派生类的作用域嵌套在基类的作用域内。如果一个名字在派生类的作用域内无法解析,编译器在外层的基类作用域中寻找改名字的定义。
### 编译时名字查找
* 引用或指针的静态类型决定了该对象有哪些成员是可见的。一个基类的引用和指针只能访问基类的成员。即是动态对象是其派生类。
### 名字冲突与继承
* 派生了重用定义在直接基类或间接基类中的名字,会屏蔽定义在外层作用域基类中的名字
### 访问隐藏的成员
* 通过作用域运算符来使用一个被隐藏的基类成员。
## 6 虚继承和虚基类
## 3 虚继承和虚基类
### 多继承
* 多继承Multiple Inheritance是指从多个直接基类中产生派生类的能力多继承的派生类继承了所有父类的成员。尽管概念上非常简单但是多个基类的相互交织可能会带来错综复杂的设计问题命名冲突就是不可回避的一个。

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -12,6 +12,10 @@
type-id(expression)//转换格式2
```
- 强制类型转换。可能会丢失精度。
- 隐式类型转换。默认情况下会向上转换,即向位数更多的位置转换。
- 避免无符号和有符号的混用。带符号的数会自动转换成无符号的数。
### C++强制类型转换
```

View File

@@ -0,0 +1,29 @@
## 概述
### 概念
* 编译器为程序中的每个“数据单元”安排在适当的位置上。
### 原因
1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
补充观点字节对齐主要是为了提高内存的访问效率比如intel 32位cpu每个总线周期都是从偶地址开始读取32位的内存数据如果数据存放地址不是从偶数开始则可能出现需要两个总线周期才能读取到想要的数据因此需要在内存中存放数据时进行对齐。
### 规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)n=1,2,4,8,16来改变这一系数其中的n就是你要指定的“对齐系数”。
1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员第一个数据成员放在offset为0的地方以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身长度中比较小的那个进行。
2. 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。
3. 结合1、2可推断#pragma pack的n值等于或超过所有数据成员长度的时候这个n值的大小将不产生任何效果。
### 结构体对齐规则
1. 结构体变量的起始地址能够被其最宽的成员大小整除
2. 结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节
3. 结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节

View File

@@ -0,0 +1,27 @@
## 基本功能
1. 实现头文件的封装MyString.h
2. 缺省构造函数对字符串的初始化MyString()
3. 使用构造函数初始化字符串的另外两种方式,动态指针+拷贝构造函数
4. 析构函数,释放动态申请的字符串空间
5. 重载输出运算符 <<
6. 重载赋值运算符 =
7. 重载下标运算符 [],索引输出
## 拓展功能.
1. 字符串长度的比较
2. 字符串的排序功能
3. 字符串的倒置
4. 字符串中指定两个字符的交换
5. 查找某字符串是否位于指定的字符串中(采用暴力查找)

136
C++/面试/20.cpp Normal file
View File

@@ -0,0 +1,136 @@
/*
* C++ string 类的实现
* 1. 构造函数和析构函数
* 2. 字符串长度
* 3. 重载=运算符
* 4. 重载+=运算符
* 5. 重载<< >> 运算符
* 6. 重载比较运算符
* 7. 重载[]下标运算符
*/
#include <iostream>
#include <cstring>
using namespace std;
class MyString
{
private:
char * str;
int length;
public:
// 长度
int size ()const {
return length;
};
char* getstr()const{
return str;
}
// 默认构造函数
MyString();
// 字符串构造函数
MyString(const char*);
// 复制构造函数
MyString(const MyString& b);
// 重载等号运算符
MyString& operator=(const MyString &b);
// 重载+=运算符
MyString& operator+=(const MyString &b);
// 重载比较运算符
bool operator<(const MyString &b);
// 重载下标运算符
char& operator[](const int &index) const ;
// 重载输入输出操作
friend ostream& operator<<(ostream& ,const MyString &b);
~MyString();
};
MyString::MyString()
{
str = new char[1];
str[0]='\0';
length = 0;
}
MyString::MyString(const char* b){
if(b){
length = strlen(b);
str = new char[length+1];
strcpy(str,b);
}
else{
MyString();
}
}
MyString::MyString(const MyString&b){
length = b.size();
if(length>0)
str = new char[length+1];
else
MyString();
}
MyString& MyString::operator=(const MyString &b){
if(&b == this){
return *this;
}
delete[] str;
length = b.size();
str = new char[length + 1];
strcpy(str,b.getstr());
return *this;
}
MyString& MyString::operator+=(const MyString&b){
if(b.size()==0){
return *this;
}
char* temp = new char[length+b.length+1];
strcpy(temp,str);
strcat(temp,b.getstr());
delete[] str;
str = temp;
return *this;
}
char& MyString::operator[](const int &index)const {
if(index>length)return str[length];
return str[index];
}
bool MyString::operator<(const MyString &b){
for(int i=0;i<length;i++){
if(i>b.size())return false;
if(b[i]>str[i])return true;
if(b[i]<str[i])return false;
}
return true;
}
MyString::~MyString()
{
delete[] str;
}
// 外部定义一个函数,内部声明为友元
ostream& operator<<(ostream &out,const MyString&b){
out<<b.getstr();
return out;
}
int main()
{
// 测试函数
MyString s1,s2="123",s3,s4="456";
s3=s2;
s1=s2;
s1+=s1;
cout<<s1<<endl;
cout<<s2<<endl;
cout<<s3<<endl;
cout<<(s3<s4)<<endl;
cout<<endl;
return 0;
}

View File

@@ -0,0 +1,12 @@
## 计划
- [x] 参加腾讯面试
- [x] 整理完成Union、Enum两个类型的数据
- [x] 实现C++ string类
- [ ] 面向对象——虚函数:构造函数能够是虚函数、多态虚函数/纯虚函数知识补充。感觉这个东西可放到C++基础中进行介绍了)
- [x] 面向对象——继承C++函数重载和运算符重载整理
- [x] C字符串整理以及其他函数了解
## 收获

BIN
数据结构/6.9 Normal file

Binary file not shown.

130
数据结构/6.9.cpp Normal file
View File

@@ -0,0 +1,130 @@
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
#include<iostream>
#include<vector>
#include<map>
using namespace std;
struct Tree{
Tree(){
m=new map<char,Tree*>();
is_word=false;
}
bool is_word=false;
map<char,Tree*> *m;
};
class Trie2 {
public:
/** Initialize your data structure here. */
Tree* tree;
Trie2() {
tree=new Tree();
}
/** Inserts a word into the trie. */
void insert(string word) {
Tree* temp;
temp=tree;
for(auto a:word){
if((temp->m)->count(a)==0){
(*(temp->m))[a]=new Tree();
}
temp=(*(temp->m))[a];
}
temp->is_word=true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Tree* temp=tree;
int i=0;
for(i=0;i<word.size();i++){
if((*(temp->m)).count(word[i])>0){
temp=(*(temp->m))[word[i]];
}
else{
break;
}
}
if(i==word.size() && temp->is_word)return true;
else return false;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Tree* temp=tree;
int i=0;
for(i=0;i<prefix.size();i++){
if((*(temp->m)).count(prefix[i])>0){
temp=(*(temp->m))[prefix[i]];
}
else{
break;
}
}
if(i==prefix.size())return true;
else return false;
}
};
// 官方没有使用心得结构体
// 使用向量存储映射而不是map效率更高。
class Trie {
private:
vector<Trie*> children;
bool isEnd;
Trie* searchPrefix(string prefix) {
Trie* node = this;
for (char ch : prefix) {
ch -= 'a';
if (node->children[ch] == nullptr) {
return nullptr;
}
node = node->children[ch];
}
return node;
}
public:
Trie() : children(26), isEnd(false) {}
void insert(string word) {
Trie* node = this;
for (char ch : word) {
ch -= 'a';
if (node->children[ch] == nullptr) {
node->children[ch] = new Trie();
}
node = node->children[ch];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this->searchPrefix(word);
return node != nullptr && node->isEnd;
}
bool startsWith(string prefix) {
return this->searchPrefix(prefix) != nullptr;
}
};
int main(){
Trie t;
string h = "hello";
string s1 = "hel";
string s2 = "hee";
t.insert(h);
cout<<t.startsWith(s1)<<endl;
cout<<t.search(h)<<endl;
cout<<t.search(s1)<<endl;
}