C++ 核心知识点全解
一、基础语法与类型系统
1. 基本数据类型与大小
C++ 标准只规定最小范围,具体大小由平台决定。常见 32/64 位平台典型大小:
| 类型 | 典型字节数 | 说明 |
|---|---|---|
| bool | 1 | true / false |
| char | 1 | 字符 / 小整数 |
| short | 2 | 短整型 |
| int | 4 | 整型(常用) |
| long | 4 / 8 | 长整型 |
| long long | 8 | 长长整型 |
| float | 4 | 单精度浮点 |
| double | 8 | 双精度浮点(常用) |
| long double | 8 / 16 | 扩展精度 |
- 无符号类型:
unsigned char、unsigned int等,只存非负数。 - 字面值后缀:
10u/10U→ unsigned10l/10L→ long10ll/10LL→ long long3.14f→ float
2. 类型限定符
- const:只读变量,必须初始化,之后不能赋值。
const int a = 10; a = 20; // 错误 - constexpr:C++11,编译期可求值,性能更好。
constexpr int square(int x) { return x*x; } int arr[square(5)]; // 合法,编译期确定大小 - volatile:告诉编译器不要优化,每次必须从内存读,多用于硬件/多线程。
- register:建议放入寄存器,现代编译器基本自动优化。
3. 初始化方式
C++11 推荐统一使用列表初始化,更安全。
int a = 10; // 拷贝初始化
int b(10); // 直接初始化
int c{10}; // 列表初始化(推荐)
int d = {10}; // 同上
- 列表初始化禁止窄化转换:
int x{3.14}; // 编译报错,防止精度丢失
4. 左值与右值
- 左值:可以放在
=左边,有内存地址,可以被多次使用。int a = 10; a = 20; // a 是左值 - 右值:临时值,不能取地址,用完即销毁。
10; // 右值 a + b; // 表达式结果是右值
5. 自增自减
- 前置
++i:先自增,返回自身(左值),效率更高。 - 后置
i++:先复制旧值,再自增,返回旧值(右值)。
int i = 0;
cout << ++i; // 输出 1
cout << i++; // 输出 1,i 变为 2
6. 流程控制
- 范围 for(C++11):遍历容器/数组更简洁。
vector<int> v = {1,2,3}; for (int x : v) { ... } // 拷贝 for (int& x : v) { ... } // 引用,可修改 for (const int& x : v) { ... } // 只读,高效 - switch:只能匹配整型/枚举常量,case 默认穿透,必须加
break。
7. 函数重点
7.1 默认参数
- 必须从右往左连续设置。
void f(int a, int b=1, int c=2); // 正确 void f(int a=1, int b, int c); // 错误 - 声明时写默认参数,实现时不要重复写。
7.2 函数重载
同一作用域内,函数名相同,参数列表不同(个数/类型/顺序)。
int add(int a, int b);
double add(double a, double b); // 重载
- 返回值不同不能构成重载。
7.3 内联函数 inline
- 建议编译器将函数体展开到调用处,减少函数调用开销。
- 适合短小、频繁调用的函数。
- 类内定义的函数默认 inline。
二、指针、引用、内存结构
1. 指针
- 指针是存地址的变量。
int a = 10; int* p = &a; // p 存 a 的地址 *p = 20; // 解引用,修改 a - 空指针:
nullptr(C++11),类型安全,优于NULL。 - 野指针:指向无效/已释放内存,使用会崩溃或未定义行为。
- 悬空指针:
delete后未置nullptr。 - 指针运算:
p+1以指向类型大小为单位,只在同一数组内合法。
2. 引用 &
- 引用是变量别名,与原对象共用同一块内存。
int a = 10;
int& r = a;
r = 20; // a 也变成 20
- 特性:
- 必须初始化,不能“空引用”。
- 不能重新绑定到另一个变量。
- 编译器底层通常用指针实现,但使用更安全。
- const 引用可以绑定临时对象(右值):
const int& r = 10; // 合法 - 常用场景:
- 函数参数避免大对象拷贝。
- 函数返回成员引用(注意生命周期)。
3. C++ 内存五大区
-
栈 stack
- 局部变量、函数参数、返回地址。
- 自动分配/释放,连续、小、极快。
- 递归过深、超大局部数组会栈溢出。
-
堆 heap
new/malloc分配,手动管理。- 空间大、随机、较慢。
- 忘记
delete→ 内存泄漏。
-
全局/静态区
- 全局变量、
static变量。 - 程序启动创建,结束释放,默认初始化为 0。
- 全局变量、
-
常量区
- 字符串常量、
const全局数据。 - 只读,修改会崩溃。
- 字符串常量、
-
代码区
- 存放可执行指令,只读共享。
4. new/delete 与 malloc/free
new/delete是运算符,会调用构造/析构函数。malloc/free是库函数,只分配/释放裸内存。- 必须配对:
new↔deletenew[]↔delete[]
- 常见错误:
- 用
free释放new出来的内存。 - 用
delete释放malloc出来的内存。 - 重复释放、释放野指针。
- 用
三、面向对象 OOP
1. 类与访问控制
class默认private,struct默认public。- 访问权限:
private:仅本类/友元可访问。protected:本类/友元/子类可访问。public:全局可访问。
2. 六大特殊成员函数(C++11)
编译器会自动生成,但若你手写其中一部分,另一些可能被删除。
- 默认构造
- 析构函数
- 拷贝构造
- 拷贝赋值
- 移动构造
- 移动赋值
3. 构造函数
- 与类同名,无返回值,创建对象时自动调用。
- 初始化列表:比函数体内赋值更早、效率更高。
Person(string n, int a) : name(std::move(n)), age(a) {} - 必须用初始化列表的场景:
const成员- 引用成员
- 基类无默认构造
- 成员对象无默认构造
4. 析构函数
~类名(),无参无返回,不可重载。- 对象销毁时自动调用,用于释放资源(堆内存、文件、socket 等)。
- 若类中有指针成员,通常需要手写析构,防止内存泄漏。
- 多态基类的析构函数建议声明为 virtual,否则可能只调用基类析构,造成泄漏。
5. 拷贝构造 & 浅/深拷贝
- 拷贝构造:用已有对象初始化新对象。
Person(const Person& other); - 默认拷贝构造是浅拷贝:只复制成员值。
- 浅拷贝问题:多个对象指向同一块堆内存,析构时重复 delete,崩溃。
- 深拷贝:重新分配内存并复制内容,需手动实现。
6. 拷贝赋值运算符
Person& operator=(const Person& other);
- 经典三步写法:
- 防止自赋值
if (this == &other) return *this; - 释放当前对象资源
- 深拷贝资源
- 返回
*this支持链式赋值
- 防止自赋值
7. 移动语义(C++11)
- 右值引用
T&&:专门绑定临时对象。 - 移动构造 / 移动赋值:转移资源所有权,不拷贝数据,效率极高。
Person(Person&& other) noexcept; Person& operator=(Person&& other) noexcept; std::move:将左值强制转为右值引用,本身不移动任何数据。- 标准库容器默认优先使用移动操作。
8. this 指针
- 指向当前对象本身,类型为
类名* const。 - 用于区分成员与参数重名:
void setAge(int age) { this->age = age; } - 链式调用:返回
*thisPerson& setName(string n) { name = std::move(n); return *this; } p.setName("Alice").setAge(20);
四、继承与多态
1. 继承方式
class 子类 : 继承方式 父类 { ... };
public继承:最常用,基类权限基本保留。protected继承:基类public变protected。private继承:基类所有成员变private。
2. 构造析构顺序
- 构造:
基类构造 → 成员对象构造 → 自身构造 - 析构:
自身析构 → 成员对象析构 → 基类析构 - 子类必须在初始化列表中初始化基类,如果基类没有默认构造。
3. 同名隐藏
- 子类定义与基类同名函数,会隐藏基类所有同名重载版本。
- 如需在子类中使用基类重载版本:
using Base::func;
4. 运行期多态
多态 = 接口统一,实现不同。
满足三条件:
- 公有继承
- 基类函数为 virtual 虚函数
- 基类指针/引用指向子类对象
- 底层实现:虚表 vtable + 虚指针 vptr。
- 重写:子类重新实现虚函数,函数签名必须完全一致。
override:显式标记重写,编译器检查拼写/签名错误,强烈推荐。void show() override;final:- 修饰类:禁止被继承。
- 修饰虚函数:禁止被重写。
5. 纯虚函数与抽象类
virtual void draw() = 0;
- 包含纯虚函数的类称为抽象类,不能实例化。
- 子类必须实现所有纯虚函数才能实例化,用于定义接口。
6. 虚析构
- 基类析构声明为
virtual,则通过基指针删除子类对象时,会多态调用子类析构。 - 否则只调用基类析构,子类资源泄漏。
class Base { public: virtual ~Base() = default; };
7. 菱形继承与虚继承
- 问题:顶层基类被多份继承,数据冗余、访问二义性。
- 解决方案:虚继承
class B : virtual public A { ... }; class C : virtual public A { ... }; - 虚继承下,由最派生类负责初始化公共基类。
五、模板与泛型编程
1. 函数模板
template <typename T>
T add(T a, T b) {
return a + b;
}
- 编译期根据实参类型生成对应版本(实例化)。
- 支持多个类型参数、非类型模板参数(整数常量)。
template <typename T, int N> T sum(T (&arr)[N]) { ... }
2. 类模板
template <class T>
class Stack {
vector<T> data;
public:
void push(const T& x);
};
- 实例化:
Stack<int> st; - 类模板成员函数通常全写在头文件/同文件,否则链接时可能找不到实例。
3. 模板特化
- 全特化:对某一特定类型完全重写。
template <> class Stack<int> { ... }; - 偏特化(只适用于类模板):对一类类型(如指针)特殊处理。
template <class T> class Stack<T*> { ... };
4. 可变参数模板(C++11)
template <class... Args>
void print(Args... args) { ... }
...表示参数包。- 展开方式:递归、逗号表达式、折叠表达式(C++17)。
template <class... Args> auto sum(Args... args) { return (args + ...); // 一元右折叠 }
六、现代 C++ 核心特性(C++11/14/17)
1. auto 类型推导
- 从初始化表达式自动推导类型。
- 会丢弃顶层 const、引用,如需保留:
const auto& x = y; - 典型场景:迭代器、复杂模板、lambda。
vector<int> v; auto it = v.begin();
2. decltype
- 推导表达式类型,不丢弃 const/引用。
- 常用于返回类型后置、模板元编程。
int a = 10; decltype(a) b = 20; // int decltype((a)) r = a; // int&
3. Lambda 表达式
[capture](params) mutable -> ret { body }
- 捕获列表:
[]不捕获[=]值捕获外部变量[&]引用捕获[this]捕获当前对象指针
mutable:允许修改值捕获的副本。- 无捕获 lambda 可隐式转为函数指针。
- 常用于 STL 算法、回调、线程函数。
sort(v.begin(), v.end(), [](int a, int b) { return a < b; });
4. 智能指针()
用于自动管理堆内存,避免泄漏/重复释放。
- unique_ptr:独占所有权,不能拷贝,只能移动,效率接近裸指针。
unique_ptr<int> p(new int(10)); auto p2 = std::move(p); - shared_ptr:引用计数共享,线程安全的计数,对象访问非线程安全。
- weak_ptr:弱引用,不增加引用计数,解决 shared_ptr 循环引用。
- 优先使用工厂函数:
make_unique<T>(...) // C++14 make_shared<T>(...)
5. 其他常用现代特性
- nullptr:类型安全空指针,替代
NULL。 - 列表初始化:统一语法,禁止窄化。
- noexcept:声明函数不抛异常,利于优化,移动操作建议加。
- constexpr:编译期计算,性能更好。
- 结构化绑定(C++17):
pair<int, string> f(); auto [id, name] = f();
七、STL 标准模板库
1. 容器分类
- 序列容器:
vector、list、deque、array、string - 关联容器:
set、map、multiset、multimap(红黑树,有序) - 无序关联容器:
unordered_set、unordered_map(哈希表,平均 O(1)) - 适配器:
stack、queue、priority_queue
2. 常用容器选型
- 需要随机访问、尾部增删快 →
vector - 需要任意位置频繁插入/删除 →
list - 需要头尾快速增删 →
deque - 需要key-value 有序查找 →
map - 需要最高查询性能、无序 →
unordered_map
3. 迭代器
- 行为类似指针,提供统一遍历接口。
- 区间
[begin(), end())左闭右开。 - 类型(从弱到强):
输入 → 输出 → 前向 → 双向 → 随机访问 cbegin()/cend():常量迭代器,不能修改元素。
4. 常用算法()
- 遍历:
for_each - 查找:
find、find_if、count、binary_search - 排序:
sort、stable_sort - 修改:
copy、replace、fill、reverse - 删除惯用手法:erase-remove
v.erase(remove(v.begin(), v.end(), val), v.end());
八、异常处理
- 关键字:
try、throw、catch - 匹配规则:从上到下,类型优先匹配。
- 捕获所有异常:
catch (...) - 标准异常继承自
std::exception,提供what()信息。 - 常见标准异常:
bad_alloc:new分配失败out_of_range:越界invalid_argument:无效参数
- RAII:利用对象生命周期自动管理资源,是异常安全的核心方案。
- 智能指针
lock_guard/unique_lock
九、友元、运算符重载
1. 友元
- 友元函数/友元类可以访问类的
private/protected成员。 - 友元关系:单向、不传递、不继承。
- 典型场景:重载
<</>>、工具类访问内部数据。class A { friend void func(A& a); friend class B; };
2. 运算符重载
- 成员函数形式:
返回值 operator op(参数) - 非成员(友元):适合对称运算符(
+、==)。 - 不能重载的运算符:
::、.*、.、?:、sizeof、typeid - 自增自减区分:
- 前置:
T& operator++() - 后置:
T operator++(int)
- 前置:
- 流运算符
<</>>必须重载为非成员函数。
十、常用工程惯用法
- RAII:资源获取即初始化,自动释放,安全简洁。
- PImpl:接口与实现分离,降低编译依赖,加速编译。
- 移动优先:能用移动就不用拷贝,提升性能。
- 尽量用 const:提高可读性、安全性、优化空间。
- 少用全局变量、宏:易冲突、难调试。
- 头文件保护:
#pragma once或#ifndef ... #define ... #endif。

7449

被折叠的 条评论
为什么被折叠?



