从类的功能增强到 STL 的全面升级,从 lambda 表达式的灵活运用到包装器的统一封装,C++11 彻底改变了 C++ 的编程范式。本文将聚焦 C++11 中类功能扩展、STL 变化、lambda 表达式和包装器四大核心模块,结合实例代码深入剖析,带你全方位掌握这些改变 C++ 编程体验的关键特性。下面就让我们正式开始吧!
C++11 对类的功能进行了大幅扩展,新增了移动语义相关的默认成员函数,优化了成员变量初始化方式,提供了更精细的默认函数控制手段,同时完善了继承体系中的类型控制。这些特性让类的设计更加灵活,代码编写更加简洁高效。
在 C++11 之前,类的默认成员函数有 6 个:构造函数、析构函数、拷贝构造函数、拷贝赋值运算符重载、取地址运算符重载和 const 取地址运算符重载。其中前 4 个是核心,后两个实际使用场景有限。但在处理大量数据拷贝的场景中,传统的拷贝构造和拷贝赋值会带来巨大的性能开销 —— 比如当我们用一个临时对象初始化另一个对象时,拷贝构造会执行深拷贝,重复分配内存并复制数据,而临时对象用完后又会被析构,造成资源的浪费。
C++11 引入了移动语义,新增了两个默认成员函数:移动构造函数和移动赋值运算符重载,专门用于处理临时对象的资源转移,避免不必要的拷贝。
编译器生成默认移动构造和移动赋值的条件非常明确,这是使用该特性的关键:
#include <iostream>
#include <string>
using namespace std;
// 自定义字符串类,模拟深拷贝
namespace bit {
class string {
public:
// 构造函数
string(const char* str = "")
: _size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
cout << "string(const char* str) -- 构造" << endl;
}
// 拷贝构造(深拷贝)
string(const string& s)
: _size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
cout << "string(const string& s) -- 拷贝构造(深拷贝)" << endl;
}
// 拷贝赋值(深拷贝)
string& operator=(const string& s) {
cout << "string& operator=(const string& s) -- 拷贝赋值(深拷贝)" << endl;
if (this != &s) {
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
// 析构函数
~string() {
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
cout << "~string() -- 析构" << endl;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
};
}
// 测试类:未手动实现拷贝相关函数,编译器会生成默认移动语义
class Person {
public:
Person(const char* name = "", int age = 0)
: _name(name)
, _age(age)
{
cout << "Person(const char* name, int age) -- 构造" << endl;
}
// 未实现析构、拷贝构造、拷贝赋值,编译器会生成默认移动构造和移动赋值
private:
bit::string _name;
int _age;
};
int main() {
Person p1("张三", 20);
// 移动构造:p2窃取p1的资源(p1变为无效状态)
Person p2 = move(p1);
Person p3("李四", 25);
// 移动赋值:p3窃取p2的资源
p3 = move(p2);
return 0;
}移动构造和移动赋值的本质是 “资源窃取” 而非 “资源拷贝”。当操作临时对象(右值)时,移动语义会直接接管临时对象的内存资源,避免了深拷贝的开销。例如,在 STL 容器中插入临时对象时,容器会调用元素的移动构造,大幅提升插入效率。
C++11 允许在类的成员变量声明时直接指定缺省值,这个缺省值会作为成员变量的默认初始化值,供初始化列表使用。如果在初始化列表中显式初始化了成员变量,则使用显式指定的值;否则使用声明时的缺省值。
#include <iostream>
#include <string>
using namespace std;
class Student {
public:
// 构造函数:未显式初始化的成员变量使用声明时的缺省值
Student(const string& name)
: _name(name)
{
// _age默认值为18,_score默认值为0
}
// 构造函数:显式初始化部分成员变量
Student(const string& name, int age)
: _name(name)
, _age(age)
{
// _score默认值为0
}
void Print() {
cout << "姓名:" << _name << ",年龄:" << _age << ",成绩:" << _score << endl;
}
private:
string _name; // 无缺省值,必须在初始化列表中显式初始化
int _age = 18; // 缺省值18
double _score = 0.0; // 缺省值0.0
};
int main() {
Student s1("张三");
s1.Print(); // 输出:姓名:张三,年龄:18,成绩:0
Student s2("李四", 20);
s2.Print(); // 输出:姓名:李四,年龄:20,成绩:0
return 0;
} C++11 提供了default和delete关键字,让开发者能够更精细地控制默认成员函数的生成,解决了 C++98 中默认函数控制不便的问题。
当我们手动实现了某些默认函数(如拷贝构造),编译器就不会自动生成移动构造和移动赋值。如果我们需要保留默认的移动语义,可以使用default显式要求编译器生成。
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
Person(const char* name = "", int age = 0)
: _name(name)
, _age(age)
{}
// 手动实现拷贝构造
Person(const Person& p)
: _name(p._name)
, _age(p._age)
{
cout << "Person(const Person& p) -- 拷贝构造" << endl;
}
// 显式要求编译器生成默认移动构造
Person(Person&& p) = default;
// 显式要求编译器生成默认移动赋值
Person& operator=(Person&& p) = default;
private:
string _name;
int _age;
};
int main() {
Person p1("张三", 20);
Person p2 = move(p1); // 调用默认移动构造
Person p3("李四", 25);
p3 = move(p2); // 调用默认移动赋值
return 0;
} 在 C++98 中,要禁止某个默认函数(如拷贝构造),需要将其声明为 private 且不实现。C++11 中只需在函数声明后加上delete,即可禁止编译器生成该函数的默认版本。
#include <iostream>
#include <string>
using namespace std;
class NonCopyable {
public:
NonCopyable() = default; // 显式生成默认构造
~NonCopyable() = default; // 显式生成默认析构
// 禁止拷贝构造
NonCopyable(const NonCopyable&) = delete;
// 禁止拷贝赋值
NonCopyable& operator=(const NonCopyable&) = delete;
};
int main() {
NonCopyable nc1;
// NonCopyable nc2 = nc1; // 编译报错:拷贝构造已被删除
// NonCopyable nc3;
// nc3 = nc1; // 编译报错:拷贝赋值已被删除
return 0;
}default:适用于需要保留默认语义,但因手动实现其他函数导致编译器不自动生成的场景。delete:适用于单例模式、禁止拷贝的对象(如文件句柄、网络连接)等场景,避免意外拷贝导致的资源问题。 在继承和多态中,final和override关键字为开发者提供了更严格的类型控制,避免了因继承关系复杂导致的错误。
#include <iostream>
using namespace std;
// final修饰类:Base类不能被继承
class Base final {
public:
virtual void Show() {
cout << "Base::Show()" << endl;
}
};
// class Derived : public Base { // 编译报错:Base是final类,不能被继承
// public:
// void Show() override {
// cout << "Derived::Show()" << endl;
// }
// };
class Base2 {
public:
// final修饰虚函数:Show不能被重写
virtual void Show() final {
cout << "Base2::Show()" << endl;
}
};
class Derived2 : public Base2 {
public:
// void Show() override { // 编译报错:Show是final函数,不能被重写
// cout << "Derived2::Show()" << endl;
// }
};
int main() {
Base b;
b.Show();
Base2 b2;
b2.Show();
return 0;
} override用于派生类的虚函数声明,表示该函数是重写基类的虚函数。编译器会检查基类是否存在对应的虚函数,若不存在或签名不匹配,则编译报错,避免因拼写错误、参数不匹配等导致的重写失败。
#include <iostream>
using namespace std;
class Base {
public:
virtual void Show(int a) {
cout << "Base::Show(int a) -- a = " << a << endl;
}
virtual void Display() {
cout << "Base::Display()" << endl;
}
};
class Derived : public Base {
public:
// 正确重写:签名与基类一致,加上override进行检查
void Show(int a) override {
cout << "Derived::Show(int a) -- a = " << a << endl;
}
// void Show(double a) override { // 编译报错:基类无对应的虚函数
// cout << "Derived::Show(double a) -- a = " << a << endl;
// }
// void DisPlay() override { // 编译报错:拼写错误,基类是Display
// cout << "Derived::DisPlay()" << endl;
// }
};
int main() {
Base* pb = new Derived();
pb->Show(10); // 调用派生类重写的Show
pb->Display(); // 调用基类的Display
delete pb;
return 0;
}final:防止不必要的继承,避免基类被意外扩展,同时保护核心虚函数的实现不被修改。override:强制编译器检查重写的正确性,减少因人为失误导致的多态失效问题,提高代码的健壮性。C++11 对 STL 进行了全面升级,不仅新增了实用的容器,还为现有容器添加了大量新接口,优化了容器的性能和使用体验。这些变化让 STL 更加强大,能够满足更多场景的需求。
C++11 新增了 4 个容器:std::array、std::forward_list、std::unordered_map和std::unordered_set。其中,std::unordered_map和std::unordered_set是最常用的,而std::array和std::forward_list则针对特定场景提供了更高效的选择。

std::array是固定大小的数组容器,结合了 C 风格数组的高效性和 STL 容器的易用性。与 C 风格数组相比,std::array提供了迭代器、大小查询、边界检查等安全特性;与std::vector相比,std::array无需动态分配内存,性能更优,但大小固定,不能动态扩展。
#include <iostream>
#include <array>
#include <algorithm>
using namespace std;
int main() {
// 初始化:指定大小为5,初始值为1,2,3,4,5
array<int, 5> arr = {1, 2, 3, 4, 5};
// 访问元素:支持[]和at(),at()会做边界检查
cout << "arr[2] = " << arr[2] << endl;
cout << "arr.at(3) = " << arr.at(3) << endl;
// arr.at(10) = 10; // 抛出out_of_range异常
// 大小相关操作
cout << "数组大小:" << arr.size() << endl;
cout << "是否为空:" << (arr.empty() ? "是" : "否") << endl;
cout << "最大容量:" << arr.max_size() << endl; // 与size()相同,固定大小
// 遍历:支持范围for、迭代器
cout << "范围for遍历:";
for (auto& val : arr) {
cout << val << " ";
}
cout << endl;
cout << "迭代器遍历:";
for (auto it = arr.begin(); it != arr.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 算法支持
sort(arr.rbegin(), arr.rend()); // 逆序排序
cout << "逆序排序后:";
for (auto& val : arr) {
cout << val << " ";
}
cout << endl;
return 0;
} std::forward_list是单向链表容器,仅支持单向遍历,相比std::list(双向链表),它占用内存更少,插入和删除操作更高效(尤其是在链表头部)。适用于只需单向遍历、频繁插入删除的场景。
#include <iostream>
#include <forward_list>
using namespace std;
int main() {
forward_list<int> flist = {1, 2, 3, 4, 5};
// 头部插入
flist.push_front(0);
// 头部删除
flist.pop_front();
// 插入到指定位置(需要通过迭代器)
auto it = flist.begin();
advance(it, 2); // 移动迭代器到第3个元素(索引2)
flist.insert_after(it, 30); // 在it之后插入30
// 遍历
cout << "forward_list遍历:";
for (auto& val : flist) {
cout << val << " ";
}
cout << endl;
// 删除指定位置的元素
it = flist.begin();
advance(it, 3);
flist.erase_after(it); // 删除it之后的元素
cout << "删除元素后:";
for (auto& val : flist) {
cout << val << " ";
}
cout << endl;
return 0;
} std::unordered_map和std::unordered_set是基于哈希表实现的关联容器,与基于红黑树的std::map和std::set相比,它们的插入、查找、删除操作的平均时间复杂度为 O (1),在大数据量场景下性能更优。但它们不保证元素的有序性,且占用内存略多。
#include <iostream>
#include <unordered_map>
#include <unordered_set>
using namespace std;
int main() {
// std::unordered_map示例
unordered_map<string, int> dict;
// 插入元素
dict["apple"] = 10;
dict["banana"] = 20;
dict.insert(make_pair("orange", 15));
// 查找元素
auto it = dict.find("apple");
if (it != dict.end()) {
cout << "apple的价格:" << it->second << endl;
}
// 遍历(无序)
cout << "unordered_map遍历:" << endl;
for (auto& pair : dict) {
cout << pair.first << " : " << pair.second << endl;
}
// std::unordered_set示例
unordered_set<int> uset = {1, 2, 3, 4, 5};
// 插入元素(不允许重复)
uset.insert(3); // 插入失败,3已存在
// 查找元素
if (uset.count(4)) {
cout << "4在unordered_set中" << endl;
}
// 遍历(无序)
cout << "unordered_set遍历:";
for (auto& val : uset) {
cout << val << " ";
}
cout << endl;
return 0;
} C++11 为现有容器(如vector、list、map等)添加了大量新接口,主要包括移动语义相关接口、initializer_list构造函数、emplace系列接口等,大幅提升了容器的使用效率和灵活性。
所有容器都新增了移动构造函数和移动赋值运算符重载,支持直接用右值(临时对象)初始化或赋值容器,避免了不必要的拷贝。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> v1 = {"a", "b", "c"};
// 移动构造:v2窃取v1的资源,v1变为空
vector<string> v2 = move(v1);
cout << "v2的大小:" << v2.size() << endl; // 输出3
cout << "v1的大小:" << v1.size() << endl; // 输出0
vector<string> v3 = {"d", "e"};
// 移动赋值:v3窃取v2的资源,v2变为空
v3 = move(v2);
cout << "v3的大小:" << v3.size() << endl; // 输出3
cout << "v2的大小:" << v2.size() << endl; // 输出0
return 0;
} 此外,push_back、insert等插入接口也新增了右值引用版本,当插入临时对象时,会调用元素的移动构造,提升插入效率。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> v;
string s = "hello";
v.push_back(s); // 左值:调用拷贝构造
v.push_back(move(s)); // 右值:调用移动构造
v.push_back("world"); // 临时对象:调用移动构造
for (auto& str : v) {
cout << str << " ";
}
cout << endl;
return 0;
} 所有容器都新增了std::initializer_list类型的构造函数,支持直接用{}初始化容器,语法更简洁。
#include <iostream>
#include <vector>
#include <list>
#include <map>
using namespace std;
int main() {
// vector初始化
vector<int> v = {1, 2, 3, 4, 5};
// list初始化
list<string> lst = {"apple", "banana", "orange"};
// map初始化
map<int, string> mp = {{1, "one"}, {2, "two"}, {3, "three"}};
// 输出vector
cout << "vector:";
for (auto& val : v) {
cout << val << " ";
}
cout << endl;
// 输出list
cout << "list:";
for (auto& str : lst) {
cout << str << " ";
}
cout << endl;
// 输出map
cout << "map:";
for (auto& pair : mp) {
cout << pair.first << ":" << pair.second << " ";
}
cout << endl;
return 0;
} C++11 新增了emplace_back、emplace等接口,它们支持直接在容器中构造元素,无需先创建临时对象,相比push_back、insert更高效。emplace系列接口基于可变参数模板实现,能够接收元素构造所需的参数,直接在容器的内存空间中构造元素。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Person {
public:
Person(const string& name, int age)
: _name(name)
, _age(age)
{
cout << "Person(const string& name, int age) -- 构造" << endl;
}
Person(string&& name, int age)
: _name(move(name))
, _age(age)
{
cout << "Person(string&& name, int age) -- 移动构造" << endl;
}
private:
string _name;
int _age;
};
int main() {
vector<Person> v;
// push_back:先创建临时对象,再移动构造到容器中
cout << "push_back临时对象:" << endl;
v.push_back(Person("张三", 20));
// emplace_back:直接在容器中构造对象,无临时对象
cout << "\nemplace_back直接构造:" << endl;
v.emplace_back("李四", 25); // 传递构造参数,直接构造
return 0;
}运行结果:
push_back临时对象:
Person(const string& name, int age) -- 构造
Person(string&& name, int age) -- 移动构造
emplace_back直接构造:
Person(const string& name, int age) -- 构造 可以看到,emplace_back直接在容器中构造元素,避免了临时对象的创建和移动,效率更高。
const迭代器,确保不能通过迭代器修改容器元素,提高代码的安全性。std::make_move_iterator可以将普通迭代器转换为移动迭代器,在拷贝容器元素时调用移动构造,提升效率。#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
vector<string> v1 = {"a", "b", "c"};
vector<string> v2;
// 使用移动迭代器,将v1的元素移动到v2
v2.insert(v2.begin(), make_move_iterator(v1.begin()), make_move_iterator(v1.end()));
cout << "v2的元素:";
for (auto& str : v2) {
cout << str << " ";
}
cout << endl;
cout << "v1的大小:" << v1.size() << endl; // v1变为空
return 0;
}lambda 表达式是 C++11 引入的核心特性之一,它允许在函数内部定义匿名函数对象,无需单独声明函数或仿函数类,让代码更简洁、更具可读性。lambda 表达式广泛应用于算法、线程、回调函数等场景,大幅提升了编程效率。
lambda 表达式的核心语法格式如下:
[capture-list] (parameters) -> return-type { function-body }各部分说明:
[])。()(连同括号一起省略)。return语句,或无返回值,可省略该部分,编译器会自动推导返回值类型。{})。#include <iostream>
using namespace std;
int main() {
// 1. 完整写法:有参数、有返回值
auto add = [](int x, int y) -> int {
return x + y;
};
cout << "add(1, 2) = " << add(1, 2) << endl; // 输出3
// 2. 省略返回值类型(编译器自动推导)
auto multiply = [](int x, int y) {
return x * y;
};
cout << "multiply(3, 4) = " << multiply(3, 4) << endl; // 输出12
// 3. 无参数、无返回值
auto printHello = [] {
cout << "Hello, lambda!" << endl;
};
printHello(); // 输出Hello, lambda!
// 4. 有参数、无返回值
auto printNum = [](int num) {
cout << "num = " << num << endl;
};
printNum(10); // 输出num = 10
return 0;
}捕捉列表是 lambda 表达式的核心特性之一,用于控制 lambda 函数对外部变量的访问权限。捕捉方式分为值捕捉、引用捕捉、隐式捕捉和混合捕捉,满足不同场景的需求。
const修饰)。[=, &var]表示其他变量隐式值捕捉,var显式引用捕捉;[&, var]表示其他变量隐式引用捕捉,var显式值捕捉。const限制,允许在 lambda 函数体中修改值捕捉的变量,但修改的是拷贝后的变量,不影响外部变量。使用mutable后,参数列表()不能省略(即使无参数)。#include <iostream>
using namespace std;
int g_val = 100; // 全局变量
int main() {
int a = 10, b = 20, c = 30, d = 40;
static int s_val = 200; // 静态局部变量
// 1. 值捕捉:a、b的值拷贝到lambda内部
auto func1 = [a, b] {
// a++; // 编译报错:值捕捉的变量默认是const的
cout << "func1: a = " << a << ", b = " << b << endl;
};
func1();
// 2. 引用捕捉:&c、&d是引用,修改会影响外部变量
auto func2 = [&c, &d] {
c++;
d++;
cout << "func2: c = " << c << ", d = " << d << endl;
};
func2();
cout << "外部:c = " << c << ", d = " << d << endl; // c=31, d=41
// 3. 隐式值捕捉:自动捕捉使用的a、b
auto func3 = [=] {
cout << "func3: a = " << a << ", b = " << b << endl;
};
func3();
// 4. 隐式引用捕捉:自动捕捉使用的c、d
auto func4 = [&] {
c++;
d++;
cout << "func4: c = " << c << ", d = " << d << endl;
};
func4();
cout << "外部:c = " << c << ", d = " << d << endl; // c=32, d=42
// 5. 混合捕捉:[=, &a] 隐式值捕捉其他变量,显式引用捕捉a
auto func5 = [=, &a] {
a++;
// b++; // 编译报错:b是值捕捉,不能修改
cout << "func5: a = " << a << ", b = " << b << endl;
};
func5();
cout << "外部:a = " << a << endl; // a=11
// 6. mutable:取消值捕捉变量的const限制
auto func6 = [a, b]() mutable {
a++;
b++;
cout << "func6: a = " << a << ", b = " << b << endl; // a=11, b=21
};
func6();
cout << "外部:a = " << a << ", b = " << b << endl; // a=11, b=20(外部变量未变)
// 7. 全局变量和静态局部变量无需捕捉,可直接使用
auto func7 = [] {
g_val++;
s_val++;
cout << "func7: g_val = " << g_val << ", s_val = " << s_val << endl;
};
func7();
return 0;
}=或&,且[&]后只能跟值捕捉变量,[=]后只能跟引用捕捉变量。lambda 表达式的最大价值在于简化代码,尤其适用于需要临时定义短小函数的场景。以下是几个典型应用场景:
STL 算法(如sort、find_if、for_each等)通常需要传入函数指针或仿函数作为参数,使用 lambda 表达式可以直接在算法调用处定义逻辑,无需单独声明函数或仿函数类。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
// 商品类
struct Goods {
string _name; // 商品名称
double _price; // 价格
int _evaluate; // 评价数
Goods(const char* name, double price, int evaluate)
: _name(name)
, _price(price)
, _evaluate(evaluate)
{}
};
int main() {
vector<Goods> goods = {
{"苹果", 2.1, 500},
{"香蕉", 3.0, 300},
{"橙子", 2.2, 400},
{"菠萝", 1.5, 200}
};
// 1. 按价格升序排序
sort(goods.begin(), goods.end(), [](const Goods& g1, const Goods& g2) {
return g1._price < g2._price;
});
cout << "按价格升序排序:" << endl;
for (auto& g : goods) {
cout << g._name << " 价格:" << g._price << endl;
}
// 2. 按评价数降序排序
sort(goods.begin(), goods.end(), [](const Goods& g1, const Goods& g2) {
return g1._evaluate > g2._evaluate;
});
cout << "\n按评价数降序排序:" << endl;
for (auto& g : goods) {
cout << g._name << " 评价数:" << g._evaluate << endl;
}
// 3. 查找价格小于2.5的商品
auto it = find_if(goods.begin(), goods.end(), [](const Goods& g) {
return g._price < 2.5;
});
if (it != goods.end()) {
cout << "\n第一个价格小于2.5的商品:" << it->_name << endl;
}
// 4. 遍历并修改商品价格(涨价10%)
for_each(goods.begin(), goods.end(), [](Goods& g) {
g._price *= 1.1;
});
cout << "\n涨价10%后:" << endl;
for (auto& g : goods) {
cout << g._name << " 新价格:" << g._price << endl;
}
return 0;
}在多线程编程中,lambda 表达式可以直接作为线程的执行函数,无需单独定义线程函数,代码更简洁。
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
int main() {
int count = 0;
// 线程1:计数+1,1秒一次
thread t1([&count] {
while (count < 5) {
count++;
cout << "t1: count = " << count << endl;
this_thread::sleep_for(chrono::seconds(1));
}
});
// 线程2:计数-1,1秒一次
thread t2([&count] {
while (count > 0) {
count--;
cout << "t2: count = " << count << endl;
this_thread::sleep_for(chrono::seconds(1));
}
});
t1.join();
t2.join();
return 0;
}在需要自定义排序的场景中,lambda 表达式可以快速定义排序逻辑,相比仿函数更灵活。
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
// 自定义set的排序规则:按字符串长度降序
set<string, function<bool(const string&, const string&)>> s(
[](const string& s1, const string& s2) {
return s1.size() > s2.size();
}
);
s.insert("apple");
s.insert("banana");
s.insert("orange");
s.insert("pear");
cout << "按字符串长度降序排列:" << endl;
for (auto& str : s) {
cout << str << "(长度:" << str.size() << ")" << endl;
}
return 0;
}lambda 表达式的本质是编译器自动生成的一个匿名仿函数类(也叫函数对象)。当我们定义一个 lambda 表达式时,编译器会:
main::lambda_1)。operator()成员函数的参数、返回值类型、函数体。operator()成员函数。#include <iostream>
using namespace std;
// 仿函数类
class Rate {
public:
Rate(double rate) : _rate(rate) {}
double operator()(double money, int year) const {
return money * _rate * year;
}
private:
double _rate;
};
int main() {
double rate = 0.049;
// 1. 使用仿函数
Rate r1(rate);
double interest1 = r1(10000, 2);
cout << "仿函数计算利息:" << interest1 << endl;
// 2. 使用lambda表达式
auto r2 = [rate](double money, int year) {
return money * rate * year;
};
double interest2 = r2(10000, 2);
cout << "lambda计算利息:" << interest2 << endl;
return 0;
} 从功能上看,lambda 表达式r2和仿函数r1完全等价。通过反汇编可以看到,lambda 表达式的底层实现与仿函数一致:
rate被作为 lambda 生成的仿函数类的成员变量。operator()。 当 lambda 使用mutable关键字时,本质是取消了仿函数类operator()的const修饰,允许修改成员变量(值捕捉的拷贝)。
// lambda表达式
auto func = [a]() mutable { a++; };
// 底层生成的仿函数类(简化)
class LambdaClass {
public:
LambdaClass(int a) : _a(a) {}
void operator()() { // 无const修饰,允许修改_a
_a++;
}
private:
int _a;
}; 若不使用mutable,operator()会被const修饰,无法修改成员变量:
class LambdaClass {
public:
LambdaClass(int a) : _a(a) {}
void operator()() const { // const修饰,不能修改_a
// _a++; // 编译报错
}
private:
int _a;
}; C++ 中的可调用对象包括函数指针、仿函数、lambda 表达式、类成员函数等,它们的类型各不相同,使用起来不够统一。于是C++11 引入的包装器(std::function和std::bind)解决了这一问题,能够将不同类型的可调用对象包装成统一的类型,方便存储、传递和使用。
std::function是一个类模板,定义在<functional>头文件中,用于包装各种可调用对象,包括函数指针、仿函数、lambda 表达式、类成员函数等。它的核心作用是统一可调用对象的类型,让不同类型的可调用对象可以用相同的方式存储和调用。
template <class Ret, class... Args>
class function<Ret(Args...)>;Ret:可调用对象的返回值类型。Args...:可调用对象的参数类型列表。#include <iostream>
#include <functional>
#include <string>
using namespace std;
// 1. 普通函数
int Add(int a, int b) {
return a + b;
}
// 2. 仿函数
struct Subtract {
int operator()(int a, int b) {
return a - b;
}
};
// 3. 类成员函数
class Calculator {
public:
// 普通成员函数(隐含this指针)
int Multiply(int a, int b) {
return a * b;
}
// 静态成员函数(无this指针)
static int Divide(int a, int b) {
return a / b;
}
};
int main() {
// 包装普通函数
function<int(int, int)> f1 = Add;
cout << "Add(10, 5) = " << f1(10, 5) << endl; // 输出15
// 包装仿函数
function<int(int, int)> f2 = Subtract();
cout << "Subtract(10, 5) = " << f2(10, 5) << endl; // 输出5
// 包装lambda表达式
function<int(int, int)> f3 = [](int a, int b) {
return a % b;
};
cout << "Mod(10, 5) = " << f3(10, 5) << endl; // 输出0
// 包装静态成员函数
function<int(int, int)> f4 = &Calculator::Divide;
cout << "Divide(10, 5) = " << f4(10, 5) << endl; // 输出2
// 包装普通成员函数(需绑定对象或对象指针)
Calculator calc;
function<int(Calculator&, int, int)> f5 = &Calculator::Multiply;
cout << "Multiply(10, 5) = " << f5(calc, 10, 5) << endl; // 输出50
function<int(Calculator*, int, int)> f6 = &Calculator::Multiply;
cout << "Multiply(10, 5) = " << f6(&calc, 10, 5) << endl; // 输出50
return 0;
}(1)存储可调用对象:将不同类型的可调用对象存储在容器中,统一管理。
#include <iostream>
#include <vector>
#include <functional>
using namespace std;
int Add(int a, int b) { return a + b; }
int Subtract(int a, int b) { return a - b; }
int main() {
vector<function<int(int, int)>> funcs;
funcs.push_back(Add);
funcs.push_back(Subtract);
funcs.push_back([](int a, int b) { return a * b; });
int x = 10, y = 5;
for (auto& func : funcs) {
cout << func(x, y) << endl;
}
return 0;
}(2)实现回调函数:将std::function作为函数参数,实现灵活的回调机制。
#include <iostream>
#include <functional>
using namespace std;
// 回调函数类型:void(int)
using Callback = function<void(int)>;
// 模拟异步任务:执行完成后调用回调函数
void AsyncTask(int taskId, Callback callback) {
cout << "执行任务 " << taskId << endl;
// 任务执行完成,调用回调
callback(taskId);
}
int main() {
// 注册回调函数(lambda表达式)
AsyncTask(1, [](int taskId) {
cout << "任务 " << taskId << " 执行完成!" << endl;
});
// 注册另一个回调函数
AsyncTask(2, [](int taskId) {
cout << "回调:任务 " << taskId << " 处理成功!" << endl;
});
return 0;
}(3)实现策略模式:通过std::function封装不同的策略,动态切换算法。
#include <iostream>
#include <functional>
#include <string>
using namespace std;
class Payment {
public:
// 构造函数:传入支付策略
Payment(function<bool(const string&, double)> payStrategy)
: _payStrategy(payStrategy)
{}
// 支付接口
bool Pay(const string& orderId, double amount) {
cout << "订单 " << orderId << " 发起支付,金额:" << amount << endl;
return _payStrategy(orderId, amount);
}
private:
function<bool(const string&, double)> _payStrategy;
};
// 支付宝支付策略
bool AlipayPay(const string& orderId, double amount) {
cout << "支付宝支付 " << amount << " 元成功" << endl;
return true;
}
// 微信支付策略
bool WechatPay(const string& orderId, double amount) {
cout << "微信支付 " << amount << " 元成功" << endl;
return true;
}
int main() {
// 支付宝支付
Payment alipayPay(AlipayPay);
alipayPay.Pay("ORDER001", 99.0);
// 微信支付
Payment wechatPay(WechatPay);
wechatPay.Pay("ORDER002", 199.0);
// 银联支付(lambda表达式)
Payment unionPay([](const string& orderId, double amount) {
cout << "银联支付 " << amount << " 元成功" << endl;
return true;
});
unionPay.Pay("ORDER003", 299.0);
return 0;
} std::bind是一个函数模板,同样定义在<functional>头文件中,用于对可调用对象进行包装和适配,主要功能包括:
std::bind的返回值是一个新的可调用对象,可直接调用或用std::function包装。
#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders; // 包含占位符 _1, _2, ...
// 普通函数:计算(a - b) * 10
int Sub(int a, int b) {
return (a - b) * 10;
}
// 三参数函数:计算(a - b - c) * 10
int Sub3(int a, int b, int c) {
return (a - b - c) * 10;
}
int main() {
// 1. 绑定部分参数:固定a=100,只保留b作为参数(_1表示第一个参数)
auto sub1 = bind(Sub, 100, _1);
cout << "sub1(5) = (100 - 5) * 10 = " << sub1(5) << endl; // 输出950
// 2. 调整参数顺序:交换a和b的位置
auto sub2 = bind(Sub, _2, _1);
cout << "sub2(10, 5) = (5 - 10) * 10 = " << sub2(10, 5) << endl; // 输出-50
// 3. 绑定三参数函数的部分参数:固定b=10,保留a和c(_1和_2)
auto sub3 = bind(Sub3, _1, 10, _2);
cout << "sub3(20, 3) = (20 - 10 - 3) * 10 = " << sub3(20, 3) << endl; // 输出70
// 4. 绑定所有参数:将可调用对象转为无参函数
auto sub4 = bind(Sub, 20, 8);
cout << "sub4() = (20 - 8) * 10 = " << sub4() << endl; // 输出120
return 0;
} 类成员函数包含隐含的this指针,使用std::bind绑定的时,需要显式传入对象或对象指针作为第一个参数。
#include <iostream>
#include <functional>
#include <string>
using namespace std;
using namespace placeholders;
class Person {
public:
Person(const string& name, int age)
: _name(name)
, _age(age)
{}
void ShowInfo(const string& prefix) {
cout << prefix << ":姓名:" << _name << ",年龄:" << _age << endl;
}
private:
string _name;
int _age;
};
int main() {
Person p("张三", 20);
// 绑定普通成员函数:传入对象引用
auto show1 = bind(&Person::ShowInfo, ref(p), _1);
show1("绑定对象引用"); // 输出:绑定对象引用:姓名:张三,年龄:20
// 绑定普通成员函数:传入对象指针
auto show2 = bind(&Person::ShowInfo, &p, _1);
show2("绑定对象指针"); // 输出:绑定对象指针:姓名:张三,年龄:20
// 绑定并固定prefix参数
auto show3 = bind(&Person::ShowInfo, &p, "固定前缀");
show3(); // 输出:固定前缀:姓名:张三,年龄:20
return 0;
}(1)适配函数参数:当调用的函数参数个数或顺序与现有接口不匹配时,用std::bind进行适配。
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
using namespace std;
using namespace placeholders;
// 现有函数:判断x是否大于y
bool IsGreater(int x, int y) {
return x > y;
}
int main() {
vector<int> v = {1, 3, 5, 7, 9, 2, 4, 6, 8};
// 需求:统计大于5的元素个数
// count_if需要的是单参数函数:bool(int),用bind适配IsGreater
int count = count_if(v.begin(), v.end(), bind(IsGreater, _1, 5));
cout << "大于5的元素个数:" << count << endl; // 输出4
return 0;
}(2)固定默认参数:将函数的某些参数固定为默认值,生成新的函数接口。
#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
// 计算复利:利息 = 本金 * 利率 * 年数
double CompoundInterest(double principal, double rate, int years) {
double amount = principal;
for (int i = 0; i < years; ++i) {
amount *= (1 + rate);
}
return amount - principal;
}
int main() {
// 固定利率为1.5%,生成不同年数的复利计算函数
auto Interest3Years = bind(CompoundInterest, _1, 0.015, 3);
auto Interest5Years = bind(CompoundInterest, _1, 0.015, 5);
// 计算100万本金的复利
cout << "100万本金,3年复利(1.5%):" << Interest3Years(1000000) << endl;
cout << "100万本金,5年复利(1.5%):" << Interest5Years(1000000) << endl;
return 0;
}(3)结合 std::function 实现灵活调用:将std::bind的结果用std::function包装,方便存储和传递。
#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int Add(int a, int b) { return a + b; }
int main() {
function<int(int)> add10 = bind(Add, _1, 10); // 固定第二个参数为10
function<int(int)> add20 = bind(Add, 20, _1); // 固定第一个参数为20
cout << "add10(5) = " << add10(5) << endl; // 输出15
cout << "add20(5) = " << add20(5) << endl; // 输出25
return 0;
}掌握上面这些 C++11 核心特性,能够帮助开发者编写更高效、更简洁、更健壮的 C++ 代码,适应现代 C++ 的编程需求。无论是日常开发还是面试求职,这些特性都是必备的知识技能。希望本文的详细解析能够帮助你全面掌握这些特性,在 C++ 的学习和实践中更上一层楼。