Skip to content

Commit

Permalink
笔记补充 && 补充 cpp 基础
Browse files Browse the repository at this point in the history
  • Loading branch information
whitestarrain committed Oct 20, 2024
1 parent 1a5843c commit 4b5bbda
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 26 deletions.
12 changes: 12 additions & 0 deletions C/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# make

[Makefile](./_reference/Makefile.md)

# autoconf

# ninja

# meson

# cmake

21 changes: 11 additions & 10 deletions C/c.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,16 @@
LD_PRELOAD=/lib64/libc-2.17.so ln -s /lib64/libc-2.17.so /lib64/libc.so.6
```


### 1.1.7. 声明和定义

- C 语言中的声明(Declaration)有变量声明、函数声明和类型声明三种。
- **如果一个变量或函数的声明要求编译器为它分配存储空间,那么也可以称为定义(Definition),因此定义是声明的一种** 。
- 在接下来几章的示例代码中变量声明都是要分配存储空间的,因而都是定义,
- 等学到[「链接详解」-「定义和声明」]()我们会看到哪些变量声明不分配存储空间因而不是定义。
- 从[「结构体」]()开始我们会看到类型声明,声明一个类型是不分配存储空间的,但似乎叫「类型定义」听起来也不错,所以在本书中「类型定义」和「类型声明」表示相同的含义。
- 声明和语句类似,也是以 `;` 号结尾的,但是在语法上声明和语句是有区别的,语句只能出现在 `{}` 括号中,而声明既可以出现在 `{}` 中也可以出现在所有 `{}` 之外。

## 1.2. 常量、变量和表达式

### 1.2.1. 注释、字符串常量、转义字符
Expand Down Expand Up @@ -632,15 +642,6 @@
double tom;
```

> - **声明和定义**
> - C 语言中的声明(Declaration)有变量声明、函数声明和类型声明三种。
> - **如果一个变量或函数的声明要求编译器为它分配存储空间,那么也可以称为定义(Definition),因此定义是声明的一种** 。
> - 在接下来几章的示例代码中变量声明都是要分配存储空间的,因而都是定义,
> - 等学到[「链接详解」-「定义和声明」]()我们会看到哪些变量声明不分配存储空间因而不是定义。
> - 在下一章我们会看到函数的定义和声明也是这样区分的,分配存储空间的函数声明可以称为函数定义。
> - 从[「结构体」]()开始我们会看到类型声明,声明一个类型是不分配存储空间的,但似乎叫「类型定义」听起来也不错,所以在本书中「类型定义」和「类型声明」表示相同的含义。
> - 声明和语句类似,也是以 `;` 号结尾的,但是在语法上声明和语句是有区别的,语句只能出现在 `{}` 括号中,而声明既可以出现在 `{}` 中也可以出现在所有 `{}` 之外。

- 浮点型有三种,
- `float` 是单精度浮点型,
- `double` 是双精度浮点型,
Expand Down Expand Up @@ -9790,7 +9791,7 @@ extern int f(void); /* previous linkage */

- 则这里的 `extern` 修饰的标识符具有 Interanl Linkage 而不是 External Linkage。
- 从上表的前两行可以总结出我们先前所说的规则「函数声明加不加 `extern` 关键字都一样」。
- 上表也说明了在文件作用域允许定义函数,在块作用域不允许定义函数,或者说函数定义不能嵌套。
- 上表也说明了在文件作用域允许定义函数,在块作用域不允许定义函数(但可以声明),或者说函数定义不能嵌套。
- 另外,在块作用域中不允许用 `static` 关键字声明函数。

##### 变量声明的规则
Expand Down
232 changes: 219 additions & 13 deletions C/cpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,34 +93,45 @@ void HelloC::hello() { cout << "hello" << endl; } // 函数实现
#### 构造函数
如果不定义,会默认会生成一个默认形式、隐藏着的构造函数,不具备任何功能
#### 拷贝构造函数
浅拷贝与深拷贝
如果不定义,系统会自动生成一个,进行两个对象成员之间对应的简单赋值 (浅拷贝)
#### 析构函数
如果不定义,会默认生成一个空的析构函数
### 访问控制
TODO: c++ 访问控制原理
c++ 访问控制原理是编译阶段实现的
访问控制是不是编译时检查的,所有成员函数应该就是全局函数,在编译的时候根据成员的访问控制类型,进行检查?
可以编译为 c 代码看看,尤其是友元函数,是不是没有额外的代码,就是告诉编译器,能访问通过?
#### 成员种类
#### 友元
即把外部的函数声明为友元类型,赋予它可以访问类内私有成员的权利,
友元的对象,它可以是全局的一般函数,也可以是其他类里的成员函数,这种叫做友元函数。不仅如此,友元还可以是一个类,这种叫做友元类。
- 友元关系不能被继承
- 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明
- 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的声明
- 友元函数并不是类的成员函数,因此在类外定义的时候不能加上class::function name
##### 友元函数
- 友元函数并不是类的成员函数,
- 因此在类外定义的时候不能加上class::function name
- 也不能使用 `this` 指针
##### 友元类
#### 常函数
#### const 成员
不允许修改
被const修饰的成员则必须进行初始化,并且不能被更改,而初始化的方式则是在类的构造函数的初始化列表里进行的。
### 运算符重载
Expand All @@ -129,25 +140,183 @@ TODO: c++ 访问控制原理
- 友元函数: 访问 private 和 projected 成员
- 常函数: 限制运算符修改实例对象
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象:
```cpp
Box operator+(const Box&);
```

大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。
如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:

```cpp
Box operator+(const Box&, const Box&);
```

## 可重载运算符/不可重载运算符

下面是可重载的运算符列表:


- 双目算术运算符: `+ (加),-(减),*(乘),/(除),% (取模)`
- 关系运算符: `==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于)`
- 逻辑运算符: `||(逻辑或),&&(逻辑与),!(逻辑非)`
- 单目运算符: `+ (正),-(负),*(指针),&(取地址)`
- 自增自减运算符: `++(自增),--(自减)`
- 位运算符: `| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)`
- 赋值运算符: `=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=`
- 空间申请与释放: `new, delete, new[ ] , delete[]`
- 其他运算符: `()(函数调用),->(成员访问),,(逗号),[](下标)`


下面是不可重载的运算符列表:

- `.`:成员访问运算符
- `.*`, `->*`:成员指针访问运算符
- `::`:域运算符
- `sizeof`:长度运算符
- `?:`:条件运算符
- `#`: 预处理符号

### 继承与派生

#### 继承方式

- 公有继承:
- (1)基类中的公有成员,在派生类中仍然为公有成员,当然无论派生里的成员函数还是派生类对象都可以访问。
- (2)基类中的私有成员,无论在派生类的成员还是派生类对象都不可以访问。
- (3)基类中的保护成员,在派生类中仍然是保护类型,可以通过派生类的成员函数访问,但派生类对象不可以访问。

- 私有继承:
> 如果为私有派生,则基类的私有成员在派生类甚至再派生出的子类中,都无法再使用,没有什么存在意义,故这种使用情况比较少。
- (1)基类的公有和受保护类型,被派生类私有继承吸收后,都变为派生类的私有类型,即在类的成员函数里可以访问,不能在类外访问。
- (2)而基类的私有成员,在派生类无论类内还是类外都不可以访问。

- 保护继承:
- (1)基类的公有成员和保护类型成员在派生类中为保护成员。
- (2)基类的私有成员在派生类中不能被直接访问。

| | 公有继承 | 公有继承 | 保护继承 | 保护继承 | 私有继承 | 私有继承 |
| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
| 访问位置 | 类内 | 类外 | 类内 | 类外 | 类内 | 类外 |
| 公有成员 | 可以 | 可以 | 可以 | 不可以 | 可以 | 不可以 |
| 保护成员 | 可以 | 不可以 | 可以 | 不可以 | 可以 | 不可以 |
| 私有成员 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 | 不可以 |

#### 构造和析构

在创建一个派生类的时候,会先创建一个基类。
需要注意的是, **派生类会吸纳基类的全部成员,但并不包括构造函数及析构函数**

- 构造函数:
- 调用顺序是先调用基类的构造函数再调用派生类的构造函数。
- 如果构造函数存在参数,就需要显式得调用基类的构造函数。
- 析构函数:
- 先调用派生类,再调用基类

#### 虚基类

在多继承关系中,如果一个派生类的从两个父类那里继承过来,并且这两个父类又恰恰是从一个基类那里继承而来,
通过虚基类只维护一份一个基类对象,避免二义性。

如果不适用虚继承,也可以显示写出调用哪个父类里面的属性。

```
obj.SuperClass::attr = value;
```

### 多态性

多态性是面向对象程序设计的重要特性之一,在面向对象程序设计中,指同样的方法被不同对象执行时会有不同的执行效果。

具体来说,多态的实现又可以分为两种:

- 编译时多态
- 编译和链接的时候就确定了具体的操作过程
- 联编在编译和链接时确认的,叫做静态联编,前面我们学习的函数重载、函数模板的实例化就属于这一类。
- 运行时多态
- 后者是在程序运行过程中才确定的操作过程。
- 是在运行的时候,才能确认执行哪段代码的,叫做动态联编,这种情况是编译的时候,还无法确认具体走哪段代码,而是程序运行起来之后才能确认。

两者相比之下,静态联编由于编译时候就已经确定好怎么执行,因此执行起来效率高;
而动态联编想必虽然慢一些,但优点是灵活。有各自不同的使用场景。

#### 虚函数

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为 **动态链接** ,或 **后期绑定**

虚函数,在调用的时候,根据的不是左值指针的类型,而是右值指针的类型。

```cpp
class Shape {
protected:
int width, height;
public:
Shape( int a=0, int b=0)
{
width = a;
height = b;
}
virtual int area()
{
cout << "Parent class area :" <<endl;
return 0;
}
};

int main( )
{
Shape *shape;
Rectangle rec(10,7);
Triangle tri(10,5);
shape = &rec;
shape->area();
shape = &tri;
shape->area();
return 0;
}
```
注意:
1. 虚函数不能是静态成员函数,或友元函数,因为它们不属于某个对象。
2. 内联函数不能在运行中动态确定其位置,即使虚函数在类的内部定义,编译时,仍将看作非内联。
3. 构造函数不能是虚函数,析构函数可以是虚函数,而且通常声明为虚函数。
#### 虚析构函数
在C++中,不能把构造函数定义为虚构造函数,因为在实例化一个对象时才会调用构造函数,
且虚函数的实现,其实本质是通过一个虚函数表指针来调用的,还没有对象更没有内存空间当然无法调用了,
故没有实例化一个对象之前的虚构造函数没有意义也不能实现。
但析构函数却是可以为虚函数的,且大多时候都声明为虚析构函数。
这样就可以在用基类的指针指向派生类的对象在释放时,可以根据实际所指向的对象类型动态联编调用子类的析构函数,实现正确的对象内存释放。
#### 抽象类、纯虚函数 (接口)
纯虚函数,就是没有函数体的虚函数。什么叫没有函数体?就是这样定义的函数:
```
virtual 返回值 函数名(形参)=0;
```
可以看到,前面virtual与虚函数定义一样,后面加了一个=0。表示没有函数体,这就是一个纯虚函数。
包含纯虚函数的类就是抽象类,一个抽象类至少有一个纯虚函数。
抽象类的存在是为了提供一个高度抽象、对外统一的接口,然后通过多态的特性使用各自的不同方法,是C++面向对象设计以及软件工程的核心思想之一。
抽象类的特点总结如下:
1. 抽象类无法实例出一个对象来,只能作为基类让派生类完善其中的纯虚函数,然后再实例化使用。
2. 抽象类的派生来依然可以不完善基类中的纯虚函数,继续作为抽象类被派生。直到给出所有纯虚函数的定义,则成为一个具体类,才可以实例化对象。
3. 抽象类因为抽象、无法具化,所以不能作为参数类型、返回值、强转类型。
4. 接着第三条,但抽象类可以定义一个指针、引用类型,指向其派生类,来实现多态特性。
## 内存分配、实例化
对象、结构体的实例化方式:
Expand Down Expand Up @@ -246,7 +415,43 @@ int main(int argc, char *argv[]) {
## 异常处理
TODO
### 示例
```cpp
#include <iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
try
{
if(b==0)
throw "error! b<0";
}
catch(const char *str)
{
cout<<str<<endl;
}
catch(int)
{
cout<<"throw int "<<endl;
}
return 0;
}
```

### exception 类

![cpp-20241021001535-179756.png](./image/cpp-20241021001535-179756.png)

使用C++自带的标准异常类,需要包含对应的头文件:

- exception、bad_exception类在头文件exception中定义,
- bad_alloc类在头文件new中定义,
- bad_typeid类在头文件typeinfo中定义,
- ios_base::failure类在头文件ios中定义,
- 其他异常类在stdexcept中定义。

# c++ 基础

Expand Down Expand Up @@ -1019,18 +1224,19 @@ ClassName& operator=(ClassName&&); // 移动赋值运算符
# 参考资料
- [ ] 《Essential c++》
- [ ] 《现代C++教程》
- [ ] [CMake、CMakeLists.txt、GCC、Clang、LLVM、MinGW、交叉编译](https://zhupite.com/program/CMake-GCC-Clang-LLVM-MinGW-CrossCompile.html)
- [ ] [gcc与clang对比](https://blog.csdn.net/sinat_36629696/article/details/79979274)
- [ ] gcc,LLVM,clang,VC 关系:
- 《Essential c++》
- 《现代C++教程》
- [CMake、CMakeLists.txt、GCC、Clang、LLVM、MinGW、交叉编译](https://zhupite.com/program/CMake-GCC-Clang-LLVM-MinGW-CrossCompile.html)
- [gcc与clang对比](https://blog.csdn.net/sinat_36629696/article/details/79979274)
- gcc,LLVM,clang,VC 关系:
- [Clang、LLVM与GCC介绍](https://blog.csdn.net/weichuang_1/article/details/48632321)
- [LLVM,Clang,GCC](https://blog.csdn.net/ShockYu/article/details/102793708)
- [VC, GCC, Clang/LLVM区别 ](https://www.cnblogs.com/xuesu/p/14542821.html)
- [ ] [cstdio和stdio.h的区别](https://blog.csdn.net/Abigial_/article/details/54799629)
- [ ] [Google的C++开源代码项目以及经典C++库](https://www.cnblogs.com/zhoug2020/p/5812578.html)
- [cstdio和stdio.h的区别](https://blog.csdn.net/Abigial_/article/details/54799629)
- [Google的C++开源代码项目以及经典C++库](https://www.cnblogs.com/zhoug2020/p/5812578.html)
- [C/C++中关于静态链接库(.a)、动态链接库(.so)的编译与使用](https://blog.csdn.net/qq_27825451/article/details/105700361)
- [从C到C++](https://nu-ll.github.io/2020/06/23/%E4%BB%8EC%E5%88%B0C++/)
- [C++入门教程](https://www.dotcpp.com/course/cpp/)
- [gnu libstdc++ doc](https://gcc.gnu.org/)
- [重述《Effective C++》](https://normaluhr.github.io/2020/12/31/Effective-C++/)
Binary file added C/image/cpp-20241021001535-179756.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4b5bbda

Please sign in to comment.