1. #include:双引号 " " vs. 尖括号 < >
结论先行
- 项目内头文件:优先用
"..." - 系统/已安装第三方库头文件:用
<...>
查找顺序(通用思路)
#include "名字"- 先在“当前包含该语句的文件所在目录”查找
- 再查 用户指定的包含目录(如编译器的
-I/“Additional Include Directories”) - 最后查 系统包含目录(标准库、系统 SDK 等)
#include <名字>- (部分编译器也会查用户指定包含目录)
- 直接查 系统包含目录
一句话经验:项目内用
"...",系统或第三方库(已安装到系统路径)用<...>。
2. 指针、常量指针、指针常量;const 的价值
2.1 指针的本质
- 指针是“带类型的地址变量”:
- 值:保存一块内存的地址
- 类型:知道自己“指向什么类型”
- 解引用:通过
*p访问该地址处的对象
2.2 “常量指针” vs “指针常量”
- 常量指针(pointer to const)
const int *p; // 等价于 int const *p;int x = 10, y = 20;
const int *p = &x;
p = &y; // ✅ 可以改指向
*p = 30; // ❌ 不可改值- 记忆:
const靠近*p,约束“所指之物”。
- 含义:不能通过
p修改所指向的值;但 p 自身可改指向。
- 记忆:
- 指针常量(const pointer)
int *const p = &x;int x = 10, y = 20;
int *const p = &x;
*p = 30; // ✅ 可改值
p = &y; // ❌ 指针本身不可变- 记忆:
const靠近标识符p,约束“指针自身”。
- 含义:
p本身的地址 不可变;但 可改所指向的值。
- 记忆:
- 两者皆定
const int *const p = &x; // 值与指针均不可变
2.3 const 的优势
- 防误改:语义化“只读”。
- 接口更清晰:
void printStr(const char* s); // 承诺不修改来访数据 - 利于优化/更安全:
- 编译期常量折叠、更少可变性带来更好推断
- 与
#define相比:const有类型、有作用域,可调试
- 编译期常量作为数组大小(需可在编译期确定):
const int N = 10;
int a[N]; // ✅ 若 N 的值编译期可知
3. C++11 新特性速览
3.1 统一初始化(Initializer List)
int arr[3]{1,2,3};
std::vector<int> v{1,2,3,4};
struct Point { int x; int y; };
Point p{10,20};
- 避免缩窄(narrowing),更安全:
double d = 3.14;
int x(d); // 允许(可能截断)
int y = d; // 允许(可能截断)
int z{d}; // ❌ 编译错误:从 double 到 int 的缩窄
3.2 auto 类型推导
- 由 初始化表达式 推导类型:
auto x = 10; // int
auto it = v.begin(); // std::vector<int>::iterator
- 约束要点:
- 必须初始化(
auto a;❌) - 不能作 函数形参类型
- 不能直接定义 数组类型(
auto arr[] = ...❌;auto p = "abc";是指针) - 同一声明语句里各变量推导类型必须一致
- 必须初始化(
- 与
decltype配合,模板代码更简洁。
3.3 decltype 表达式求型
int a = 5;
decltype(a) b = 10; // b 为 int
规则速记
exp非括号包围的名字/表达式 → 推导为其 类型本身exp是函数调用 → 推导为 返回类型exp为 左值 或 被括号包围 → 推导为 引用类型
示例:
class Base { public: int m; };
int fun(int a, int b){ return a + b; }
int x = 2;
decltype(x) y = x; // int
decltype(fun(x,y)) sum = 0; // int(fun 的返回类型)
Base A;
decltype(A.m) u = 0; // int
decltype((A.m)) r = u; // int& —— 注意括号
decltype(x+y) c = 0; // int
decltype(x = x + y) d = c; // int& —— 左值
与 auto 的差别
auto:由 初始值 推导,必须初始化,更简洁decltype:由 任意表达式 推导,不必初始化,更灵活
3.4 范围 for(Range-based for)
std::vector<int> v{1,2,3};
for (auto &x : v) {
x *= 2;
}
- 可读性强,不再显式使用迭代器/下标。
3.5 nullptr
void f(int);
void f(char*);
f(nullptr); // 调用 f(char*),避免与 f(int) 二义性
- 类型安全的空指针常量,优于
NULL/0。
3.6 Lambda 表达式
语法:
[capture](params) -> ret { body }
示例:
auto add = [](int x, int y){ return x + y; };
std::for_each(v.begin(), v.end(), [](int &x){ x *= 2; });
捕获用法:
int a = 10, b = 20;
// 按值捕获
auto by_val = [=](){ /* 读 a,b 的副本 */ };
// 按引用捕获
auto by_ref = [&](){ a *= 2; b += 5; };
3.7 智能指针(Smart Pointer)
- 头文件:
<memory> std::unique_ptr:独占所有权,不可拷贝,可移动(std::move)。std::shared_ptr:共享所有权,引用计数;注意循环引用问题。- **
std::weak_ptr**:弱引用,不增加计数,用于打破循环引用,可lock()获得临时shared_ptr。
示例(避免循环引用):
#include <memory>
#include <iostream>
class A;class B;
class A {
public:
std::shared_ptr<B> b;
~A(){ std::cout << "A destroyed\n"; }
};
class B {
public:
std::weak_ptr<A> a; // 用 weak_ptr 观察 A
~B(){ std::cout << "B destroyed\n"; }
};
int main(){
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a; // 不会形成循环引用
if (auto locked = b->a.lock()) {
std::cout << "lock ok\n";
}
}
3.8 左值/右值 与 移动语义
- 左值(lvalue):有稳定存储位置,可取地址,可做赋值号左侧;如变量、解引用结果等。
- 右值(rvalue):临时对象/表达式结果;不可持久绑定到非常量左值引用。
移动示例:
std::vector<int> v1{1,2,3};
std::vector<int> v2 = std::move(v1); // 资源转移,避免深拷贝
std::move(x)只是把x转换为右值引用(T&&),提示可移动;并不“搬运”数据。
引用类别:
int a = 10; int& lr = a; // 左值引用
int&& rr = 5 + 2; // 右值引用
4. 速记清单(Cheat Sheet)
#include:项目内"...";系统/第三方<...>。const:读语义、防误改、利优化;#define无类型且仅文本替换。- 指针修饰:
const T* p(值不可改) vs.T* const p(指针不可改)。 - 统一初始化
{}:禁止危险缩窄。 auto需初始化;decltype按表达式求型。nullptr避免重载二义性。- Lambda:捕获
[=](值)/[&](引用)。 - 智能指针:优先
unique_ptr,共享用shared_ptr,破环用weak_ptr。 - 移动语义:
std::move表达可移动性,减少拷贝。
微信扫描下方的二维码阅读本文

Comments NOTHING