c++11语法点

1. 列表初始化(统一初始化,大括号 {}

作用

C++11 引入统一的初始化语法 ,使用 {} 对变量、数组、类、容器进行初始化,替代传统 ()=,语法统一。

核心优点

  1. 防止窄化转换 :不允许精度丢失的隐式转换(如 int a{3.14} 直接编译报错),安全性更高。
  2. 语法统一 :普通变量、数组、STL 容器、类对象都能用 {}
  3. 支持默认初始化int a{}; 自动初始化为 0,避免随机垃圾值。

示例

cpp 复制代码
int a{10};
double b{3.14};
int arr[]{1,2,3};
vector<int> vec{1,2,3};

2. auto:自动类型推导

作用

编译器根据右侧的值自动推导变量类型,不用手动写类型,简化代码。

规则

  1. 忽略顶层 const 和引用 ,想要保留需手动加:const autoauto&auto&&
  2. 不能用于:函数参数、函数返回值、类成员变量。
  3. {} 初始化时,auto 会推导成 std::initializer_list

示例

cpp 复制代码
auto a = 10;        // int
auto b = 3.14;      // double
auto& c = a;        // int&
auto d{1,2,3};      // initializer_list<int>

3. decltype:获取表达式类型

作用

查询表达式 / 变量的类型,不计算表达式的值,常用于模板中。

和 auto 的区别

  • auto:根据初始化值推导变量类型;
  • decltype:直接获取表达式本身的类型,不需要初始化。

规则

  1. 普通变量:decltype(x) 得到 x 的类型;
  2. 加括号 decltype((x)):会推导成引用类型

示例

cpp 复制代码
int x = 10;
decltype(x) y = 20;    // y是int类型
decltype((x)) z = y;   // z是int&类型

4. 空值指针:nullptr(替代 C 语言NULL

背景

C 语言用#define NULL 0表示空指针,本质是整数 0,会引发函数重载二义性问题。

C++11 引入nullptr

  • nullptr空指针字面量 ,类型为std::nullptr_t专门代表空指针,类型安全。

核心区别(必背)

  1. NULL:是整数0,传参时优先匹配int类型函数;
  2. nullptr:只能匹配指针类型,不会和整数混淆,解决重载歧义。

示例

cpp 复制代码
void fun(int);
void fun(char*);
fun(NULL);    // 调用fun(int),不符合预期
fun(nullptr); // 调用fun(char*),正确

5. overridefinal 关键字

override

  • 作用强制检查虚函数重写是否合法,编译期校验。
  • 规则:子类函数后加override,编译器检查该函数是否正确重写父类虚函数;函数名、参数、返回值不对,直接报错。
  • 意义:避免手写错函数签名,导致隐藏而非重写。
cpp 复制代码
class Base { virtual void func(); };
class Son : public Base {
    void func() override; // 正确重写
    // void fun() override; 编译报错:父类没有该虚函数
};

final

两种用法:

  1. 修饰虚函数:禁止子类重写该虚函数;
  2. 修饰类:禁止该类被继承。
cpp 复制代码
// 用法1:禁止重写
class Base { virtual void func() final; };
// 用法2:禁止继承
class Son final : public Base {};

一句话总结

override = 强制校验重写final = 禁止重写 / 禁止继承


6. 默认成员函数控制:=default=delete

C++11 可显式控制编译器是否生成默认函数(默认构造、拷贝构造、赋值重载、析构)。

=default

  • 作用:强制让编译器生成默认版本的成员函数,即使你写了其他构造函数。
  • 场景:写了有参构造,仍想保留无参默认构造。
cpp 复制代码
class A {
public:
    A() = default; // 强制生成默认无参构造
    A(int a);
};

=delete

  • 作用:禁用某个成员函数,调用时直接编译报错。
  • 高频场景:禁止拷贝、禁止赋值。
cpp 复制代码
class A {
public:
    A(const A&) = delete; // 禁用拷贝构造,禁止拷贝对象
    A& operator=(const A&) = delete; // 禁用赋值重载
};

核心区别

  • =default启用默认函数
  • =delete禁用函数

7.新增容器

C++11 新增两类容器:序列式容器arrayforward_list)、哈希关联式容器unordered_*系列)

序列式容器

1. array:静态顺序表

  • 本质:固定大小的数组,栈上分配内存,大小编译期确定
  • 对比普通数组:
    1. 支持迭代器、size()empty()等容器接口,使用更规范
    2. 不会退化为指针,类型安全
  • 特点:连续内存、随机访问 O (1)、容量固定不可扩容
  • 用法:array<int,5> arr;(5 个 int 元素)

2. forward_list:单向链表(单向非循环链表

  • 本质:带头结点的单向链表,每个结点只存后继指针
  • 对比list(双向链表):
    1. 内存占用更小,只存一个指针
    2. 只支持前向遍历,不支持反向迭代
    3. 插入 / 删除快,但无法访问前驱结点
  • 适用:只需要单向遍历、节省内存的场景

哈希桶结构关联式容器(unordered 系列)

包含 4 个:unordered_mapunordered_setunordered_multimapunordered_multiset

底层结构

哈希表(数组 + 链表 / 红黑树)无序存储,查找平均 O (1)

与传统map/set(红黑树,有序,O (logn))核心区别

  1. 结构:unordered → 哈希表;map/set → 红黑树
  2. 有序性 :unordered无序 ;map/set按键有序
  3. 效率:unordered 查找更快(平均 O (1));map 稳定 O (logn)
  4. 底层冲突:哈希冲突用链地址法解决

4 个容器区别

  1. unordered_set:键唯一,只存键,无序
  2. unordered_multiset:键可重复,只存键
  3. unordered_map:键唯一,键值对 <key,value>,无序
  4. unordered_multimap:键可重复,键值对

面试必背对比总结

  1. array vs vector:array 固定大小栈分配;vector 动态扩容堆分配
  2. forward_list vs list:forward_list 单向、省内存;list 双向、支持反向遍历
  3. unordered_map vs map:哈希表无序、O (1);红黑树有序、O (logn)

8.C++ 智能指针全解

1. 什么是智能指针

智能指针是封装了普通裸指针的类 ,利用RAII 思想 ,自动管理堆内存的申请与释放,防止内存泄漏,不用手动调用delete

2. RAII(核心原理)

资源获取即初始化

  • 构造函数中获取资源(申请内存)
  • 析构函数中释放资源(释放内存)
  • 智能指针出作用域自动调用析构,内存自动释放

3. 智能指针通用实现原理

  1. 封装裸指针,重载*->运算符,实现指针的解引用、访问成员
  2. 依靠 RAII,出作用域自动释放内存
  3. 根据管理方式分为:独占式、共享式、弱引用式

4. C++98 auto_ptr

实现原理

所有权转移:拷贝 / 赋值时,把资源所有权从原对象转移到新对象,原对象置空。

缺陷

  1. 拷贝后原指针失效,极易出现空指针问题
  2. 不能放入容器(容器会频繁拷贝)
  3. C++11 已废弃,被unique_ptr替代

5. unique_ptr(独占式智能指针)

实现原理

独占所有权一份资源只能被一个unique_ptr管理 ,禁止拷贝,只允许移动(std::move

缺陷

无法多对象共享同一份资源

特点

开销最小、效率最高,优先使用

6. shared_ptr(共享式智能指针)

实现原理

引用计数机制

  1. 维护 2 块内存:管理的资源 + 引用计数
  2. 拷贝时引用计数 + 1,析构时计数 - 1
  3. 计数减为 0 时,释放资源

缺陷

循环引用问题 :两个对象互相用shared_ptr指向对方,计数永远不为 0,内存泄漏

解决方式

使用 weak_ptr(弱智能指针):不增加引用计数,打破循环引用


三者一句话总结

  • unique_ptr:独占,不能拷贝,效率最高
  • shared_ptr:共享,引用计数,会循环引用
  • weak_ptr:配合 shared_ptr,解决循环引用

9.右值引用

1. 什么是右值引用

C++11 引入,用 && 表示,专门绑定右值的引用,用来实现移动语义,避免不必要的深拷贝,提升性能。

2. 右值引用和普通引用(左值引用)的区别

  • 左值引用 & :只能绑定左值(有名字、可取地址)
  • 右值引用 && :只能绑定右值(临时值、不可取地址)
  • 简单记:& 绑左值,&& 绑右值

3. 右值引用能否引用左值?如何做?

可以 ,通过 std::move() 强制将左值转为右值,让右值引用绑定左值。例:

cpp 复制代码
int a = 10;
int&& b = move(a); // move把左值a转成右值

4. move 函数

  • 作用:强制类型转换 ,将左值无条件转为右值,本身不移动资源,只是转换语义。
  • 本质:只是类型转换,实际移动由移动构造 / 移动赋值完成。

5. 左值和右值

  • 左值:有变量名、可被取地址、可修改;如普通变量、函数返回的引用。
  • 右值 :临时值、没有名字、不可取地址;如字面量10、表达式a+b、函数临时返回值。

6. 移动语义

转移资源所有权,不拷贝资源

  • 深拷贝:复制一份完整数据,开销大
  • 移动语义:直接把原对象的堆内存指针交给新对象,原对象置空,O (1) 开销,大幅提升效率。

7. 移动构造 和 移动赋值

移动构造函数

参数为右值引用,用临时对象的资源构造新对象,转移资源。

移动赋值运算符

参数为右值引用,将临时对象资源转移给当前对象。

编译器默认生成;类内写了拷贝构造 / 赋值,编译器不再默认生成。

8. 完美转发 std::forward

作用

模板函数 中,保持参数原本的左 / 右值属性,原样转发给下层函数。

  • move:一律转成右值
  • forward:左值传进来保持左值,右值传进来保持右值

用法

cpp 复制代码
template<typename T>
void func(T&& t) {
    test(forward<T>(t));
}

一句话总结

右值引用绑定临时值;move 转左值为右值;forward 保持属性;移动语义转移资源,代替深拷贝。

10.lambda 表达式

1. lambda 表达式概念

C++11 引入的匿名内联函数,可以直接在代码行定义临时函数,常用于回调、排序、遍历,不用单独定义函数,代码更简洁。

2. lambda 语法

cpp 复制代码
[捕获列表](参数列表) mutable -> 返回值类型 { 函数体 }
  • []:捕获外部变量
  • ():函数参数
  • mutable:允许修改值捕获的变量
  • ->:显式指定返回值类型,可省略(编译器自动推导)

3. 捕获列表规则(高频考点)

  1. []:不捕获任何变量
  2. [=]值捕获,所有外部变量拷贝一份,只读,不可修改
  3. [&]引用捕获,所有外部变量按引用捕获,可修改原变量
  4. [this]:捕获当前类的 this 指针,访问类成员
  5. [=, &a]:混合捕获:默认值捕获,a 用引用捕获
  6. 注意:值捕获的变量默认const,需加mutable才能修改

4. lambda 和仿函数的区别

仿函数

重载operator()的类,需要显式定义类,可复用,类型可命名。

lambda

编译器自动生成匿名仿函数类 ,一次性使用,不能复用,语法更简洁。简单说:lambda = 语法糖版匿名仿函数

5. 底层实现原理

编译器遇到 lambda,自动生成一个匿名类

  1. 捕获的变量 → 变成匿名类的成员变量
  2. 函数体 → 重载operator()运算符
  3. 引用捕获存引用,值捕获存拷贝值本质:lambda 就是编译器帮你写的仿函数

一句话总结

lambda 是匿名内联函数 ,底层是自动生成的仿函数;通过捕获列表获取外部变量,支持值捕获 / 引用捕获,简洁灵活。

11.线程库

C++11 引入标准多线程库 ,头文件 <thread>,实现跨平台多线程编程,核心组件如下:

1.std::thread 线程类

  1. 创建线程thread t(函数/可调用对象, 参数),线程创建后立即开始执行
  2. join():主线程阻塞,等待子线程执行完毕,回收资源
  3. detach():线程分离,后台独立运行,主线程不等待
  4. 注意:一个线程只能 joindetach 一次

2.互斥锁(解决线程安全、数据竞争)

  1. std::mutex :基础互斥锁,手动lock()/unlock()
  2. std::lock_guardRAII 自动锁,构造加锁、析构解锁,自动释放,避免死锁
  3. std::unique_lock:灵活锁,支持手动加解锁、延时加锁、所有权转移,可配合条件变量

3.条件变量 std::condition_variable

  1. 作用:线程间等待 / 唤醒,实现线程同步(生产者 - 消费者模型)
  2. 核心函数:
    • wait():线程阻塞,释放锁,等待被唤醒
    • notify_one():唤醒 1 个等待线程
    • notify_all():唤醒全部等待线程
  3. 必须配合 unique_lock 使用

4.原子类型 std::atomic

  1. 作用:无锁并发,对基础类型(int、bool)原子操作,避免加锁开销
  2. 特点:操作不可分割,不会出现指令中断,线程安全

5.future & promise(获取线程返回值)

  1. std::promise:线程内存储结果
  2. std::future:主线程获取异步结果,get() 阻塞等待
  3. std::async:便捷创建异步任务,直接返回 future

6.线程安全问题 & 死锁

死锁产生 4 个必要条件

  1. 互斥条件
  2. 占有且等待
  3. 不可剥夺
  4. 循环等待

解决:破坏任意一个条件,最常用顺序加锁


一句话总结

thread 创建线程,mutex 保护共享数据,condition_variable 实现线程等待唤醒,atomic 无锁操作,future 获取返回值。

相关推荐
Peter·Pan爱编程4 小时前
C++中的 const 与 volatile:比C强大十倍
c++·const·volatile·c++基础
lihao lihao4 小时前
MFC知识点
c++·mfc
Shadow(⊙o⊙)4 小时前
进程分析2.0——进程退出、进程等待-Linux重要经典模块
linux·运维·服务器·开发语言·c++·学习
ch.ju4 小时前
Java Programming Chapter 4——Dynamic part
java·开发语言
阿正的梦工坊4 小时前
Kotlin 面试题全面解析:从基础到进阶
android·开发语言·kotlin
沐知全栈开发4 小时前
TypeScript Map 对象
开发语言
yujunl4 小时前
U9开发模式之一门面模式的理解
开发语言
奔跑的Ma~4 小时前
第6篇:蓝桥杯C++进阶突破(难题拆解+算法优化,冲刺国赛高奖)
c++·算法·蓝桥杯·#蓝桥杯备战·#c++编程·编程竞赛
Chase_______4 小时前
【Java基础核心知识点全解·第0篇】Java开发环境搭建指南:JDK + IDEA 从安装配置到运行 HelloWorld
java·开发语言·intellij-idea