C++ 快速上手指南
第一部分:基础与项目结构
1. 项目组织结构
一个完整的C++项目通常包含以下文件类型和目录结构。
常见文件类型
| 后缀 | 类型 | 功能 |
|---|---|---|
.h / .hpp |
头文件 | 放置各种声明(类型、函数、类等),不建议放实现代码。 |
.c / .cpp |
源文件 | 放置头文件中声明的定义。通过 #include 关联头文件。 |
makefile |
构建配置 | 定义项目源码的编译过程。 |
CMakeLists.txt |
CMake配置 | 跨平台的编译配置,可生成makefile。 |
.dll / .so |
动态链接库 | 程序的外部功能支持库。 |
.o / .obj |
编译目标文件 | 源码直接编译生成的原始二进制文件。 |
.a / .lib |
静态链接库 | 由.o / .obj文件组合而成。 |
推荐项目目录结构
build/:编译目录(可包含debug/和release/子目录)。dist/:发布目录。doc/:文档目录。include/:公共头文件目录。lib/:外部依赖库目录。res/:资源目录。samples/:示例代码目录。src/:项目源代码目录。tools/:项目支持工具目录。- 根目录:
makefile或CMakeLists.txt。
程序入口点
- 每个C++应用有且仅有一个
main()函数。 - 标准形式:
int main(int argc, char *argv[])。argc:参数个数。argv:参数字符串数组。
2. 源码文件结构
C++源码分为声明和定义两部分。
- 声明:将名称(变量、函数、类等)引入程序,通常放在头文件(
.h)中。 - 定义:将声明的内容具体实现,通常放在源文件(
.cpp)中。 - 前向声明:为解决交叉引用问题,在定义前先声明类名,但只能用于定义指针、引用和函数形参。
代码组织顺序(推荐)
#include引用- 预处理指令
- 命名空间定义或引用
- 自定义类型声明
- 声明(类、函数等)
- 定义(类成员、函数等)
main()主函数
第二部分:核心语言特性
1. 预处理器指令
以 # 开头,在编译前执行。
#include
#include <文件名>:在标准库目录中搜索。#include "文件名":在指定文件目录(当前目录或相对路径)中搜索。
#define
- 对象宏:
#define 宏名 替代内容。例如:#define PI 3.14 - 函数宏:
#define 宏名(参数) (表达式)。例如:#define MAX(a,b) (a>b?a:b) - 特殊运算符:
#:将参数转换为字符串。例如:#define STR(x) #x,STR(123)->"123"##:连接两个参数。例如:#define CC(x,y) x##y,CC(a, b)->ab
- 取消定义:
#undef 宏名
条件编译
#ifdef/#ifndef:检查宏是否已定义。#if、#elif、#else、#endif头文件保护(防止重复包含):
1
2
3
4
// 类声明内容
2. 命名空间
- 定义:
namespace 命名空间名 { ... },用于避免命名冲突。 - 访问:使用
::作用域解析符,例如Galaxy::stars。 - 简化使用:
using 命名空间名::名称;:引入单个名称。using namespace 命名空间名;:引入整个命名空间。
3. 数据类型与变量
基本数据类型
| 类型 | 关键字 | 常用修饰符 |
|---|---|---|
| 布尔型 | bool |
true, false |
| 字符型 | char |
signed, unsigned |
| 整型 | int |
short, long, signed, unsigned |
| 浮点型 | float |
单精度 |
| 双精度浮点 | double |
双精度 |
| 无类型 | void |
- |
| 宽字符型 | wchar_t |
- |
变量
- 定义:
数据类型 变量名;,例如int a; - 初始化:
int a = 5;或int a(5); - 作用域:
- 局部变量:在函数或代码块内声明,需手动初始化。
- 全局变量:在函数外声明,系统自动初始化为默认值(0,
\0,nullptr等)。 - 形式参数:函数定义中声明的参数。
变量修饰符
static:保持变量生命周期或限制作用域。extern:声明在其他文件中定义的全局变量或函数。const:定义常量,值不可修改。volatile:通知编译器不要优化,直接从内存读取。mutable:允许被const成员函数修改。thread_local:每个线程拥有独立副本。
4. 复合数据类型
数组
- 定义:
类型 数组名[大小]; - 初始化:
int arr[3] = {1, 2, 3}; - 访问:
arr[索引],索引从0开始。 - 多维数组:
int arr2d[3][4];,视为“数组的数组”。
字符串
- C风格字符串:字符数组,以
\0结尾。char greeting[] = "Hello"; - C++ string类:需包含
<string>。std::string str = "Hello";
结构体 (struct)
- 用户自定义的聚合数据类型,成员默认是
public。 - 定义:
struct 结构名 { 成员类型 成员名; }; - 访问:
结构变量.成员或结构指针->成员。
联合体 (union)
- 所有成员共享同一内存,一次只能使用一个成员。
- 大小由最大成员决定。
枚举 (enum)
- 定义一组命名的整数常量。
- 定义:
enum 枚举名 { 常量1, 常量2, ... };。默认从0开始递增。 - C++11强类型枚举:
enum class 枚举名 { ... };,防止隐式转换和命名污染。
类型别名 (typedef)
- 定义:
typedef 原类型 别名; - C++11 using别名:
using 别名 = 原类型;,更适用于模板。
5. 控制结构
分支语句
if/else if/elseswitch/case/default:注意break的使用,否则会“穿透”。- 三元运算符:
条件 ? 表达式1 : 表达式2
循环语句
for循环:for (初始化; 条件; 步进) { ... }while循环:while (条件) { ... }do-while循环:do { ... } while (条件);,至少执行一次。- 循环控制:
break:立即退出当前循环。continue:跳过本次循环剩余部分,进入下一次迭代。
6. 函数
函数定义
1 | 返回值类型 函数名(参数列表) { |
函数重载
函数名相同,但参数列表(数量或类型)不同。
参数传递
- 按值传递:传递副本,不修改原值。
- 按指针传递:传递地址,可修改原值。
void func(int *p) - 按引用传递:传递别名,可修改原值。
void func(int &ref)
参数默认值
在声明时指定:int func(int a, int b = 10);
Lambda表达式 (C++11)
- 定义:
[捕获列表](参数列表) -> 返回值类型 { 函数体 } - 捕获方式:
[]:不捕获任何外部变量。[=]:以值捕获所有外部变量。[&]:以引用捕获所有外部变量。[a, &b]:a值捕获,b引用捕获。
7. 指针与引用
指针
- 定义:
类型 *指针名;,例如int *p; - 操作符:
&:取地址符,p = &a;*:解引用符,*p = 10;
- 指针与数组:数组名是常量指针,指向数组首元素。
arr[i]等价于*(arr + i) - 空指针:
nullptr(C++11),NULL或0(旧式)。 - 函数指针:
返回值类型 (*函数指针名)(参数列表);
引用
- 定义:
类型 &引用名 = 原变量;,是原变量的别名。 - 特性:
- 必须在声明时初始化。
- 一旦绑定,不可更改引用的对象。
- 不存在空引用。
- 常用场景:函数参数(避免拷贝,可修改原值)和返回值(返回对象本身)。
8. 动态内存管理
- 分配:
类型 *指针 = new 类型;(单个对象)类型 *数组 = new 类型[大小];(对象数组)
- 释放:
delete 指针;(单个对象)delete[] 数组指针;(对象数组)
- 规则:
- 不要重复释放同一内存。
delete空指针是安全的。- 务必匹配
new和delete,new[]和delete[]。
- C风格:
malloc()和free(),但不会调用构造函数/析构函数。
第三部分:面向对象编程
1. 类 (class)
- 声明:
class 类名 { private: ... public: ... };成员默认是private。 - 成员函数定义:
返回值类型 类名::函数名(参数) { ... } - this指针:指向当前对象实例的指针。
2. 构造函数与析构函数
- 构造函数:
- 与类同名,无返回值,用于初始化对象。
- 可以重载。
- 初始化列表:
类名(参数列表): 成员1(值1), 成员2(值2) { ... }
- 析构函数:
- 与类同名,前加
~,无返回值,无参数。 - 对象销毁时自动调用,用于清理资源。
- 与类同名,前加
- 默认构造函数:
类名() = default;(C++11) - 委托构造函数 (C++11):构造函数调用另一个构造函数。
3. 继承
- 格式:
class 子类名 : 访问控制符 基类名 { ... } - 访问控制符:
public:保持基类成员的原有访问级别。protected:基类的public和protected成员变为protected。private:基类所有成员变为private。
- 构造函数:子类构造函数通过初始化列表调用基类构造函数。
Child(参数): Base(参数) { ... }
4. 多态
- 虚函数:基类中使用
virtual关键字声明,子类可重写,实现动态绑定。 - 纯虚函数:
virtual void func() = 0;,使类成为抽象类,不可实例化。 - override (C++11):显式声明重写基类的虚函数。
- final (C++11):阻止类被继承或虚函数被重写。
5. 运算符重载
- 格式:
返回值类型 operator运算符(参数列表) - 可以重载为成员函数或友元函数。
6. 友元
- 使用
friend关键字,允许外部函数或类访问其私有成员。 - 类型:友元函数、友元类、友元成员函数。
7. 异常处理
抛出:
throw 异常对象;捕获:
1
2
3
4
5
6
7try {
// 可能抛出异常的代码
} catch (异常类型1 &e) {
// 处理
} catch (...) {
// 处理所有其他异常
}异常规范:
noexcept(C++11):函数不会抛出异常。throw(...):旧式规范,已弃用。
8. const 成员函数
- 定义:在成员函数声明末尾加
const,表示该函数不会修改对象的数据成员。 - 只能调用其他
const成员函数,不能修改非mutable成员。
9. 模板 (泛型)
函数模板:
1
2template <typename T>
T max(T a, T b) { return a > b ? a : b; }类模板:
1
2template <typename T>
class Stack { ... };非类型模板形参:
template <typename T, size_t N> void func(T (&arr)[N]);
第四部分:标准库与构建
1. 输入输出 (IO)
- 头文件:
<iostream>、<iomanip>、<fstream> - 标准流:
cout:标准输出,配合<<。cin:标准输入,配合>>。cerr/clog:标准错误输出。
- 格式控制:使用
<iomanip>中的setw(),setprecision()等。
2. 文件操作
头文件:
<fstream>类:
ifstream(读)、ofstream(写)、fstream(读写)。打开模式:
ios::in、ios::out、ios::app、ios::trunc等。示例:
1
2
3ofstream file("test.txt");
file << "Hello" << endl;
file.close();
3. 常用容器 (STL)
| 容器 | 头文件 | 特点 |
|---|---|---|
vector |
<vector> |
动态数组,快速随机访问。 |
list |
<list> |
双向链表,快速插入/删除。 |
deque |
<deque> |
双端队列。 |
stack |
<stack> |
栈,LIFO。 |
queue |
<queue> |
队列,FIFO。 |
map |
<map> |
有序键值对。 |
unordered_map |
<unordered_map> |
哈希表,无序键值对。 |
4. 智能指针 (C++11)
std::unique_ptr:独占所有权的智能指针。std::shared_ptr:共享所有权的智能指针,通过引用计数管理。std::weak_ptr:配合shared_ptr使用,解决循环引用问题。
5. 编译与构建
CMake 基础
最小配置:
1
2
3cmake_minimum_required(VERSION 3.0)
project(MyProject)
add_executable(myapp main.cpp)常用命令:
set(CMAKE_CXX_STANDARD 11):设置C++标准。target_include_directories:指定头文件目录。target_link_libraries:链接库。
编译器与链接库
- 静态库 (.a/.lib):编译时被链接进可执行文件。
- 动态库 (.so/.dll):运行时被加载,可多程序共享。
- g++ 常用参数:
-c:只编译,不链接。-o:指定输出文件名。-I:指定头文件搜索路径。-L:指定库文件搜索路径。-l:链接指定的库。-fPIC:生成位置无关代码(用于动态库)。-shared:生成动态链接库。
第五部分:现代C++特性 (C++11/14)
C++11 重要新特性
右值引用 (
&&) 与移动语义- 延长临时对象的生命周期,实现资源转移,避免深拷贝。
std::move():将左值转换为右值引用。
统一初始化
- 使用大括号
{}进行初始化,适用于所有类型。
1
2vector<int> v{1, 2, 3};
int arr[]{1, 2, 3};- 使用大括号
自动类型推导
auto:编译器自动推导变量类型。decltype:获取表达式或变量的类型。
基于范围的
for循环1
2
3for (auto &elem : container) {
// 遍历容器
}空指针
nullptr- 替代
NULL,避免与整数0混淆。
- 替代
强类型枚举
enum class- 作用域限制,防止隐式转换。
静态断言
static_assert- 编译期断言,若条件为假则编译失败。
override与finaloverride:显式表明重写基类虚函数。final:阻止类被继承或虚函数被重写。
Lambda 表达式
- 创建匿名函数对象。
=default与=delete=default:显式要求编译器生成默认的特殊成员函数。=delete:禁止编译器生成特殊成员函数。
C++14 新特性
泛型Lambda
- Lambda 参数可使用
auto,成为模板。
1
auto add = [](auto a, auto b) { return a + b; };
- Lambda 参数可使用
变量模板
- 模板化的变量定义。