入口函数
在 C++ 中,程序的入口函数是 main
函数。所有 C++ 程序都从 main
函数开始执行。main
函数有几种不同的签名,最常见的有两种:不带参数和带参数的形式。
1. 无参数的 main
函数
这种形式的 main
函数没有参数,是最简单的形式。
2. 带参数的 main
函数
这种形式的 main
函数接受两个参数:argc
和 argv
,用于处理命令行参数。
int argc
: 表示命令行参数的数量。
char* argv[]
: 是一个数组,包含命令行参数的字符串。
3. main
函数返回值
main
函数的返回值类型是 int
,通常返回 0 表示程序正常结束,返回其他值表示程序出现错误。
return 0;
: 表示程序成功执行。
return 1;
: 通常表示程序执行过程中遇到错误。
4. main
函数的命令行参数
命令行参数允许用户在运行程序时传递参数,main
函数可以通过 argc
和 argv
访问这些参数。
示例
假设有一个程序 example.cpp
:
编译并运行:
C++ 中定义变量
在 C++ 中,定义变量的基本格式如下:
基本变量类型定义
- 整数类型:
int
: 通常用于整数。
short
: 短整型。
long
: 长整型。
long long
: 超长整型。
- 浮点类型:
float
: 单精度浮点数。
double
: 双精度浮点数。
long double
: 长双精度浮点数。
- 字符类型:
char
: 字符类型,用于存储单个字符。
wchar_t
: 宽字符类型,用于存储宽字符。
- 布尔类型:
常量定义
使用 const
关键字定义常量:
枚举类型
使用 enum
定义枚举类型:
指针变量
指针是存储内存地址的变量:
引用变量
引用是另一个变量的别名:
数组变量
数组用于存储相同类型的元素集合:
字符串
在 C++ 中,字符串可以使用字符数组或 std::string
定义:
变量初始化
1. 默认初始化 (Default Initialization)
对于局部变量,如果没有显式初始化,局部变量的默认值是不确定的,即它们会包含垃圾值。对于全局变量和静态变量,默认初始化会将它们设置为零。
2. 拷贝初始化 (Copy Initialization)
使用赋值符号 =
进行初始化。这种方式创建一个临时对象,然后将其拷贝给变量。
3. 直接初始化 (Direct Initialization)
直接调用构造函数进行初始化。与拷贝初始化不同,这种方式不会创建临时对象。
4. 列表初始化 (List Initialization)
C++11 引入了列表初始化,可以使用大括号 {}
来初始化变量。列表初始化分为两种:统一初始化和聚合初始化。
这种方式适用于所有变量,包括内置类型和用户自定义类型。
聚合初始化 (Aggregate Initialization)
用于初始化聚合类型,如数组和结构体。
5. 值初始化 (Value Initialization)
C++ 中的值初始化会将变量初始化为其类型的默认值(零或空)。这种方式通常用于类的默认构造函数调用。
6. 零初始化 (Zero Initialization)
零初始化会将变量设置为零。这通常在静态或全局范围内的变量以及在 new 操作符中使用。
7. 延迟初始化 (Lazy Initialization)
延迟初始化是在第一次使用变量时进行初始化。这通常用于提高程序的性能和资源管理。
示例
综合示例展示了不同类型的初始化:
范围内变量和全局变量
局部变量
局部变量在函数或块内部定义,只在该范围内有效:
全局变量
全局变量在所有函数外部定义,在整个程序中有效:
类型转换
在C++中,类型转换(Type Conversion)是将一种数据类型的值转换为另一种数据类型的值的过程。类型转换可以分为隐式转换(implicit conversion)和显式转换(explicit conversion)。
隐式转换
隐式转换由编译器自动完成,不需要显式指定。常见的隐式转换包括:
- 从较小范围的数据类型转换为较大范围的数据类型。例如,
int
可以隐式转换为 double
。
- 从
char
转换为 int
。
显式转换
显式转换需要程序员明确指定。常用的显式转换方式有:
- C风格的强制类型转换
- C++风格的强制类型转换
1. C风格的强制类型转换
2. C++风格的强制类型转换
C++提供了四种专门的类型转换运算符,分别是:
static_cast
dynamic_cast
const_cast
reinterpret_cast
static_cast
static_cast
用于在相关类型之间进行转换,如基本数据类型之间的转换和类层次结构中的上行转换(子类到父类的转换)。
dynamic_cast
dynamic_cast
主要用于处理类层次结构中的下行转换(父类到子类的转换)。它要求基类必须是多态类型(即包含虚函数)。
const_cast
const_cast
用于增加或移除变量的 const
属性。
reinterpret_cast
reinterpret_cast
用于在不同类型的指针或引用之间进行转换,几乎不做任何类型检查。
示例
以下是一个综合使用上述类型转换的示例:
字符串格式化
在C++中,字符串格式化有几种常见的方法,以下是每种方法的示例:
1. 使用 sprintf
这是C语言中常用的字符串格式化函数,C++也可以使用。
2. 使用 std::stringstream
这是C++中更常用的方法,利用字符串流来进行格式化。
从C++20开始,标准库中引入了 std::format
,类似于Python的 str.format
。
如果使用Boost库,可以使用 boost::format
进行字符串格式化。
5. 使用 std::snprintf
类似于 sprintf
,但更加安全,因为可以指定缓冲区的大小。
条件判断语句
在 C++ 中,判断语句用于根据条件的真或假来执行不同的代码块。常用的判断语句有 if
语句、else if
语句、else
语句和 switch
语句。
1. if
语句
if
语句用于在条件为真时执行某个代码块。
2. else if
语句
else if
语句用于在前面的 if
或 else if
条件为假时,测试另一个条件。
3. else
语句
else
语句用于在所有前面的 if
和 else if
条件都为假时执行一个代码块。
分支选择
带初始化的if
constexpr if
4. switch
语句
switch
语句用于当有多个条件分支时,简化多重 if-else if-else
结构。它可以对一个变量的多个可能值进行判断,并执行对应的代码块。
注意事项
-
大括号: if
, else if
, else
语句中的代码块应使用 {}
包围,虽然对于单行代码可以省略,但为了代码的可读性和维护性,推荐始终使用 {}
。
-
switch
语句中的 break
: 每个 case
分支末尾应使用 break
语句来防止程序继续执行下一个 case
代码块。default
分支是可选的,但它能处理所有未列出的 case
情况,提供更健壮的程序行为。
综合示例
以下示例展示了判断语句的使用:
5. for循环
6. while循环
7. do-while循环
8. range-for循环
在 C++ 中,要使一个类能够与范围 for 循环(range-based for loop)一起使用,类必须实现以下两种方法之一:
- 提供 begin() 和 end() 成员函数,这些函数返回迭代器或指针。
- 提供自定义的迭代器类,该类实现 operator*, operator!= 和 operator++ 操作符。
9. 循环控制语句
break
:立即终止循环。
continue
:跳过当前迭代,继续下一次迭代。
goto
:跳转到标记的代码位置(不推荐使用)。
break
示例
continue
示例
goto
示例
函数
在 C++ 中,函数是执行特定任务的自包含代码块。函数的定义包括返回类型、函数名、参数列表和函数体。函数的声明告诉编译器函数的名称、返回类型和参数类型,定义则提供了函数的具体实现。
1. 函数声明
函数声明(原型)通常出现在头文件中或函数定义之前,告诉编译器函数的名称和参数类型。
示例
2. 函数定义
函数定义提供了函数的具体实现。
示例
3. 函数调用
定义函数后,可以在程序中调用它。
4. 函数参数
函数可以接受任意数量的参数。参数可以是值传递、引用传递或指针传递。
值传递
参数的值传递,函数内部修改参数不会影响实参。
引用传递
参数的引用传递,函数内部修改参数会影响实参。
指针传递
参数的指针传递,函数内部可以修改指针所指向的变量。
5. 函数返回值
函数可以返回一个值,也可以返回多个值(通过引用参数或返回结构体)。
返回单个值
返回多个值(通过引用)
返回结构体
6. 函数重载
C++ 支持函数重载,即可以定义多个同名函数,但参数列表必须不同。
7. 内联函数
内联函数使用 inline
关键字定义,建议编译器将函数体插入到每个调用该函数的地方,减少函数调用的开销。
8. 默认参数
函数参数可以有默认值,如果调用时未提供参数,则使用默认值。
9. 变参函数
在 C++ 中,可变参数函数允许函数接受可变数量的参数。主要有两种实现方式:一种是使用 C 风格的 ...
语法(也称为 variadic functions),另一种是使用 C++11 引入的 std::initializer_list
或模板参数包(template parameter pack)。以下是对这两种方法的详细介绍和示例。
1. 使用 C 风格的可变参数函数
这种方法使用 stdarg.h
头文件中的宏来处理可变参数。这种方法适用于需要向后兼容 C 代码的情况。
语法
示例
2. 使用 std::initializer_list
这种方法使用 C++11 引入的 std::initializer_list
来处理可变参数。它提供了类型安全并且易于使用。
语法
示例
3. 使用模板参数包
模板参数包是 C++11 引入的一种更强大的处理可变参数的方法,它适用于需要处理不同类型的可变参数。
语法
示例
综合示例
以下是一个综合示例,展示了这三种方法的使用:
10. 示例程序
以下是一个综合示例,展示了上述概念的应用:
错误处理
C++ 提供了多种错误处理机制,以确保程序能够正确处理异常情况和错误。主要的错误处理机制包括异常(exceptions)、错误代码(error codes)以及断言(assertions)。
1. 异常处理
异常处理是 C++ 中一种常见的错误处理机制,允许程序在运行时捕获和处理错误。异常处理使用 try
、catch
和 throw
关键字。
1.1 基本异常处理
1.2 自定义异常
可以定义自己的异常类型,以提供更具体的错误信息。
2. 错误代码
使用错误代码是一种更传统的错误处理方式,通常用于返回值来指示操作是否成功。
2.1 使用返回值指示错误
2.2 使用 std::optional
返回可选值
在 C++17 中,可以使用 std::optional
来返回一个可选值,以指示操作是否成功。
3. 断言
断言用于在开发过程中捕获程序中的逻辑错误。断言在调试版本中有效,但在发布版本中通常被禁用。
示例:使用 assert
4. 标准库中的错误处理类
4.1 std::error_code
和 std::error_condition
C++11 引入了 std::error_code
和 std::error_condition
,用于表示和处理错误代码。这些类通常与标准库中的 I/O 库一起使用。
示例:使用 std::error_code
4.2 std::exception_ptr
和 std::rethrow_exception
C++11 引入了 std::exception_ptr
和 std::rethrow_exception
,用于捕获和重新抛出异常,通常用于异步编程和多线程编程。
示例:使用 std::exception_ptr