系统版本:v0.0.7 最后更新:2026-04-10 内容范围:项目结构、编译构建、头文件、命名空间、基础语法、函数、数组与指针、面向对象、成员函数定义、内存管理、模板、STL、异常处理
一、 项目结构与编译构建 1. 完整 C++ 项目目录结构 一个规范的 C++ 项目通常采用以下目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 MyProject/ ├── CMakeLists.txt # 顶层 CMake 配置文件 ├── README.md # 项目说明文档 ├── LICENSE # 开源许可证 ├── .gitignore # Git 忽略规则 │ ├── src/ # 源代码目录 │ ├── main.cpp # 程序入口 │ ├── utils.cpp # 工具函数实现 │ └── core/ # 核心模块(可按功能划分子目录) │ ├── engine.cpp │ └── renderer.cpp │ ├── include/ # 头文件目录(对外公开接口) │ ├── utils.h │ └── core/ │ ├── engine.h │ └── renderer.h │ ├── tests/ # 测试代码目录 │ ├── CMakeLists.txt # 测试专用的 CMake 配置 │ ├── test_utils.cpp │ └── test_engine.cpp │ ├── cmake/ # 自定义 CMake 模块(可选) │ └── FindSomeLib.cmake │ ├── third_party/ # 第三方库(可选,也可用 FetchContent) │ └── nlohmann_json/ │ ├── docs/ # 文档目录(可选) │ └── api.md │ ├── scripts/ # 辅助脚本(可选) │ ├── build.sh │ └── run_tests.sh │ └── build/ # 编译输出目录(由 CMake 生成,应加入 .gitignore) ├── CMakeCache.txt ├── Makefile └── MyProject # 最终生成的可执行文件
各目录说明:
目录/文件
说明
src/
存放 .cpp 源文件,按功能模块可划分子目录
include/
存放 .h / .hpp 头文件,与 src/ 结构对应
tests/
单元测试和集成测试代码
cmake/
自定义 CMake 查找脚本(如 FindXXX.cmake)
third_party/
直接嵌入的第三方依赖库
docs/
项目文档、API 说明等
scripts/
构建、部署、测试等辅助脚本
build/
CMake 输出目录,不应提交到 Git
最佳实践:
src/ 和 include/ 分离,保持接口与实现解耦
build/ 目录永远不要提交到版本控制
大型项目推荐按模块划分子目录,而非把所有文件堆在 src/ 下
2. 使用 g++ 直接编译 适合单文件或小型项目:
1 2 3 4 5 6 7 8 g++ main.cpp -o main g++ main.cpp utils.cpp -o main g++ -Wall -Wextra -std=c++17 -O2 main.cpp -o main
3. 使用 CMake 构建 CMake 的作用是跨平台地生成 Makefile 或工程文件 ,简化 C++ 项目的编译过程。
基本构建流程:
1 2 3 mkdir build && cd buildcmake .. make
CMakeLists.txt 逐行详解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 cmake_minimum_required (VERSION 3.5 )project (HelloWorld)set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -std=c++14" )set (source_dir "${PROJECT_SOURCE_DIR}/src/" )file (GLOB source_files "${source_dir}/*.cpp" )add_executable (HelloWorld ${source_files} )
更推荐的现代 CMake 写法(C++17):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 cmake_minimum_required (VERSION 3.14 )project (MyProject LANGUAGES CXX)set (CMAKE_CXX_STANDARD 17 )set (CMAKE_CXX_STANDARD_REQUIRED ON )include_directories (${PROJECT_SOURCE_DIR} /include )add_executable (${PROJECT_NAME} src/main.cpp src/utils.cpp ) enable_testing ()add_subdirectory (tests)
4. 头文件与 #pragma once 4.1 头文件的作用 头文件(.h / .hpp)是 C++ 中声明与实现分离 的核心机制:
文件类型
内容
示例
.h / .hpp
声明 :函数原型、类定义、宏、常量
int add(int a, int b);
.cpp
实现 :函数的具体逻辑、类的成员函数定义
int add(int a, int b) { return a + b; }
为什么需要头文件?
多个 .cpp 文件需要共享同一组声明时,只需 #include 一次头文件
修改实现(.cpp)时,不影响其他文件的编译
隐藏实现细节,只暴露接口
4.2 #include 的两种形式 1 2 #include <iostream> #include "myheader.h"
4.3 头文件重复包含的问题 当多个头文件互相引用时,容易出现重复包含 :
1 2 3 4 a.h 包含了 common.h b.h 也包含了 common.h main.cpp 同时 #include "a.h" 和 #include "b.h" → common.h 被包含了两次 → 编译报错:重复定义
4.4 #pragma once 的作用 #pragma once 是编译器指令,确保同一个头文件在一次编译过程中只被包含一次 :
1 2 3 4 5 6 #pragma once #include <string> std::string greet (const std::string& name) ;
工作原理: 编译器首次遇到 #pragma once 时,会记录该文件的唯一标识(通常是文件路径或 inode)。后续再次 #include 时,直接跳过,不再读取文件内容。
4.5 #pragma once vs #ifndef 守卫 传统的替代方案是使用头文件守卫(Include Guard) :
1 2 3 4 5 6 7 8 9 #ifndef UTILS_H #define UTILS_H #include <string> std::string greet (const std::string& name) ;#endif
对比项
#pragma once
#ifndef 守卫
标准性
非 C++ 标准(但所有主流编译器都支持)
C++ 标准,100% 可移植
写法
一行搞定,简洁
需要三行,宏名需手动维护
编译速度
更快(编译器直接跳过文件)
稍慢(仍需打开文件检查宏)
可靠性
依赖文件系统识别唯一性
绝对可靠
推荐度
✅ 现代项目首选
老旧项目或极端跨平台场景
最佳实践: 现代 C++ 项目统一使用 #pragma once,简洁高效。只有在需要支持极冷门编译器时才考虑 #ifndef 守卫。
二、 基础语法 1. 数据类型
类型
说明
典型大小
int
整型
4 字节
long long
长整型
8 字节
float
单精度浮点
4 字节
double
双精度浮点
8 字节
char
字符型
1 字节
bool
布尔型
1 字节
std::string
字符串(需 #include <string>)
动态
2. 变量声明与初始化 1 2 3 4 5 6 int a = 10 ; int b (10 ) ; int c{10 }; auto d = 10 ; const int e = 20 ; constexpr int f = 30 ;
3. 输入输出 1 2 3 4 5 6 7 8 9 #include <iostream> int main () { int age; std::cout << "Enter your age: " ; std::cin >> age; std::cout << "You are " << age << " years old." << std::endl; return 0 ; }
三、 命名空间 1. 为什么需要命名空间 当项目变大或引入多个第三方库时,很容易出现同名冲突 :
1 2 3 4 5 6 7 void log (const std::string& msg) ;void log (const std::string& msg) ;
命名空间(namespace)就是为了解决这个问题——给标识符加一个作用域前缀 ,避免名称冲突。
2. 基本用法 1 2 3 4 5 6 7 8 9 10 11 12 namespace math { double pi = 3.14159 ; int add (int a, int b) { return a + b; } } int result = math::add (3 , 4 );double val = math::pi;
3. using 声明与指令 1 2 3 4 5 6 7 8 9 10 11 using math::add;int r = add (1 , 2 ); using namespace math;double p = pi; int s = add (5 , 3 ); double x = math::pi;
最佳实践:
.cpp 文件中可以适度使用 using namespace xxx;
头文件中永远不要写 using namespace ,会污染所有包含该头文件的源文件
优先使用 using xxx::symbol; 或完整限定名
4. 嵌套命名空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 namespace company { namespace project { void init () ; } } namespace company::project { void init () ; } company::project::init ();
5. 匿名命名空间 匿名命名空间中的符号仅在当前编译单元(.cpp 文件)内可见 ,等价于 C 语言的 static 全局变量:
1 2 3 4 5 6 namespace { int helper () { return 42 ; } }
6. std 命名空间 C++ 标准库的所有内容都在 std 命名空间中:
1 2 3 std::cout << "Hello" << std::endl; std::vector<int > v; std::string s = "world" ;
#include <iostream> 后写 using namespace std; 是初学者常见做法,但在实际项目中不推荐 ,因为 std 包含大量符号,极易与自定义名称冲突。
四、 控制流 1. 条件语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 if (score >= 90 ) { std::cout << "A" << std::endl; } else if (score >= 60 ) { std::cout << "B" << std::endl; } else { std::cout << "C" << std::endl; } switch (choice) { case 1 : std::cout << "Option 1" << std::endl; break ; case 2 : std::cout << "Option 2" << std::endl; break ; default : std::cout << "Unknown" << std::endl; }
2. 循环语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 for (int i = 0 ; i < 10 ; ++i) { std::cout << i << " " ; } std::vector<int > nums = {1 , 2 , 3 , 4 , 5 }; for (const auto & num : nums) { std::cout << num << " " ; } int count = 0 ;while (count < 5 ) { std::cout << count << " " ; ++count; } do { std::cout << "At least once" << std::endl; } while (false );
四、 函数 1. 基本定义 1 2 3 4 5 6 7 8 9 int add (int a, int b) { return a + b; } void printHello () { std::cout << "Hello!" << std::endl; }
2. 参数传递方式 1 2 3 4 5 6 7 8 9 10 11 12 13 void byValue (int x) { x = 100 ; }void byReference (int & x) { x = 100 ; }void byConstRef (const std::string& str) { std::cout << str << std::endl; } void byPointer (int * x) { *x = 100 ; }
3. 默认参数与函数重载 1 2 3 4 5 6 7 8 void greet (const std::string& name, const std::string& greeting = "Hello" ) { std::cout << greeting << ", " << name << "!" << std::endl; } int add (int a, int b) { return a + b; }double add (double a, double b) { return a + b; }
4. Lambda 表达式(C++11) 1 2 3 4 5 6 auto sum = [](int a, int b) { return a + b; };std::cout << sum (3 , 4 ) << std::endl; int factor = 10 ;auto multiply = [factor](int x) { return x * factor; };
五、 数组与指针 1. 数组 1 2 3 4 5 6 7 int arr[5 ] = {1 , 2 , 3 , 4 , 5 };#include <array> std::array<int , 5> arr2 = {1 , 2 , 3 , 4 , 5 }; std::cout << arr2. size () << std::endl;
2. 指针基础 1 2 3 4 5 6 int value = 42 ;int * ptr = &value; std::cout << *ptr; int * empty = nullptr ;
3. 指针与数组 1 2 3 int arr[] = {10 , 20 , 30 };int * p = arr; std::cout << *(p + 1 );
七、 面向对象编程 面向对象编程(OOP)是 C++ 的核心范式之一,围绕封装、继承、多态 三大特性组织代码。
1. 类与对象 类是对象的蓝图,对象是类的实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person {public : Person (const std::string& name, int age) : name_ (name), age_ (age) {} void introduce () const { std::cout << "I'm " << name_ << ", " << age_ << " years old." << std::endl; } const std::string& getName () const { return name_; } int getAge () const { return age_; } private : std::string name_; int age_; }; Person p ("Alice" , 25 ) ;p.introduce ();
2. 访问控制
修饰符
类内
派生类
类外
public
✅
✅
✅
protected
✅
✅
❌
private
✅
❌
❌
设计原则: 成员变量默认用 private,通过 public 接口暴露行为。这就是封装 ——隐藏内部实现,只暴露必要的接口。
3. 成员函数的定义方式 成员函数是类中定义的函数,用于操作类的成员变量。C++ 提供了多种定义方式:
3.1 在类内部定义(内联函数) 直接在类声明内部定义函数,称为内联函数 。编译器可能会将函数体直接展开到调用点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Person {public : void setName (const std::string& name) { name_ = name; } std::string getName () const { return name_; } int getAge () const { return age_; } private : std::string name_; int age_; };
适用场景: 函数体简短(通常几行)、调用频繁的 getter/setter。
3.2 在类外部定义 将函数声明放在类内部,实现放在类外部 (通常在 .cpp 文件中):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Person {public : void setName (const std::string& name) ; std::string getName () const ; void introduce () const ; private : std::string name_; int age_; }; #include "Person.h" void Person::setName (const std::string& name) { name_ = name; } std::string Person::getName () const { return name_; } void Person::introduce () const { std::cout << "I'm " << name_ << ", " << age_ << " years old." << std::endl; }
适用场景: 函数体较长、复杂实现、需要隐藏细节。
3.3 常量成员函数(const) 用 const 修饰的成员函数承诺不会修改对象的任何成员变量 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Circle {public : Circle (double radius) : radius_ (radius) {} double getRadius () const { return radius_; } void setRadius (double r) { radius_ = r; } private : double radius_; }; const Circle c (5.0 ) ;double r = c.getRadius ();
关键规则:
只读访问的成员函数应该标记为 const
const 对象只能调用 const 成员函数
非 const 对象可以调用两者
3.4 静态成员函数 静态成员函数属于类本身,而非某个对象,不能访问 this 指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Config {public : Config () { ++instanceCount_; } static Config& getInstance () { static Config instance; return instance; } static int getInstanceCount () { return instanceCount_; } private : static int instanceCount_; std::string configPath_; }; int Config::instanceCount_ = 0 ;Config& cfg = Config::getInstance (); std::cout << Config::getInstanceCount () << std::endl;
关键规则:
静态成员函数只能访问静态成员变量
可以通过 类名::函数名() 调用,无需对象
3.5 特殊的成员函数 C++ 为类自动生成几个特殊的成员函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Buffer {public : Buffer () = default ; Buffer (size_t size) : data_ (new int [size]), size_ (size) {} Buffer (const Buffer& other) : data_ (new int [other.size_]), size_ (other.size_) { std::copy (other.data_, other.data_ + other.size_, data_); } Buffer& operator =(const Buffer& other) { if (this != &other) { delete [] data_; size_ = other.size_; data_ = new int [size_]; std::copy (other.data_, other.data_ + other.size_, data_); } return *this ; } Buffer (Buffer&& other) noexcept : data_ (other.data_), size_ (other.size_) { other.data_ = nullptr ; other.size_ = 0 ; } Buffer& operator =(Buffer&& other) noexcept { if (this != &other) { delete [] data_; data_ = other.data_; size_ = other.size_; other.data_ = nullptr ; other.size_ = 0 ; } return *this ; } ~Buffer () { delete [] data_; } private : int * data_; size_t size_; };
关键规则:
如果类管理资源(指针、文件句柄等),必须显式定义拷贝构造、拷贝赋值、析构函数
C++11 起,还需要考虑移动构造和移动赋值
使用 = default 让编译器生成默认实现
使用 = delete 禁用拷贝或移动
3.6 inline 关键字 可以用 inline 关键字显式请求内联 (只是建议,编译器可能忽略):
1 2 3 4 5 6 7 8 9 10 11 12 class Math {public : double add (double a, double b) { return a + b; } inline double multiply (double a, double b) ; }; inline double Math::multiply (double a, double b) { return a * b; }
最佳实践: 现代编译器会自动优化内联,无需过度关注。除非有特殊性能需求,否则让编译器自行决定。
3.7 成员函数重载与默认参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Logger {public : void log (const std::string& msg) { log (msg, Level::INFO); } void log (const std::string& msg, Level level) { } void setLevel (Level level = Level::DEBUG) { level_ = level; } private : enum class Level { DEBUG, INFO, WARNING, ERROR }; Level level_ = Level::INFO; };
3.8 const 重载与重写 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Data {public : const std::string& getName () const { return name_; } std::string& getName () { return name_; } private : std::string name_; }; Data d; d.getName () = "Alice" ; const Data cd;std::string n = cd.getName ();
4. 构造函数详解 构造函数负责对象的初始化。C++ 提供了多种构造函数形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Student {public : Student () : name_ ("Unknown" ), age_ (0 ), gpa_ (0.0 ) {} Student (const std::string& name, int age) : name_ (name), age_ (age), gpa_ (0.0 ) {} Student (const std::string& name, int age, double gpa) : name_ (name), age_ (age), gpa_ (gpa) {} Student (const std::string& name) : Student (name, 0 ) {} Student (const Student& other) : name_ (other.name_), age_ (other.age_), gpa_ (other.gpa_) {} Student (Student&& other) noexcept : name_ (std::move (other.name_)), age_ (other.age_), gpa_ (other.gpa_) {} private : std::string name_; int age_; double gpa_; };
关键规则:
成员初始化列表(: name_(name))优于在构造函数体内赋值,因为前者直接构造,后者先默认构造再赋值
如果类管理资源(如动态内存),必须实现拷贝构造 和拷贝赋值 ,或显式禁用它们
4. 析构函数 析构函数在对象生命周期结束时自动调用,用于清理资源。
1 2 3 4 5 6 7 8 9 10 11 12 class Buffer {public : Buffer (size_t size) : data_ (new int [size]), size_ (size) {} ~Buffer () { delete [] data_; } private : int * data_; size_t size_; };
关键规则: 基类有虚函数时,析构函数必须声明为 virtual,否则通过基类指针删除派生类对象时会只调用基类析构函数 ,导致派生类部分内存泄漏。
5. 静态成员 static 成员属于类本身,而非某个对象实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Counter {public : Counter () { ++count_; } ~Counter () { --count_; } static int getCount () { return count_; } private : static int count_; }; int Counter::count_ = 0 ;Counter a, b, c; std::cout << Counter::getCount () << std::endl;
6. 友元(friend) 友元允许外部函数或类访问私有成员,打破了封装 ,应谨慎使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Vector2D {public : Vector2D (double x, double y) : x_ (x), y_ (y) {} friend double dot (const Vector2D& a, const Vector2D& b) ; friend class VectorSerializer ; private : double x_, y_; }; double dot (const Vector2D& a, const Vector2D& b) { return a.x_ * b.x_ + a.y_ * b.y_; }
7. 运算符重载 让自定义类型支持 +、-、==、<< 等运算符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Complex {public : Complex (double real, double imag) : real_ (real), imag_ (imag) {} Complex operator +(const Complex& other) const { return {real_ + other.real_, imag_ + other.imag_}; } bool operator ==(const Complex& other) const { return real_ == other.real_ && imag_ == other.imag_; } friend std::ostream& operator <<(std::ostream& os, const Complex& c) { os << c.real_ << " + " << c.imag_ << "i" ; return os; } private : double real_, imag_; }; Complex a (1 , 2 ) , b (3 , 4 ) ;Complex c = a + b; std::cout << c << std::endl;
最佳实践:
对称运算符(+、-、==)推荐用友元函数 实现
赋值类运算符(+=、-=)推荐用成员函数 实现
<< 和 >> 必须是友元函数
8. 继承 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Animal {public : Animal (const std::string& name) : name_ (name) {} virtual void speak () const { std::cout << "..." << std::endl; } virtual ~Animal () = default ; protected : std::string name_; }; class Dog : public Animal {public : Dog (const std::string& name) : Animal (name) {} void speak () const override { std::cout << name_ << " says: Woof!" << std::endl; } }; class Cat : public Animal {public : Cat (const std::string& name) : Animal (name) {} void speak () const override { std::cout << name_ << " says: Meow!" << std::endl; } };
继承方式对比:
继承方式
public 成员变为
protected 成员变为
使用场景
public
public
protected
最常见,”是一个”关系
protected
protected
protected
较少用
private
private
private
“用…实现”关系
9. 多态详解 多态的核心是通过基类指针/引用调用派生类的实现 。
1 2 3 4 5 6 7 8 9 10 11 std::vector<std::unique_ptr<Animal>> animals; animals.push_back (std::make_unique <Dog>("Buddy" )); animals.push_back (std::make_unique <Cat>("Whiskers" )); for (const auto & animal : animals) { animal->speak (); }
多态的三个必要条件:
基类有 virtual 函数
通过基类指针或引用 调用
派生类用 override 重写(C++11 推荐,编译器会检查签名是否匹配)
override 与 final:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Base {public : virtual void foo () {} virtual void bar () {} }; class Derived : public Base {public : void foo () override {} void bar () final {} }; class MoreDerived : public Derived { };
10. 纯虚函数与抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Shape {public : virtual double area () const = 0 ; virtual ~Shape () = default ; }; class Circle : public Shape {public : Circle (double r) : radius_ (r) {} double area () const override { return 3.14159 * radius_ * radius_; } private : double radius_; }; class Rectangle : public Shape {public : Rectangle (double w, double h) : width_ (w), height_ (h) {} double area () const override { return width_ * height_; } private : double width_, height_; };
关键规则:
含有纯虚函数的类是抽象类 ,不能实例化
派生类必须实现所有纯虚函数,否则它也是抽象类
纯虚函数可以有默认实现,但派生类仍需显式声明
11. 组合 vs 继承 继承 表达”是一个”(is-a)关系,组合 表达”有一个”(has-a)关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Engine {public : void start () { std::cout << "Engine started" << std::endl; } }; class Car {public : void drive () { engine_.start (); std::cout << "Car is driving" << std::endl; } private : Engine engine_; };
设计原则:优先使用组合,而非继承。
继承是强耦合,基类改动会影响所有派生类
组合更灵活,可以在运行时替换组件
过度继承会导致”菱形继承”等复杂问题
12. RAII:资源获取即初始化 RAII(Resource Acquisition Is Initialization)是 C++ 最重要的编程范式:用对象的生命周期管理资源 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void bad () { FILE* f = fopen ("data.txt" , "r" ); fclose (f); } class FileGuard {public : FileGuard (const char * path, const char * mode) : file_ (fopen (path, mode)) {} ~FileGuard () { if (file_) fclose (file_); } FileGuard (const FileGuard&) = delete ; FileGuard& operator =(const FileGuard&) = delete ; FILE* get () const { return file_; } private : FILE* file_; }; void good () { FileGuard f ("data.txt" , "r" ) ; }
RAII 的典型应用:
std::unique_ptr / std::shared_ptr → 管理动态内存
std::lock_guard → 管理互斥锁
std::fstream → 管理文件句柄
自定义 Guard 类 → 管理任何需要成对 acquire/release 的资源
13. 面向对象设计原则速查
原则
含义
示例
单一职责
一个类只做一件事
不要把数据库操作和 UI 渲染放在同一个类
开闭原则
对扩展开放,对修改封闭
用虚函数 + 派生类扩展功能,而非修改基类
依赖倒置
依赖抽象,而非具体实现
函数参数用 Shape& 而非 Circle&
接口隔离
接口尽量小而专
不要一个 IAnimal 包含 fly()、swim()、run()
里氏替换
派生类必须能替换基类
Dog 必须能用在所有需要 Animal 的地方
七、 内存管理 1. 栈 vs 堆
特性
栈(Stack)
堆(Heap)
分配方式
自动
手动(new/delete)
生命周期
作用域结束自动释放
手动释放
速度
快
较慢
大小
有限(通常几 MB)
受内存限制
2. 动态内存分配 1 2 3 4 5 6 7 int * p = new int (42 );delete p;int * arr = new int [10 ];delete [] arr;
3. 智能指针(C++11,推荐) 1 2 3 4 5 6 7 8 9 10 11 #include <memory> std::unique_ptr<int > up = std::make_unique <int >(42 ); std::shared_ptr<int > sp1 = std::make_shared <int >(42 ); std::shared_ptr<int > sp2 = sp1; std::weak_ptr<int > wp = sp1;
原则: 优先使用智能指针,避免手动 new/delete。
八、 模板 1. 函数模板 1 2 3 4 5 6 7 template <typename T>T max (T a, T b) { return (a > b) ? a : b; } std::cout << max (3 , 5 ) << std::endl; std::cout << max (3.14 , 2.72 ) << std::endl;
2. 类模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename T>class Stack {public : void push (const T& value) { data_.push_back (value); } T pop () { T top = data_.back (); data_.pop_back (); return top; } bool empty () const { return data_.empty (); } private : std::vector<T> data_; }; Stack<int > intStack; intStack.push (10 );
九、 STL 标准库 1. 常用容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <vector> #include <string> #include <map> #include <unordered_map> #include <set> std::vector<int > v = {1 , 2 , 3 }; v.push_back (4 ); v.pop_back (); std::string s = "Hello" ; s += " World" ; std::cout << s.length () << std::endl; std::map<std::string, int > ages; ages["Alice" ] = 25 ; std::unordered_map<std::string, int > fastMap; std::set<int > nums = {3 , 1 , 2 };
2. 常用算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <algorithm> #include <numeric> std::vector<int > nums = {3 , 1 , 4 , 1 , 5 , 9 }; std::sort (nums.begin (), nums.end ()); auto it = std::find (nums.begin (), nums.end (), 4 );if (it != nums.end ()) { std::cout << "Found: " << *it << std::endl; } int sum = std::accumulate (nums.begin (), nums.end (), 0 );std::for_each(nums.begin (), nums.end (), [](int n) { std::cout << n << " " ; });
十、 异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdexcept> double divide (double a, double b) { if (b == 0 ) { throw std::invalid_argument ("Division by zero" ); } return a / b; } int main () { try { double result = divide (10 , 0 ); } catch (const std::invalid_argument& e) { std::cerr << "Error: " << e.what () << std::endl; } catch (...) { std::cerr << "Unknown error" << std::endl; } return 0 ; }
十一、 常用 C++ 特性速查 C++11
特性
示例
auto 类型推导
auto x = 10;
范围 for
for (auto& item : vec)
Lambda
[](int x){ return x * 2; }
智能指针
std::make_shared<T>()
nullptr
int* p = nullptr;
constexpr
constexpr int SIZE = 100;
C++17
特性
示例
结构化绑定
auto [a, b] = std::pair{1, 2};
std::optional
std::optional<int> find(int key);
std::variant
std::variant<int, std::string> v;
if constexpr
if constexpr (std::is_integral_v<T>)
文件系统
std::filesystem::path p("file.txt");
📊 版本迭代记录
版本
日期
更新内容
v0.0.1
2026-04-04
初始版本,包含编译运行、基础语法、控制流、函数、数组与指针、面向对象、内存管理、模板、STL、异常处理、C++11/17 特性速查
v0.0.2
2026-04-04
补充 CMakeLists.txt 逐行详解
v0.0.3
2026-04-04
补充完整 C++ 项目目录结构
v0.0.4
2026-04-04
重构内容逻辑:将项目结构、g++ 编译、CMake 构建整合为统一的”项目结构与编译构建”章节,按”认识结构 → 简单编译 → 构建系统”的顺序组织
v0.0.5
2026-04-07
新增头文件笔记:头文件作用、#include 两种形式、重复包含问题、#pragma once 原理及与 #ifndef 守卫对比
v0.0.6
2026-04-07
新增命名空间完整讲解;大幅扩展面向对象编程:构造函数/析构函数详解、静态成员、友元、运算符重载、多态三要素、override/final、组合vs继承、RAII范式、面向对象设计原则
v0.0.7
2026-04-10
新增成员函数定义完整讲解:类内/类外定义、常量成员函数、静态成员函数、特殊成员函数(构造/析构/拷贝/移动)、inline关键字、函数重载与默认参数、const重载