【C++】C++11 知识点梳理(中)

【C++】C++11 知识点梳理(中)

一、智能指针

C++11 引入了智能指针,用于自动管理动态分配的内存,避免内存泄漏。

1.1 unique_ptr

unique_ptr独占所指向的对象,不支持拷贝,但支持移动语义。

#include <memory> #include <iostream> int main() { // 创建一个 unique_ptr,管理一个 int std::unique_ptr<int> p1(new int(42)); std::cout << *p1 << std::endl; // 输出 42 // 移动语义转移所有权 std::unique_ptr&lt;int&gt; p2 = std::move(p1); // 此时 p1 为空,p2 拥有对象 if (!p1) { std::cout &lt;&lt; "p1 is now empty" &lt;&lt; std::endl; } // 使用 make_unique (C++14 引入,但常与 C++11 一起讨论) auto p3 = std::make_unique&lt;int&gt;(100); return 0; }

1.2 shared_ptr

shared_ptr通过引用计数实现共享所有权,当最后一个shared_ptr被销毁时,对象被释放。

#include <memory> #include <iostream> class MyClass { public: MyClass() { std::cout << "MyClass constructed\n"; } ~MyClass() { std::cout << "MyClass destroyed\n"; } void sayHello() { std::cout << "Hello from MyClass\n"; } }; int main() { std::shared_ptr<MyClass> sp1(new MyClass()); { std::shared_ptr<MyClass> sp2 = sp1; // 引用计数+1 sp2->sayHello(); } // sp2 离开作用域,引用计数-1 // sp1 仍然有效 sp1->sayHello(); return 0; } // sp1 离开作用域,引用计数归零,对象被销毁

1.3 weak_ptr

weak_ptrshared_ptr的弱引用,不增加引用计数,用于解决循环引用问题。

#include <memory> #include <iostream> struct Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // 使用 weak_ptr 避免循环引用 int value; Node(int v) : value(v) {} }; int main() { auto node1 = std::make_shared<Node>(1); auto node2 = std::make_shared<Node>(2); node1-&gt;next = node2; node2-&gt;prev = node1; // 弱引用,不会增加 node1 的引用计数 // 通过 weak_ptr 访问对象 if (auto sp = node2-&gt;prev.lock()) { std::cout &lt;&lt; "Previous node value: " &lt;&lt; sp-&gt;value &lt;&lt; std::endl; } else { std::cout &lt;&lt; "Object has been destroyed\n"; } return 0; }

二、右值引用与移动语义

右值引用(T&&)和移动语义是 C++11 性能优化的核心,允许资源(如动态内存)的“转移”而非“拷贝”。

2.1 左值、右值与右值引用

  • 左值 (lvalue):有名字、有地址的表达式。
  • 右值 (rvalue):临时对象、字面量(除字符串字面量)、返回非引用的函数调用。
  • 右值引用:绑定到右值的引用,用&&声明。
int a = 10; // a 是左值 int& lref = a; // 左值引用 // int& rref = 20; // 错误:不能将左值引用绑定到右值 int&& rref = 20; // 右值引用绑定到字面量 int&& rref2 = std::move(a); // 通过 std::move 将左值转换为右值引用

2.2 移动构造函数与移动赋值运算符

定义了移动语义的类可以高效转移资源。

#include <utility> // for std::move #include <cstring> class String { public: char* data; size_t length; // 构造函数 String(const char* str) { length = strlen(str); data = new char[length + 1]; strcpy(data, str); std::cout &lt;&lt; "Constructor\n"; } // 拷贝构造函数(深拷贝) String(const String&amp; other) { length = other.length; data = new char[length + 1]; strcpy(data, other.data); std::cout &lt;&lt; "Copy Constructor\n"; } // 移动构造函数(资源转移) String(String&amp;&amp; other) noexcept { data = other.data; length = other.length; other.data = nullptr; // 置空原指针,防止重复释放 other.length = 0; std::cout &lt;&lt; "Move Constructor\n"; } // 析构函数 ~String() { delete[] data; } }; int main() { String s1("Hello"); String s2 = s1; // 调用拷贝构造函数 String s3 = std::move(s1); // 调用移动构造函数,s1 的资源被转移 return 0; }

2.3 std::move 与 std::forward

  • std::move:无条件将参数转换为右值引用,用于启动移动语义。
  • std::forward:完美转发,保持参数原有的值类别(左值/右值),用于模板编程。
#include <utility> #include <iostream> void process(int& x) { std::cout << "lvalue: " << x << std::endl; } void process(int&& x) { std::cout << "rvalue: " << x << std::endl; } template<typename T> void relay(T&& arg) { // 使用 std::forward 保持 arg 的原始值类别 process(std::forward<T>(arg)); } int main() { int a = 10; relay(a); // 传递左值,调用 process(int&) relay(20); // 传递右值,调用 process(int&&) return 0; }

三、Lambda 表达式

Lambda 表达式提供了一种简洁的定义匿名函数对象的方式。

3.1 基本语法

[capture] (parameters) -> return_type { body }
  • capture:捕获列表,指定哪些外部变量在 lambda 体内可用。
  • parameters:参数列表(可选)。
  • return_type:返回类型(可选,可自动推导)。
  • body:函数体。

3.2 捕获方式

#include <iostream> #include <vector> #include <algorithm> int main() { int x = 10; int y = 20; // 值捕获 auto lambda1 = [x]() { return x; }; // 捕获 x 的副本 // 引用捕获 auto lambda2 = [&amp;y]() { return ++y; }; // 捕获 y 的引用 // 隐式值捕获所有变量 auto lambda3 = [=]() { return x + y; }; // 捕获所有外部变量的副本 // 隐式引用捕获所有变量 auto lambda4 = [&amp;]() { return ++x + ++y; }; // 捕获所有外部变量的引用 // 混合捕获 auto lambda5 = [x, &amp;y]() { return x + y; }; // x 值捕获,y 引用捕获 std::cout &lt;&lt; lambda1() &lt;&lt; std::endl; // 10 std::cout &lt;&lt; lambda2() &lt;&lt; std::endl; // 21, y 变为 21 std::cout &lt;&lt; y &lt;&lt; std::endl; // 21 // 在算法中使用 lambda std::vector&lt;int&gt; vec = {1, 2, 3, 4, 5}; int threshold = 3; // 捕获 threshold 用于比较 auto it = std::find_if(vec.begin(), vec.end(), [threshold](int val) { return val &gt; threshold; }); if (it != vec.end()) { std::cout &lt;&lt; "First element &gt; " &lt;&lt; threshold &lt;&lt; " is " &lt;&lt; *it &lt;&lt; std::endl; } return 0; }

3.3 泛型 Lambda (C++14) 与 mutable

#include <iostream> int main() { // mutable 允许修改按值捕获的变量(修改的是副本) int count = 0; auto counter = count mutable { return ++count; // 修改的是捕获的副本,不影响外部的 count }; std::cout << counter() << std::endl; // 1 std::cout << counter() << std::endl; // 2 std::cout << "Original count: " << count << std::endl; // 0 // C++14 泛型 Lambda(使用 auto 参数) auto add = [](auto a, auto b) { return a + b; }; std::cout &lt;&lt; add(1, 2) &lt;&lt; std::endl; // 3 std::cout &lt;&lt; add(1.5, 2.3) &lt;&lt; std::endl; // 3.8 return 0; }