【学习笔记05】C++11新特性学习总结(下)
📝 说明:本文为C++学习笔记,AI辅助系统整理
📅 整理时间:2025年9-10月
🎯 目的:系统梳理C++11核心特性,建立完整知识体系
💡 适用场景:面试复习、技术查阅
📚 本文内容
- nullptr(空指针)
- enum class(强类型枚举)
- delete和default关键字
- using别名
本篇整理的知识点(下半部分)
下半部分整理了剩余4个C++11特性:
- nullptr(空指针)
- enum class(强类型枚举)
- delete和default关键字
- using别名
知识点5:nullptr
为什么需要nullptr?
NULL的问题:
void func(int x) { cout << "func(int): " << x << endl;
}void func(int* p) { cout << "func(int*): " << p << endl;
}int main() {func(NULL); // 调用func(int)!❌ 有歧义func(nullptr); // 调用func(int*) ✅ 明确return 0;
}
问题原因:
NULL
本质是0
或(void*)0
- 编译器会优先匹配
func(int)
- 导致重载函数调用歧义
nullptr的特点
核心特点:
// nullptr是真正的空指针类型
int* p1 = nullptr; // OK
int* p2 = NULL; // OK,但是旧写法// nullptr不能转换为int
int x = nullptr; // ❌ 编译错误
int y = NULL; // ✅ OK(NULL是0)// nullptr可以转换为任何指针类型
char* cp = nullptr;
void* vp = nullptr;
我的理解:nullptr是C++11专门为空指针设计的关键字,比NULL更安全、更明确。
nullptr使用对比图
知识点6:enum class
传统enum的问题
典型示例:
// 传统enum(有问题)
enum Color { RED, GREEN, BLUE };
enum Status { RED, OK }; // ❌ 编译错误!RED重复定义// 枚举值会污染外部作用域
int x = RED; // OK,但容易混淆
问题:
- 枚举值会污染外部作用域
- 不同枚举的值可能重名冲突
- 可以隐式转换为int
enum class的解决方案
典型示例:
// enum class(安全)
enum class Color { Red, Green, Blue };
enum class Status { Red, Ok }; // ✅ OK,不冲突// 必须用作用域限定符
Color c = Color::Red;
Status s = Status::Red;// 不能隐式转int
int x = Color::Red; // ❌ 编译错误
int y = static_cast<int>(Color::Red); // ✅ 显式转换
enum class特点对比
特性 | enum | enum class |
---|---|---|
作用域污染 | ✅ 会污染 | ❌ 不会 |
名字冲突 | ✅ 可能冲突 | ❌ 不会冲突 |
隐式转int | ✅ 可以 | ❌ 不能 |
类型安全 | ❌ 较弱 | ✅ 强类型 |
我的理解:enum class更安全,防止命名冲突,是C++11推荐的写法。
知识点7:delete和default
delete禁止函数
示例:禁止拷贝
class NoCopy {
public:NoCopy() = default; // 默认构造// 禁止拷贝NoCopy(const NoCopy&) = delete;NoCopy& operator=(const NoCopy&) = delete;
};NoCopy obj1;
NoCopy obj2 = obj1; // ❌ 编译错误:拷贝构造被删除
示例:禁止特定参数
class OnlyInt {
public:void process(int x) { cout << "处理int: " << x << endl;}// 禁止double调用void process(double) = delete;
};OnlyInt obj;
obj.process(42); // ✅ OK
obj.process(3.14); // ❌ 编译错误:函数被删除
default使用默认实现
典型示例:
class MyClass {
public:MyClass() = default; // 使用编译器生成的默认构造~MyClass() = default; // 使用编译器生成的析构MyClass(const MyClass&) = default; // 默认拷贝构造MyClass& operator=(const MyClass&) = default; // 默认拷贝赋值
};
Rule of 0/3/5
核心规则:
- Rule of 0:如果不需要管理资源,全部用
= default
- Rule of 3:如果定义了析构/拷贝构造/拷贝赋值,就要三个都定义
- Rule of 5:C++11加上移动构造和移动赋值,一共5个
推荐策略:
// ✅ Rule of 0:让编译器生成
class Simple {
public:Simple() = default;~Simple() = default;Simple(const Simple&) = default;Simple& operator=(const Simple&) = default;
};// ✅ Rule of 5:自己管理资源
class Resource {
public:~Resource() { delete[] data; }Resource(const Resource&) { /* 深拷贝 */ }Resource& operator=(const Resource&) { /* 拷贝赋值 */ }Resource(Resource&&) noexcept { /* 移动构造 */ }Resource& operator=(Resource&&) noexcept { /* 移动赋值 */ }
private:int* data;
};
学习中的疑问6:构造函数可以多态吗?
疑问:构造函数可以是虚函数吗?可以多态吗?
解答:
- 不能是虚函数:构造时虚函数表还未完全建立
- 可以重载:不同参数的构造函数
- 推荐策略:能用
= default
就用,需要特殊逻辑才自定义
例子:
class Base {
public:Base() = default; // 默认构造Base(int x) { } // 重载构造virtual ~Base() { } // 虚析构(可以)
};
知识点8:using别名
using vs typedef
语法对比:
// typedef(旧)
typedef std::vector<int> IntVec;
typedef int (*FuncPtr)(int, int); // 函数指针(难读)// using(新,推荐)
using IntVec = std::vector<int>;
using FuncPtr = int (*)(int, int); // 更直观
using的模板别名
重要优势:typedef不支持模板别名,using可以!
// ✅ using支持模板
template<typename T>
using Vec = std::vector<T>;Vec<int> v1; // std::vector<int>
Vec<string> v2; // std::vector<string>// ❌ typedef做不到
// typedef std::vector<T> Vec<T>; // 编译错误
实用的别名
典型示例:
// 简化智能指针
template<typename T>
using SharedPtr = std::shared_ptr<T>;template<typename T>
using UniquePtr = std::unique_ptr<T>;// 简化容器
template<typename T>
using Vector = std::vector<T>;template<typename K, typename V>
using HashMap = std::unordered_map<K, V>;// 函数类型别名
using UpdateFunction = std::function<void(float)>;
using EventCallback = std::function<bool(const Event&)>;
我的理解:using比typedef语法更清晰,而且支持模板,是现代C++的标准写法。
using语法对比图
学习中的疑问7:std::function和bind
疑问:std::function是什么?bind又是什么?
解答:
std::function
存储可调用对象的通用容器:
#include <functional>// 存储普通函数
int add(int a, int b) { return a + b; }
std::function<int(int, int)> f1 = add;// 存储lambda
std::function<int(int, int)> f2 = [](int a, int b) { return a + b; };// 存储成员函数
class Calculator {
public:int multiply(int a, int b) { return a * b; }
};
Calculator calc;
std::function<int(int, int)> f3 = std::bind(&Calculator::multiply, &calc, std::placeholders::_1, std::placeholders::_2);
std::bind(现在少用)
绑定函数参数:
auto add_five = std::bind(add, std::placeholders::_1, 5);
int result = add_five(10); // 等于add(10, 5) = 15
建议:现代C++推荐用lambda代替bind,更清晰。
学习收获(完整课时04)
上半部分收获:
- auto简化代码,decltype保留完整类型
- 列表初始化更安全,防止窄化转换
- 移动语义避免深拷贝,性能提升明显
- lambda配合STL算法,代码更简洁
下半部分收获:
- nullptr比NULL更安全,避免重载歧义
- enum class强类型,防止命名冲突
- delete禁止函数,default使用默认实现
- using别名比typedef更清晰,支持模板
总体感受:
C++11这些新特性让代码更安全、更简洁、性能更好,是现代C++的基础。特别是移动语义和lambda表达式,对性能提升很明显。
课时04对应的面试题
Q1: 什么是C++中的auto和decltype?
A: auto让编译器自动推导变量类型,会丢失const和引用;decltype保留完整类型信息,主要用于模板编程。
Q2: 什么是C++的移动语义和完美转发?
A: 移动语义通过右值引用(&&
)和移动构造函数,实现资源转移而不是拷贝,避免深拷贝开销。std::move将左值转为右值引用。
Q3: C++中move有什么作用?
A: std::move是类型转换函数,将左值强制转换为右值引用,启用移动语义。它本身不移动任何东西,只是类型转换。
Q4: C++的function、bind、lambda都在什么场景下会用到?
A:
- lambda:STL算法谓词、短小回调、局部函数
- function:存储不同类型可调用对象、回调接口
- bind:参数绑定(现在推荐用lambda代替)
Q5: C++中为什么要使用nullptr而不是NULL?
A: nullptr是真正的空指针类型,避免重载函数调用歧义。NULL本质是0,可能导致func(int)
和func(int*)
重载时选择错误。
Q6: C++中enum和enum class的区别?
A:
- enum:枚举值污染外部作用域,可能命名冲突,隐式转int
- enum class:强类型枚举,不污染作用域,必须用
::
访问,不能隐式转int
💡 下一步:明天学习课时05(内存管理与智能指针),掌握unique_ptr、shared_ptr、weak_ptr的使用。
📝 本篇整理完成 - C++11新特性知识体系(下)