C++11 核心精髓:类新功能、lambda与包装器实战


🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!


🎬 博主简介:


文章目录

  • 前言:
  • [一. 类的新功能:更精细的默认函数与初始化控制](#一. 类的新功能:更精细的默认函数与初始化控制)
    • [1.1 新增默认成员函数:移动构造与移动赋值](#1.1 新增默认成员函数:移动构造与移动赋值)
    • [1.2 成员变量声明时给缺省值 && final与override](#1.2 成员变量声明时给缺省值 && final与override)
    • [1.3 default 与 delete:控制默认函数生成](#1.3 default 与 delete:控制默认函数生成)
    • [1.4 其他类新功能(补充:委托构造 && 继承构造)](#1.4 其他类新功能(补充:委托构造 && 继承构造))
  • [二. STL中的一些变化](#二. STL中的一些变化)
  • [三. lambda 表达式:简洁的匿名函数对象](#三. lambda 表达式:简洁的匿名函数对象)
    • [3.1 lambda 核心语法](#3.1 lambda 核心语法)
    • [3.2 捕捉列表:灵活复用外层变量](#3.2 捕捉列表:灵活复用外层变量)
    • [3.3 lambda 的应用场景:替代仿函数与函数指针](#3.3 lambda 的应用场景:替代仿函数与函数指针)
    • [3.4 lambda 的原理:底层是仿函数](#3.4 lambda 的原理:底层是仿函数)
  • [四. 包装器:统一可调用对象的类型](#四. 包装器:统一可调用对象的类型)
    • [4.1 std::function:可调用对象的 "容器"](#4.1 std::function:可调用对象的 “容器”)
    • [4.2 std::bind:可调用对象的 "适配器"](#4.2 std::bind:可调用对象的 “适配器”)
  • 结尾:

前言:

C++11 不仅优化了泛型编程和性能(如移动语义、可变参数模板),还对类机制、可调用对象、资源管理进行了革命性升级。从类的默认函数控制、lambda 匿名函数,到 function/bind 包装器,再到智能指针,这些特性彻底改变了 C++ 的编程范式,让代码更简洁、安全、灵活。本文结合核心知识点和 test.cpp 代码,从类的新功能入手,逐步拆解 lambda 表达式、function/bind 包装器的用法与原理,帮你吃透 C++11 进阶特性,提升代码质量与开发效率。


一. 类的新功能:更精细的默认函数与初始化控制

C++11 扩展了类的核心能力,允许开发者更灵活地控制默认成员函数、初始化方式,解决了传统 C++ 中类设计的诸多痛点。

1.1 新增默认成员函数:移动构造与移动赋值

C++98 有 6 个默认成员函数,C++11 新增移动构造函数移动赋值运算符重载 ,专门用于 "窃取" 右值对象的资源,提升效率。
核心规则

  • 若未手动实现移动构造 / 移动赋值,且未实现析构、拷贝构造、拷贝赋值中的任意一个,编译器会自动生成默认移动构造 / 移动赋值;
  • 默认移动构造 / 赋值:对内置类型成员逐字节拷贝(浅拷贝),对自定义类型成员调用其移动构造 / 赋值(无则调用拷贝构造/拷贝赋值);
  • 若手动提供移动构造 / 赋值,编译器不再自动生成拷贝构造 / 赋值。

实际示例

cpp 复制代码
namespace Lotso {
class string {
public:
		// ............
    // 移动构造:窃取右值资源
    string(string&& s) {
        cout << "string(string&& s) -- 移动构造" << endl;
        swap(s); // 交换当前对象与右值对象的资源
    }

    // 移动赋值:窃取右值资源
    string& operator=(string&& s) {
        cout << "string& operator=(string&& s) -- 移动赋值" << endl;
        swap(s);
        return *this;
    }

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)
	{
	}
	/*Person(const Person& p)
	:_name(p._name)
	,_age(p._age)
	{}*/
	/*Person& operator=(const Person& p)
	{
	if(this != &p)
	{
	_name = p._name;
	_age = p._age;
	}
	return *this;
	}*/
	/*~Person()
{}*/
private:
	Lotso::string _name;
	int _age;
};
int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);
	Person s4;
	s4 = std::move(s2);
	return 0;
}

1.2 成员变量声明时给缺省值 && final与override

成员变量给缺省值

直接在类中声明成员变量时指定缺省值,初始化列表未显式初始化时,会使用该缺省值:

cpp 复制代码
class Person {
private:
    Lotso::string _name = "张三"; // 声明时给缺省值
    int _age = 18;
};

final和override:

好的,这是一个关于 C++ finaloverride 关键字的简单使用场景对比表格。

关键字 作用对象 主要目的 简单使用场景举例
final 禁止该类被继承。 class Base final { }; class Derived : public Base { }; // 错误:无法继承 final 类
final 虚函数 禁止该虚函数在派生类中被重写。 virtual void func() final { } void func() override { } // 错误:无法重写 final 函数
override 虚函数 显式声明意图重写基类的虚函数,让编译器检查签名是否正确。 virtual void baseFunc(int); void baseFunc(int) override; // 正确 void baseFunc(float) override; // 错误:函数签名不匹配

核心思想总结:

  • final:用于"禁止"进一步扩展(继承或重写)。
  • override:用于"明确"并"验证"重写行为,防止因笔误导致的错误,提高代码安全性。

1.3 default 与 delete:控制默认函数生成

  • default:强制编译器生成默认函数(如手动实现拷贝构造后,仍想保留默认移动构造);
  • delete:禁止编译器生成默认函数(如禁止拷贝构造,避免对象拷贝)。
cpp 复制代码
class Person {
public:
    Person(const char* name = "", int age = 0)
        : _name(name), _age(age) {}

    // 强制生成默认移动构造
    Person(Person&& p) = default;

    // 禁止拷贝构造(类似ostream的设计)
    Person(const Person& p) = delete;

private:
    Lotso::string _name;
    int _age;
};
cpp 复制代码
class Person
{
public:
	Person(const char* name = "张三yyyyyyyyyyyy",int age = 18)
		:_name(_name)
		,_age(age)
	{}

	// C++11
	// delete之后就不会自动生成默认拷贝构造函数了,库里面的ostream就用到了
	// Person(const Person& p) = delete;
	// default之后让编译器强制生成移动构造函数
	/*Person(const Person& p) = default;
	Person(Person&& p) = default;*/

	~Person()
	{}

private:
	// C++98--将该函数设置为private,并且只声明不定义,这样也能实现类似于C++11中delete的作用
	/*Person(const Person& p);*/
	string _name;
	int _age;
};

int main()
{
	Person s1;
	Person s2 = s1;
	Person s3 = std::move(s1);

	//Person s4("************************", 1);
	//s4 = std::move(s2);
	return 0;
}

1.4 其他类新功能(补充:委托构造 && 继承构造)

  • 委托构造:一个构造函数调用同类的另一个构造函数,减少代码冗余。其中被委托的构造函数必须初始化所有成员变量,因为委托构造函数后(也是因为所有成员都会走初始化列表)不能再重复初始化了。
  • 继承构造 :通过using Base::Base继承基类所有的普通构造函数(特殊的拷贝构造函数/移动构造函数不继承),简化派生类设计;继承构造函数中派生类自己的成员变量如果有缺省值会使用缺省值初始化,如果没有缺省值那么跟之前类似,内置类型成员不确定,自定义类型成员使用默认构造初始化,但是派生类有自己的成员需要初始化时,一般不适合用继承构造。
cpp 复制代码
// 委托构造
class Example
{
public:
	Example(int a, int b)
		:_x(a)
		, _y(b)
	{
		cout << "目标构造函数\n";
	}

	Example(int a)
		:Example(a, 0)
	{
		cout << "委托构造函数\n";
	}

	int _x;
	int _y;
};

class Time
{
public:
	Time(int h,int m)
		:_hour(h)
		,_minute(m)
	{}
	 //error C3511: "Time": 对委托构造函数的调用应仅为成员初始值设定项,被委托函数必须已经实现了全部成员变量的初始化
    // error C2437 : "_second": 已初始化
    Time(int h, int m, int s)
        :Time(h, m)
     // , _second(s)
    {}

private:
    int _hour;
    int _minute;
    int _second = 0;
};

int main()
{
	Example(1, 2);
	Example(1);

	return 0;
}
cpp 复制代码
// 继承构造
class Base
{
public:
	Base(int x,double d)
		:_x(x)
		,_d(d)
	{}

	Base(int x)
		:_x(x)
	{}

	Base(double d)
		: _x(d)
	{}

protected:
	int _x = 0;
	double _d = 0;
};

// 传统的派生类实现构造,很麻烦复杂
//class Derived : public Base {
//public:
//    Derived(int x) : Base(x) {}
//    Derived(double d) : Base(d) {}
//    Derived(int x, double d) : Base(x, d) {}
//};

// C++11继承基类的所有构造函数
class Derived : public Base {
public:
    using Base::Base;

	// 这里需要注意的是这样的继承构造仅仅适合子类没有变量需要构造的,或者通过缺省值就可以了的。
    /*protected:
        int _i = 0;
        string _s;*/
};


int main()
{
    Derived d1(1);
    Derived d2(1.1);
    Derived d3(2, 2.2);

    return 0;
}

二. STL中的一些变化

  • 下图中圈起来的就是STL中新的容器,但是实际最有用的是 unordered_map 和 unordered_set。这两个我们前面已经进行了非常详细的讲解,其他的大家了解一下即可。
  • STL中容器的新接口也不少,最重要的就是右值引用和移动语义相关的push/insert/emplace系列接口和移动构造与移动赋值,还有initializer_list版本的构造等,这些前面都有讲过,还有一些无关痛痒的如cbegin/cend等需要时查查文档即可。
  • 容器的范围 for 遍历,这个在容器部分也讲过了。

三. lambda 表达式:简洁的匿名函数对象

lambda 表达式本质是 "匿名仿函数",可在函数内部定义,无需单独声明类,极大简化可调用对象的定义。

3.1 lambda 核心语法

lambda 表达式语言使用层而言没有类型,所以我们一般是用 auto 或者 模版参数定义的对象 去接受 lambda 对象

格式

cpp 复制代码
[capture-list] (parameters) -> return-type { function-body }
  • [capture-list] :捕捉列表(不可省),捕捉外层作用域变量供函数体使用;捕捉列表总是出现在 lambda 函数的开始位置,编译器根据 [] 来判断接下来的代码是否为 lambda 函数,捕捉列表能够捕捉上下文的变量供函数使用,捕捉列表可以传值或传引用捕捉,具体的后面还会再讲。捕捉列表为空也不能省略。
  • (parameters):参数列表(可省),与普通函数的参数列表功能类似,无参数时可省略;
  • -> return-type:返回值类型(可省),用追踪返回类型形式声明函数的返回值类型,没有返回值的时候这部分可以直接省略。一般返回值类型明确的情况下,也可以省略,编译器可自动推导(但还是建议写一下的);另外这种形式普通函数也可以用,但是用的比较少。
  • {function-body}:函数体(不可省),函数体内的实现跟普通函数完全类似,在该函数体内,除了可以使用其参数外,还可以使用所以捕获到的变量,函数体就算为空也不可以省略。

简单示例演示

cpp 复制代码
// 无捕捉、无参数、返回值自动推导
auto func1 = [] { cout << "hello Lotso" << endl; };
func1(); // 输出:hello Lotso

// 有参数、有返回值
auto add = [](int x, int y) -> int { return x + y; };
cout << add(1, 2) << endl; // 输出:3
cpp 复制代码
// 关于返回值后置的
// std::map<std::string, std::pair<std::string, std::string>>::iterator func();
// auto func()->std::map<std::string, std::pair<std::string, std::string>>::iterator;

int main()
{
	// 一个简单的lamba表达式
	// 这里可以只靠auto推导返回值类型,但是还是建议写出来的
	/*auto add1 = [](int x, int y){return x + y; };*/
	auto add1 = [](int x, int y)->int {return x + y; };
	cout << add1(1, 2) << endl;

	// 关于那些东西可以省略,哪些不可以
	// 1. 就算捕捉为空也是不可以省略的
	// 2. 参数为空可以直接省,()都不用了
	// 3. 返回值可以省略,可以通过返回对象自动推导
	// 4. 函数体不可以省略
	auto func1 = []
	{
		cout << "hello Lotso" << endl;
		return 0;
	};//分号不要掉哈
	func1();


	int a = 0, b = 1;
	auto swap1 = [](int& x, int& y)
	{
		int temp = x;
		x = y;
		y = temp;
	};
	swap1(a, b);
	cout << a << ":" << b << endl;

	return 0;
}

3.2 捕捉列表:灵活复用外层变量

捕捉列表控制外层变量的访问方式,支持值捕捉、引用捕捉、混合捕捉,核心规则如下:

捕捉方式 含义 示例
[var] 值捕捉 var(拷贝,默认 const,不可修改) [a] { return a * 2; }
[&var] 引用捕捉 var(可修改外层变量) [&a] { a++; }
[=] 隐式值捕捉所有使用的外层变量 [=] { return a + b; }
[&] 隐式引用捕捉所有使用的外层变量 [&] { a++; b++; }
[=, &var] 隐式值捕捉 + 显式引用捕捉 var [=, &a] { a++; return b; }
[&, var] 隐式引用捕捉 + 显式值捕捉 var [&, a] { b++; return a; }
[this] 捕捉当前类的 this 指针(类成员函数中使用) [this] { _a1++; }

关键注意

  • 值捕捉的变量默认被const修饰,需修改时加mutable仅修改拷贝,不影响外层变量);
  • 全局变量、静态变量无需捕捉,可直接使用;
  • 捕捉列表不能为空(无变量需捕捉时写[])。
  • 如果是在类里面就算是值捕捉,成员变量也是可以修改的。

补充说明

实际示例

cpp 复制代码
int x = 0;
// 这里捕捉列表必须为空,因为全局变量不用捕捉就可以用,没有可以被捕捉的变量(静态成员变量也是同样的道理)
auto func1 = []()
{
	x++;
};

int main()
{
	// 只能用当前 lambda 局部域捕捉到的对象和全局对象
	// 捕获列表的意义,本质是更方便的使用当前局部域的对象
	int a = 0, b = 1, c = 2, d = 3;
	// 加了这个mutable之后值捕捉也可以修改a了,这里()就不可以省了就算没参数
	// 传值捕捉本质是⼀种拷⻉,并且被const修饰了
	// mutable相当于去掉const属性,可以修改了
	// 但是修改了不会影响外⾯被捕捉的值,因为是⼀种拷⻉
	/*auto func1 = [a,&b]() mutable*/
	auto func1 = [a, &b]
		{
			// 值捕捉的变量不可以修改,引用捕捉的可以修改
			//a++;
			b++;
			int ret = a + b;
			x++;//全局变量
			return ret;
		};
	cout << func1() << endl;

	// 隐式值捕捉
	// 用了哪些变量就捕捉哪些变量
	auto func2 = [=]
		{
			int ret = a + b;
			return ret;
		};
	cout << func2() << endl;


	// 隐式引用捕捉
	// 用了哪些变量就捕捉哪些变量
	auto func3 = [&]
		{
			a++;
			c++;
			d++;
		};
	func3();
	cout << a << " " << b << " " << c << " " << d << endl;

	cout << "*********************" << endl;

	// 混合捕捉的=或&一定是最前面的,而且如果前面是=后面必须是引用捕捉,前面是&后面必须是值捕捉

	// 混合捕捉1
	auto func4 = [&, a, b]
		{
			//a++;
			//b++;
			c++;
			d++;
			int ret = a + b + c + d;
			return ret;
		};
	cout << func4() << endl;
	cout << a << " " << b << " " << c << " " << d << endl;

	// 混合捕捉2
	auto func5 = [=, &a, &b]
		{
			a++;
			b++;
			/*c++;
			d++;*/
			return a + b + c + d;
		};
	func5();
	cout << a << " " << b << " " << c << " " << d << endl;

	// 局部的静态和全局变量不能捕捉,也不需要捕捉
	static int m = 0;
	auto func6 = []
	{
		int ret = x + m;
		return ret;
	};

	// 传值捕捉本质是⼀种拷⻉,并且被const修饰了
	// mutable相当于去掉const属性,可以修改了
    // 但是修改了不会影响外⾯被捕捉的值,因为是⼀种拷⻉
	auto func7 = [=]()mutable
	{
		a++;
		b++;
		c++;
			d++;
		return a + b + c + d;
	};
	cout << func7() << endl;
	cout << a << " " << b << " " << c << " " << d << endl;
}

在类里面的使用

cpp 复制代码
class A
{
public:
	void func()
	{
		int x = 0, y = 1;
		
		auto f1 = [=]
		{
			// 为什么是值捕捉,但是成员变量还可以++呢,因为这里实际上捕捉的是他的this指针
			_a1++;
			return x + y + _a1 + _a2;
		};

		cout << f1() << endl;

		auto f2 = [&]
		{
			x++;
			_a1++;
			return x + y + _a1 + _a2;
		};

		cout << f2() << endl;

		// 捕捉this指针的本质就是为了可以访问成员变量
		auto f3 = [x,this]
		{
			_a1++;
			return x + _a1 + _a2;
		};

		cout << f3() << endl;
	}
private:
	int _a1 = 0;
	int _a2 = 1;
};

int main()
{
	A a;
	a.func();
	return 0;
}

3.3 lambda 的应用场景:替代仿函数与函数指针

传统排序需要定义仿函数,lambda 则简洁高效:

  • 在学习 lambda 表达式之前,我们使用的可调用对象只有函数指针和仿函数对象,函数指针的类型定义起来比较麻烦,仿函数则是要定义一个类,相对会比较麻烦。使用 lambda 去定义可调用对象,即简单又方便。
  • lambda 在很多其他地方用起来也是很好用的。比如线程中定义线程的执行函数逻辑,智能指针中定制删除器等,lambda 的应用还是很广范的,以后我们会不断接触到。

实际使用示例

cpp 复制代码
struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 平价

	// ...
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{
	}
};


//struct ComparePriceLess
//{
//	bool operator()(const Goods& gl, const Goods& gr)
//	{
//		return gl._price < gr._price;
//	}
//};
//
//struct ComparePriceGreater
//{
//	bool operator()(const Goods& gl, const Goods& gr)
//	{
//		return gl._price > gr._price;
//	}
//};
//
//struct CompareEvaluateLess
//{
//	bool operator()(const Goods& gl, const Goods& gr)
//	{
//		return gl._evaluate < gr._evaluate;
//	}
//};
//
//struct CompareEvaluateGreater
//{
//	bool operator()(const Goods& gl, const Goods& gr)
//	{
//		return gl._evaluate > gr._evaluate;
//	}
//};



int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3}, { "菠萝", 1.5, 4 } };

	// 类似这样的场景,我们实现仿函数对象或者函数指针支持商品中
	// 不同项的比较,相对还是比较麻烦的,那么这里lambda就很好用了
	//sort(v.begin(), v.end(), ComparePriceLess());
	//sort(v.begin(), v.end(), ComparePriceGreater());
	//sort(v.begin(), v.end(), CompareEvaluateLess());
	//sort(v.begin(), v.end(), CompareEvaluateGreater());

	//auto priceLess = [](const Goods& gl, const Goods& gr)
	//{
	//	return gl._price < gr._price;
	//};

	//sort(v.begin(), v.end(), priceLess);


	// 这样写更简单,但是调试查看的时候需要将断点打到下一个sort或下一个语句上
	// 因为如果打到当前sort,他即算打到sort又算打到lambda

	sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr){
			return gl._price < gr._price;
		});

	sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
		return gl._price > gr._price;
		});

	sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
		return gl._evaluate < gr._evaluate;
		});

	sort(v.begin(), v.end(), [](const Goods& gl, const Goods& gr) {
		return gl._evaluate > gr._evaluate;
		});

	return 0;
}

注意:上面的程序最后那些sort需要调试的话可以按注释的方式来做

3.4 lambda 的原理:底层是仿函数

编译器会将 lambda 表达式编译为一个匿名仿函数类

  • 捕捉列表的变量成为该类的成员变量;
  • operator()的参数、返回值、函数体与 lambda 一致;
  • 不同 lambda 对应不同的仿函数类(类名由编译器自动生成)。

补充说明

实际示例

cpp 复制代码
int x = 0;
// lambda参数/返回类型 / 函数体就是仿函数operator()的参数 / 返回类型 / 函数体
// lambda 的捕捉列表本质是生成的仿函数类的成员变量,也就是说捕捉列表的变量都是 lambda 类构造函数的实参
int main()
{
	int a = 0, b = 1, c = 2, d = 3;
	//class lambada5(const int& a_, int& b_) //也行,但是int没必要
	class lambada5
	{
	public:
		lambada5(int a_, int& b_)
			:a(a_)
			,b(b_)
		{}

		int operator()(int x)
		{
			//a++;
			++b;
			return a + b + x;
		}
	private:
		const int a;//对应值捕捉
		int& b;// 对应引用捕捉
	};

	// 可以看看汇编层
	auto func = [a, &b](int x)
	{
		//a++;
		++b;
		return a + b + x;
	};
	cout << func(1) << endl;

	// 等价于
	/*lambada5 func(a, b);*/

	return 0;
}


汇编层部分代码展示

  • 由此可以很明确的看出 lambda 底层的调用逻辑,其实就是仿函数

四. 包装器:统一可调用对象的类型

C++ 中的可调用对象包括函数指针、仿函数、lambda、成员函数,但它们类型各异,难以统一管理。std::functionstd::bind包装器解决了这一问题。

4.1 std::function:可调用对象的 "容器"

std::function是类模板,可包装任意符合 "返回值 (参数类型)" 签名的可调用对象,统一类型接口。


参考文档function - C++ Reference

补充说明:

核心用法:

cpp 复制代码
#include<functional>

int f(int a, int b)
{
	return a + b;
}

struct Functor
{
public:
	int operator()(int a, int b)
	{
		return a + b;
	}
};

class Plus
{
public:
	Plus(int n = 10)
		:_n(n)
	{}

	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return (a + b) * _n;
	}
private:
	int _n;
};
//
//
int main()
{
	// 类型擦除
	function<int(int, int)>f1 = f;
	function<int(int, int)>f2 = Functor();
	function<int(int, int)>f3 = [](int a, int b) {return a + b; };
	cout << f1(1, 1) << endl;
	cout << f2(1, 1) << endl;
	cout << f3(1, 1) << endl;

	vector<function<int(int, int)>> v;
	v.push_back(f);
	v.push_back(Functor());
	v.push_back([](int a, int b) {return a + b; });
	for (auto& f : v)
	{
		cout << f(1, 1) << endl;
	}


	// 静态成员函数,下面两种写法都可以,用第二种可以统一规范
	// function<int(int, int)> f4 = Plus::plusi;
	 function<int(int, int)> f4 = &Plus::plusi;
	 cout << f4(1, 1) << endl;

	 // 成员函数,必须带&,并且不要忘了this指针的存在,所以下面实际是三个参数,但是写法很多
	 function<double(Plus*, double, double)>  f5 = &Plus::plusd;
	 Plus ps;
	 cout << f5(&ps, 1.1, 1.1) << endl;

	 function<double(Plus, double, double)>  f6 = &Plus::plusd;
	 // Plus ps;
	 cout << f6(ps, 1.1, 1.1) << endl;

	 function<double(Plus, double, double)>  f7 = &Plus::plusd;
	 cout << f7(Plus(), 1.1, 1.1) << endl;

	 function<double(Plus&&, double, double)>  f8 = &Plus::plusd;
	 cout << f8(Plus(), 1.1, 1.1) << endl;


	 auto pf1 = &Plus::plusd;
	 Plus* ptr = &ps;
	 cout << (ps.*pf1)(1.1, 1.1) << endl;
	 cout << (ptr->*pf1)(1.1, 1.1) << endl;

	return 0;
}
  • 关于类型擦除大家可以自己下去再了解一下,这里就不过多介绍了。

实战场景:实现运算映射表(逆波兰表达式求值)

这个题目我们之前就写过,学完function之后可以优化一下
题目链接150. 逆波兰表达式求值 - 力扣(LeetCode)

C++算法代码

cpp 复制代码
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        map<string,function<int(int,int)>> oFuncMap
        {
            {"+",[](int a, int b){return a + b;}},
            {"-",[](int a, int b){return a - b;}},
            {"*",[](int a, int b){return a * b;}},
            {"/",[](int a, int b){return a / b;}}
        };

        stack<int> st;
        for(auto& str:tokens)
        {
            if(oFuncMap.count(str))
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();

                int ret = oFuncMap[str](left,right);
                st.push(ret);
            }
            else
            {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

4.2 std::bind:可调用对象的 "适配器"

std::bind是函数模板,可调整可调用对象的参数个数、顺序,绑定固定参数,返回一个新的可调用对象。

参考文档: bind - C++ Reference

补充说明:

核心用法:(有的地方需要用到上面的代码,这里就不重复写了)

cpp 复制代码
#include<functional>

using placeholders::_1;
using placeholders::_2; 
using placeholders::_3;

int Sub(int a, int b)
{
	return (a - b) * 10;
}

int SubX(int a, int b, int c)
{
	return (a - b - c) * 10;
}


int main()
{
	// bind 本质返回一个仿函数对象
	// 调整参数顺序(不常用)
	// _1 代表第一个实参
	// _2 代表第二个实参
	// ............
	auto f1 = bind(Sub, _1, _2);
	auto f2 = bind(Sub, _2, _1);

	// _1 代表第一个实参
	// _2 代表第二个实参
	cout << f1(10, 5) << endl;
	cout << f2(10, 5) << endl;

	// 调整参数个数
	auto f3 = bind(SubX, 10, _1, _2);
	cout << f3(15, 5) << endl;
	// _1 代表第一个实参
	// _2 代表第二个实参
	// 底层operator(),调整SubX,第一个参数10,15,5

	auto f4 = bind(SubX, _1, 10, _2);
	cout << f4(15, 5) << endl;
	// 底层operator(),调整SubX,第一个参数15,10,5

	auto f5 = bind(SubX, _1, _2, 10);
	cout << f5(15, 5) << endl;
	// 底层operator(),调用SubX,第一个参数15,5,10

	// 利用bind改进
	function<double(Plus, double, double)>f7 = &Plus::plusd;
	cout << f7(Plus(), 1.1, 1.1) << endl;
	cout << f7(Plus(), 2.2, 1.1) << endl;
	cout << f7(Plus(), 3.3, 1.1) << endl;

	// 绑定成员函数(需传入this指针或对象)
	function<double(double, double)>f8 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f8(1.1, 1.1) << endl;
	cout << f8(2.2, 1.1) << endl;
	cout << f8(3.3, 1.1) << endl;

	return 0;
}

实战场景:银行复利产品

cpp 复制代码
#include<functional>

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;

int main()
{
	// 计算复利的 lambda
	auto func1 = [](double rate, double money, int year)->double {
		double ret = money;
		for (int i = 0; i < year; i++)
		{
			ret += ret * rate;
		}

		return ret - money;
		};

	function<double(double)>func_r1_5_y3 = bind(func1, 0.015, _1, 3);
	function<double(double)>func_r1_5_y5 = bind(func1, 0.015, _1, 5);
	function<double(double)>func_r1_5_y20 = bind(func1, 0.015, _1, 20);

	cout << func_r1_5_y3(100000) << endl;
	cout << func_r1_5_y5(100000) << endl;
	cout << func_r1_5_y20(100000) << endl;

	cout << "*****************************" << endl;

	function<double(double)>func_r10_y3 = bind(func1, 0.1, _1, 3);
	function<double(double)>func_r10_y5 = bind(func1, 0.1, _1, 5);
	function<double(double)>func_r10_y20 = bind(func1, 0.1, _1, 20);

	cout << func_r10_y3(100000) << endl;
	cout << func_r10_y5(100000) << endl;
	cout << func_r10_y20(100000) << endl;

}

声明:C++11中还有智能指针,在后面会单独写一篇博客的

核心总结与避坑指南:

1. 类新功能避坑

  • 手动实现拷贝构造 / 赋值后,默认移动构造 / 赋值不再生成,需手动添加或用default
  • delete可禁止拷贝(如单例模式),default可强制生成默认函数(如移动构造)。

2. lambda 避坑

  • 值捕捉的变量默认const,修改需加mutable(仅影响拷贝);
  • 引用捕捉需确保外层变量生命周期长于 lambda,避免悬垂引用。

3. 包装器避坑

  • function包装成员函数时,需传入this指针或对象(隐含第一个参数);
  • bind的占位符_n对应新可调用对象的第 n 个参数,顺序不可混淆。

结尾:

html 复制代码
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:C++11 的类新功能、lambda、包装器和智能指针,共同构建了现代 C++ 的核心编程模型。类新功能让类设计更灵活,lambda 简化了可调用对象定义,包装器统一了接口类型,智能指针则保障了资源安全。这些特性并非孤立存在,实际开发中常结合使用(如 lambda 作为function的参数、智能指针配合 lambda 自定义删除器)。掌握它们,能让你写出更简洁、高效、安全的 C++ 代码,适配从简单工具到复杂项目的各类场景。

✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど

相关推荐
qq_336313931 小时前
java基础-stream流练习
java·开发语言·python
用户497357337981 小时前
【轻松掌握通信协议】C#的通信过程与协议实操 | 2024全新
后端
一水鉴天1 小时前
整体设计 定稿 之30 架构表述表总 语义分析 之1(codybuddy)
人工智能·重构
欧特克_Glodon2 小时前
C++医学图像处理经典ITK库用法详解<三>: 图像配准模块功能
c++·图像处理·vtk·图像配准
非著名架构师2 小时前
物流算法的“高阶变量”:高精度AI气象如何为智能供应链注入“天气理解力”,实现动态成本与风险最优?
人工智能·疾风气象大模型·高精度天气预报数据·galeweather.cn·高精度气象·风电光伏功率预测
后端小肥肠2 小时前
Coze编程首测:我用大白话搭了个“AI漫剧流水线”,太离谱了!
人工智能·aigc·coze
倪偲0012 小时前
livox/CustomMsg消息从ROS1 bag转换成ROS2
人工智能·机器人·自动驾驶
IT知识分享2 小时前
中科天玑全要素AI舆情系统功能、架构解析
人工智能·语言模型·架构
黎雁·泠崖2 小时前
【C语言指针精讲】从内存到运算,吃透指针核心逻辑
c语言·开发语言