### 类基础概述
1. **自定义类型**:类是用户自定义的类型,由数据成员和成员函数组成。
2. **成员访问**:使用 `.` 访问对象成员,使用 `->` 访问指针成员。
3. **运算符重载**:可以为类定义运算符。
4. **名字空间**:类是一个包含其成员的名字空间。
5. **访问控制**:`public` 成员提供接口,`private` 成员提供实现细节。
6. **结构体与类**:`struct` 默认成员为 `public`,`class` 默认成员为 `private`。
### 默认拷贝
- 默认情况下,类对象可以通过拷贝初始化。
- 拷贝是通过逐个成员进行的。
### class 和 struct
- `struct` 默认成员为 `public`。
- `class` 默认成员为 `private`。
- 推荐将数据成员放在类的最后,以强调公共接口。
### 构造函数
- 构造函数用于初始化对象。
- 可以提供默认参数。
- `explicit` 关键字防止隐式转换。
### 类内初始化器
- 允许在成员声明时直接初始化成员变量,减少重复代码。
### 可变性
- **常量成员函数**:使用 `const` 修饰成员函数,表示该函数不会修改对象状态。
分离编译是一种将程序分解为多个独立部分的方法,有助于代码的模块化和重用。在C++中,每个源文件通常包含一个或多个逻辑组件,这些组件在文件系统中的组织方式构成了物理结构。编译器处理源文件时,首先进行预处理,生成编译单元,这是编译器实际处理的内容。
链接器负责将分离编译的多个部分绑定在一起,确保程序的声明在整个程序中保持一致。链接可以在程序运行前完成,也可以在运行中动态添加新代码。
在C++中,函数名、类名等必须在所有编译单元中保持一致,除非显式声明为局部名字。全局变量应尽量避免使用,因为它们可能导致维护问题和多线程数据竞争。如果必须使用全局变量,可以通过无名名字空间或`static`关键字限制其作用范围。
头文件用于包含接口信息,确保不同编译单元的声明一致性。头文件可以包含类型定义、模板声明、函数声明等,但不能包含普通函数定义和数据定义。通过合理使用头文件,可以有效管理代码的接口和实现分离。
C++ 通过名字空间、函数、类和源码组织实现模块化。名字空间用于避免名字冲突,形成具名作用域。成员可通过显式限定、`using` 声明或指示引用。全局作用域可用 `::` 显式引用。`using` 声明简化频繁使用的名字空间成员,应用于所有重载版本。`using` 指示允许不加限定使用整个名字空间,但需谨慎避免冲突。参数依赖查找(ADL)根据参数类型自动查找函数定义,适用于运算符重载和模板函数,减少显式限定并避免名字空间污染。
异常处理是C++中用于处理错误的关键机制,通过抛出和捕获异常来传递错误信息。异常处理改进了传统的错误处理方法,如终止程序、返回错误值、设置错误状态和调用错误处理函数,提供了更高效、精细且规范的错误处理方式。异常可以携带关于错误的描述信息,帮助程序员更好地理解和处理错误。
异常安全保障确保程序在抛出异常后仍处于有效状态,并释放所有已申请的资源。资源管理推荐使用RAII(资源获取即初始化)技术,通过构造函数和析构函数自动管理资源的生命周期。
C++不支持`finally`块,但可以通过RAII实现类似的效果,确保无论是否发生异常,资源都能被正确释放。
函数声明是C++中定义函数的方式,包括函数名、返回类型和参数列表。它允许在程序的其他部分调用该函数,即使函数的定义在声明之后。函数声明可以包含多种修饰符和限定符,如`inline`、`constexpr`、`noexcept`等,以及成员函数特有的修饰符如`virtual`、`override`、`final`等。函数定义则是提供函数体具体实现的特殊声明,必须与所有声明保持类型一致。C++会自动忽略参数类型的顶层`const`,以兼容C语言。参数名称不属于函数类型的一部分,不同的声明中参数名称可以不同。
`{}` 列表在C++中用于初始化命名变量,并可作为表达式使用。它们有两种形式:限定类型 `T{......}` 和未限定类型 `{......}`。未限定类型的 `{}` 列表根据上下文确定其类型。
实现模型方面,`{}` 列表的语义与函数调用类似,通过构造函数或聚合初始化来创建对象。例如,`vector<int>{1, 2, 3}` 会调用 `vector<int>` 的构造函数。
`{}` 列表的优势在于提供了更一致和安全的初始化方式,避免了窄化和隐式转换的问题。例如,`int x{7.8};` 会导致编译错误,因为 `7.8` 不能窄化为 `int`。
此外,`{}` 列表可以用于初始化数组、结构体和类对象,支持嵌套和多维初始化。例如,`vector<vector<int>> v{{1, 2}, {3, 4}};` 初始化了一个二维向量。
总之,`{}` 列表提供了一种灵活且安全的初始化机制,广泛应用于C++编程中。
本文介绍了一个简单的桌面计算器程序,该程序支持四种标准算术操作(加、减、乘、除)以及变量定义。程序由四个主要部分组成:分析器、输入函数、符号表和驱动。分析器负责语法分析,输入函数处理输入和词法分析,符号表存储永久信息,驱动处理初始化、输出和错误。
计算器的语法规则包括表达式、项和初等项的定义。表达式可以包含加法和减法,项可以包含乘法和除法,初等项可以是数字、名称、赋值表达式或括号内的表达式。程序通过一个枚举类 `Kind` 来定义不同类型的标记(如运算符、数字、名称等),并使用 `TokenStream` 类来管理输入流和获取标记。
程序的核心逻辑是通过递归下降解析器实现的,分别处理表达式、项和初等项。输入函数 `TokenStream` 负责从输入流中读取字符并生成相应的标记。符号表使用 `unordered_map` 存储变量及其对应的值。
整个程序的设计遵循了编译器的基本结构,通过分层处理输入、解析语法、执行计算和输出结果,实现了基本的计算功能。
本文详细介绍了C++中的语句类型及其使用规则。主要内容包括:
1. **语句概述**:C++语句的形式化定义,涵盖声明、表达式、选择语句、循环语句等。特别强调了块(block)和复合语句(compound statement)的概念,以及名字作用域的规则。
2. **声明作为语句**:解释了为什么声明可以作为语句使用,目的是减少未初始化变量造成的错误,并提高代码的局部性。
3. **选择语句**:
- **if语句**:条件可以是表达式或声明,支持隐式转换为`bool`类型。逻辑运算符`&&`、`||`、`!`常用于条件中,具有短路求值特性。
- **switch语句**:在一组候选项(`case`标签)中进行选择,`case`标签必须是整型或枚举类型的常量表达式。强调每个分支应包含结束语句(如`break`),并讨论了`default`分支的使用场景。
4. **循环语句**:介绍了`while`、`do-while`、`for`循环的语法和使用规则,特别是`for`循环中的初始化语句和初始化声明的区别。
5. **异常处理**:简要提及了`try`语句块用于处理异常。
通过这些内容,文章全面解析了C++中各种语句的结构和使用方法,帮助读者更好地理解和编写C++程序。
本文介绍了C++中的结构(`struct`)、联合(`union`)、枚举(`enum`)和限定作用域的枚举类型(`enum class`)。
1. **结构(`struct`)**:
- 由任意类型的元素(成员)构成的序列。
- 成员在内存中按声明顺序分配空间,但可能存在“空洞”以满足对齐要求。
- 类型名字一旦出现即可使用,但对象声明需在类型定义完成后进行。
- 可以包含成员函数,尤其是构造函数。
2. **布局**:
- 成员按声明顺序存储,但可能因对齐要求而产生未使用的空间。
- 可以通过调整成员顺序减少空间浪费。
3. **名字**:
- 类型名字可提前使用,但需避免递归定义。
- 允许在同一作用域中分别声明同名的`struct`和非`struct`,但不推荐。
4. **结构与类**:
- `struct`是一种`class`,成员默认是`public`的。
- 可以包含成员函数,尤其是构造函数。
5. **联合(`union`)**:
- 同一时刻只保存一个元素的值。
6. **枚举(`enum`)**:
- 包含一组命名常量(枚举值)的类型。
7. **限定作用域的枚举类型(`enum class`)**:
- 枚举值位于枚举类型的作用域内,不存在向其他类型的隐式类型转换。
通过这些内容,读者可以了解如何在C++中定义和使用结构、联合和枚举类型,以及它们与类的关系。
本文主要讨论了C++中的指针和数组的相关概念和操作。首先,强调了`void*`指针不允许进行加减运算,因为无法确定其指向对象的大小。接着,解释了指针的数值运算依赖于所指向的对象类型,并说明了指针之间的减法只有在指向同一数组的元素时才有效。
在数组部分,文章指出C++不允许直接拷贝数组,并提醒不要尝试按值传递数组给函数,因为这实际上是传递指针,会影响原数组。此外,还介绍了多维数组的定义和使用,包括如何省略第一维的大小。
最后,文章简要介绍了原始字符串字面量的使用,展示了如何在字符串中包含双引号、反斜杠以及多行文本。