C++11 四大核心知识点详解

auto 自动类型推导

作用

编译器根据初始值自动推导变量类型,无需手动书写复杂类型,简化代码、提升可读性,C++11 正式标准化。

基本语法

auto 变量名 = 初始值;

基础用法

// 推导为 int
auto a = 10;
// 推导为 double
auto b = 3.14;
// 推导为 const char*
auto str = "hello";
// 推导为 std::string
auto s = std::string("c++11");

结合引用、const

  • auto 默认舍弃引用、顶层 const,如需保留必须手动加修饰符
int x = 10;
int& ref = x;
const int cx = 20;

// 普通auto:推导为 int(丢弃引用)
auto v1 = ref;
// auto&:保留引用
auto& v2 = ref;
// const auto&:保留 const + 引用(常用,避免拷贝)
const auto& v3 = cx;

适用场景

  1. 复杂容器迭代器(最常用)
std::vector<int> vec = {1,2,3};
// C++11 前:std::vector<int>::iterator it
auto it = vec.begin();
  1. 模板返回值、长类型、lambda 接收等场景。

限制与注意事项

  1. 变量必须初始化auto a; 编译报错,无初始值无法推导;
  2. 不能用于函数形参、类非静态成员变量;
    普通函数的形参类型是确定类型;
    一旦用 auto,函数直接变成模板函数
    每传一种不同类型,编译器会实例化一份新函数
    函数地址、重载决议、函数指针绑定全部改变
  3. 同一作用域下 auto 变量推导类型固定,不能二次赋值不同类型。

范围 for 循环(基于范围的 for 循环)

作用

简化容器/数组遍历,无需手动管理迭代器、下标,C++11 新增语法。

标准语法

for (元素声明 : 可遍历对象)
{
    循环体;
}

可遍历对象:数组、std::string、STL 容器(vector/map/list 等)。

基础遍历(值拷贝)

std::vector<int> vec = {1,2,3,4};
// elem 是容器元素的拷贝,修改不影响原容器
for (auto elem : vec)
{
    std::cout << elem << " ";
    elem += 1; // 仅修改副本
}

引用遍历(推荐,避免拷贝)

  • auto&:可读写原元素
  • const auto&:只读遍历(性能最优,优先使用)
// 读写遍历
for (auto& elem : vec)
{
    elem *= 2;
}

// 只读遍历(常量容器/仅读取场景)
const std::vector<int>& cv = vec;
for (const auto& elem : cv)
{
    std::cout << elem << " ";
}

遍历原生数组

int arr[] = {10,20,30};
for (auto x : arr)
{
    std::cout << x << " ";
}

注意事项

  1. 反汇编后范围 for 依赖迭代器,遍历过程中不要增删容器元素,会导致迭代器失效
  2. 无法直接获取当前元素下标,需要下标时改用传统 for 循环;
  3. 仅支持完整遍历,不能指定起始/结束位置。

列表初始化(统一初始化语法)

C++11 统一 {} 初始化规则,称为列表初始化,解决 C++98 不同类型初始化语法混乱问题,又称大括号初始化

基础语法

所有类型统一使用 {} 初始化,支持变量、数组、结构体、类、容器

// 内置类型
int a{10};
double b{3.14};

// 数组
int arr[]{1,2,3};
int arr2[3]{10,20}; // 未赋值元素自动置0

窄转换检查(核心特性)

{}严格禁止隐式窄类型转换,编译报错,规避隐性 bug;() 无此限制。

int x1 = 3.9;    // C++98 允许,截断为 3(隐患)
int x2(3.9);     // 允许截断
int x3{3.9};     // C++11 编译报错:浮点转整型属于窄转换

容器初始化(极大简化代码)

STL 容器直接用 {} 初始化元素,C++98 无法实现:

// vector 初始化
std::vector<int> v{1,2,3,4};
// map 键值对初始化
std::map<int, std::string> m{{1,"a"}, {2,"b"}};

类/结构体初始化

普通结构体
struct Point
{
    int x;
    int y;
};
Point p{100, 200}; // 顺序赋值成员
自定义类
class Test
{
public:
    Test(int a) : num(a){}
private:
    int num;
};
Test t{666}; // 调用构造函数

初始化列表空值

{} 表示零初始化,内置类型初始化为 0,类调用默认构造:

int n{};    // n = 0
double d{}; // d = 0.0

类型别名 using

C++11 扩展 using 关键字,实现类型别名,替代传统 typedef,语法更直观、支持模板别名。

基础用法(替代 typedef)

语法对比:

  • typedef 原类型 别名; (C++98)
  • using 别名 = 原类型; (C++11,推荐)
// 1. 普通类型别名
typedef unsigned int uint;
using UInt = unsigned int; // 等价,语法更自然

// 2. 容器类型别名(长类型简化)
typedef std::vector<std::string> StrVec;
using StringVec = std::vector<std::string>;

函数指针别名(typedef 易读性差,using 优势明显)

// C++98 typedef 写法(晦涩)
typedef void(*Func)(int, int);

// C++11 using 写法(直观:别名 = 原类型)
using FuncPtr = void(*)(int, int);

模板别名(using 独有能力,typedef 不支持)

这是 using 最大亮点,可以给模板起别名

// 给 vector 模板起别名
template <typename T>
using Vec = std::vector<T>;

// 使用
Vec<int> v1;
Vec<std::string> v2;

结合 const、引用

using IntRef = int&;
using ConstStr = const std::string;

using 与 typedef 总结

  1. 功能等价:基础类型、指针、引用场景均可互换;
  2. 语法:using 别名 = 类型 阅读顺序和逻辑一致,更友好;
  3. 关键区别:仅 using 支持模板别名,现代 C++ 优先使用 using

更多推荐