探索C++11:解锁现代编程(3)
1.包装器1.包装器是 C++ 标准库中的一个模板类,位于头文件中。它用于封装可调用对象,包括普通函数、Lambda 表达式、函数对象、成员函数等。提供了极大的灵活性,使得你可以将不同类型的可调用对象统一管理。
1.包装器
1.1function
std::function
是 C++ 标准库中的一个模板类,位于 <functional>
头文件中。它用于封装可调用对象,包括普通函数、Lambda 表达式、函数对象、成员函数等。std::function
提供了极大的灵活性,使得你可以将不同类型的可调用对象统一管理。
std::function
的组成部分
1.模板参数
std::function
的模板参数定义了它所封装的函数的签名。例如:
std::function<ReturnType(ParamType1, ParamType2, ...)>
ReturnType
:函数的返回类型。ParamType1, ParamType2, ...
:函数的参数类型。
2.构造函数
std::function
提供了多种构造函数,用于封装不同类型的可调用对象。
- 默认构造函数:
std::function<int(int, int)> func;
创建一个空的 std::function
对象,不绑定任何可调用对象。
- 普通函数构造:
int add(int a, int b) {
return a + b;
}
std::function<int(int, int)> func = add;
将普通函数 add
封装到 std::function
中。
- Lambda 表达式构造:
std::function<int(int, int)> func = [](int a, int b) { return a + b; };
将 Lambda 表达式封装到 std::function
中。
- 函数对象构造:
struct Adder {
int operator()(int a, int b) {
return a + b;
}
};
std::function<int(int, int)> func = Adder();
将函数对象 Adder
封装到 std::function
中。
- 成员函数构造:
class MyClass {
public:
int add(int a, int b) {
return a + b;
}
};
MyClass obj;
std::function<int(MyClass&, int, int)> func = &MyClass::add;
将成员函数 add
封装到 std::function
中。
#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;
// 包装静态成员函数
//
// 成员函数要指定类域并且前⾯加&才能获取地址
function<int(int, int)> f4 = &Plus::plusi;
cout << f4(1, 1) << endl;
// 包装普通成员函数
// 普通成员函数还有⼀个隐含的this指针参数,所以绑定时传对象或者对象的指针过去都可以
function<double(Plus*, double, double)> f5 = &Plus::plusd;
Plus pd;
cout << f5(&pd, 1.1, 1.1) << endl;// 显式传递对象指针
function<double(Plus, double, double)> f6 = &Plus::plusd;
cout << f6(pd, 1.1, 1.1) << endl;// 传递对象副本
// pd 被拷贝生成临时对象,临时对象的地址被隐式作为 this 指针。
function<double(Plus&&, double, double)> f7 = &Plus::plusd;
cout << f7(move(pd), 1.1, 1.1) << endl; 传递右值引用
cout << f7(Plus(), 1.1, 1.1) << endl;//传递临时对象(右值)
return 0;
}
参数对比
参数类型 | 传递方式 | 对象所有权 | 性能开销 | 适用场景 |
---|---|---|---|---|
Plus* |
指针传递 | 操作原对象 | 无 | 需修改原对象或避免拷贝 |
Plus |
值传递(副本) | 操作临时副本 | 拷贝构造开销 | 安全但低效,适用于小型对象 |
Plus&& |
右值引用 | 操作临时或移动对象 | 无或移动开销 | 高效传递临时对象或资源转移 |
1.2bind
#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;
}
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;
// bind 本质返回的⼀个仿函数对象
// 调整参数顺序(不常⽤)
// _1代表第⼀个实参
// _2代表第⼆个实参
// ...
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl;
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl;
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl;
auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl;
auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl;
// 成员函数对象进⾏绑死,就不需要每次都传递了
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl;
cout << f6(Plus(), 1.1, 1.1) << endl;
// bind⼀般⽤于,绑死⼀些固定参数
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl;
// 成员函数指针:&Plus::plusd。
// 对象实例:Plus()(临时对象)。
// 参数占位符:_1 和 _2(表示调用时的参数)。
// 计算复利的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;
};
return 0;
}
- 头文件与命名空间
#include <functional>
using namespace std::placeholders; // 启用占位符 _1, _2, _3
<functional>
:提供 std::bind
、std::function
等功能。
placeholders
:定义占位符 _1
(第一个参数)、_2
(第二个参数)等。
std::bind
基础用法
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl; // 输出 (10-5)*10 = 50
作用:将 Sub
的参数按顺序绑定为 _1
(第一个实参)、_2
(第二个实参)。
等效调用:Sub(10, 5)
。
-
调整参数顺序
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl; // 输出 (5-10)*10 = -50
参数交换:_2
绑定到第一个参数位置,_1
绑定到第二个位置。
等效调用:Sub(5, 10)
。
- 固定部分参数
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl; // 输出 (100-5)*10 = 950
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl; // 输出 (5-100)*10 = -950
固定参数:将 Sub
的第一个参数固定为 100
,第二个参数由 _1
提供。
等效调用:Sub(100, 5)
和 Sub(5, 100)
。
- 绑定多参数函数
auto sub5 = bind(SubX, 100, _1, _2);
cout << sub5(5, 1) << endl; // 输出 (100-5-1)*10 = 940
auto sub6 = bind(SubX, _1, 100, _2);
cout << sub6(5, 1) << endl; // 输出 (5-100-1)*10 = -960
auto sub7 = bind(SubX, _1, _2, 100);
cout << sub7(5, 1) << endl; // 输出 (5-1-100)*10 = -960
参数绑定规则:
sub5
:SubX(100, 5, 1)
。sub6
:SubX(5, 100, 1)
。sub7
:SubX(5, 1, 100)
。
- 成员函数绑定
function<double(Plus&&, double, double)> f6 = &Plus::plusd;
Plus pd;
cout << f6(move(pd), 1.1, 1.1) << endl; // 输出 2.2
cout << f6(Plus(), 1.1, 1.1) << endl; // 输出 2.2
右值引用绑定:
f6
接受 Plus&&
(右值引用),调用时需传递临时对象或使用 std::move
。
非静态成员函数 plusd
的 this
指针来自绑定的对象。
- 使用
bind
简化成员函数调用
function<double(double, double)> f7 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f7(1.1, 1.1) << endl; // 输出 2.2
固定对象实例:
bind
的第一个参数是成员函数地址,第二个参数是绑定的对象实例 Plus()
(临时对象)。
调用 f7
时只需传递 a
和 b
,无需显式传递对象。
// 伪代码:内部行为
Plus temp = Plus(); // 创建临时对象
temp.plusd(1.1, 1.1); // 调用成员函数
Plus::plusd
:仅表示成员函数的地址,不需要对象实例。
Plus()
:构造临时对象供 std::bind
存储副本,用于后续调用时提供 this
指针。
2.const限定符
2.1顶层const和底层const
- 指针本⾝是⼀个对象,它有可以指向另⼀个对象,因此指针涉及到本⾝是不是const和指向对象是不是const的问题,C++⽤为了好区分,把本⾝被const修饰叫做顶层const,把指向的对象被const修饰叫做底层const。
- ⼤多数对象被const修饰都叫顶层const,指针被const修饰时,*左边的const叫底层const,*右边的const叫做顶层const。
-
const修饰引⽤时,这个const是底层const。
int main()
{
int i = 0;
int* const p1 = &i; // 顶层const
const int ci = 42; // 顶层const
const int* p2 = &ci; // 底层const
const int& r = ci; // 底层const
return 0;
}
2.2constexpr
constexpr
是 C++11 引入的关键字,用于声明在编译时可求值的表达式、变量或函数。它的核心目的是将计算移到编译期,提升运行时效率,并支持在需要编译期常量的上下文中使用(如数组大小、模板参数等)
constexpr
与 const
的区别
特性 | constexpr |
const |
---|---|---|
求值时机 | 强制编译期求值 | 可能为编译期或运行时常量 |
适用场景 | 数组大小、模板参数等需编译期确定值的场景 | 运行时常量或只读变量 |
函数修饰 | 可修饰函数 | 仅修饰变量或成员函数 |
constexpr
变量
作用:声明变量为编译期常量。
规则:
-
必须用常量表达式初始化。
-
所有声明为
constexpr
的变量隐含为const
。
constexpr int size = 10; // 正确
int arr[size]; // 合法,数组大小需编译期确定
int runtime_val = 5;
constexpr int invalid = runtime_val; // 错误!runtime_val 不是常量表达式
constexpr
函数
作用:允许函数在编译时求值(若参数为常量表达式)。
规则:
-
C++11:函数体只能包含一条
return
语句,不能有循环、局部变量等。 -
C++14 起:允许更复杂的逻辑(如循环、条件、局部变量)。
-
参数和返回值必须是字面类型(Literal Type)。
// C++11 风格(简单返回)
constexpr int add(int a, int b) {
return a + b;
}
// C++14 风格(允许复杂逻辑)
constexpr int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
constexpr int sum = add(3, 4); // 编译时计算,sum=7
constexpr int fact_5 = factorial(5); // 编译时计算,fact_5=120
constexpr
构造函数与对象
作用:允许在编译时构造对象。
规则:
-
构造函数必须初始化所有成员。
-
成员函数若为
constexpr
,可被编译时调用。
class Point {
public:
constexpr Point(int x, int y) : x_(x), y_(y) {}
constexpr int x() const { return x_; } // C++11: 必须为 const 成员函数
constexpr int y() const { return y_; }
private:
int x_, y_;
};
constexpr Point p(3, 4); // 编译时构造对象
constexpr int px = p.x(); // px=3
3.处理类型
3.1auto
基本用法
auto x = 42; // x 被推导为 int
auto y = 3.14; // y 被推导为 double
auto str = "hello"; // str 被推导为 const char*
int a = 10;
const auto& ref = a; // ref 是 const int&
auto ptr = &a; // ptr 是 int*
常见应用场景
(1)简化复杂类型
std::vector<std::map<std::string, std::list<int>>> data;
auto it = data.begin(); // 无需写冗长的迭代器类型
(2)范围for循环
std::vector<int> vec = {1, 2, 3};
for (auto num : vec) { // 拷贝元素(num 是 int)
// ...
}
for (const auto& num : vec) { // 避免拷贝(num 是 const int&)
// ...
}
(3) Lambda 表达式
auto lambda = [](int x) { return x * 2; };
std::function<int(int)> func = lambda;
(4) 返回值类型推导(C++14+)
auto add(int a, int b) { // 返回类型自动推导为 int
return a + b;
}
(5) 结构化绑定(C++17+)
std::pair<int, double> p = {1, 2.5};
auto [x, y] = p; // x 是 int, y 是 double
3.2typedef和using
- C++98中我们⼀般使⽤typedef重定义类型名,也很⽅便,但是typedef不⽀持带模板参数的类型重定义。C++11中新增了using可以替代typedef,using 的别名语法覆盖了 typedef 的全部功能,还⽀持带模板参数重定义的语法。
- using 类型别名 = 类型;
#include <map>
#include <string>
using namespace std;
//typedef map<string, int> CountMap;
//typedef map<string, string> DictMap;
//typedef int DateType;
// using 兼容typedef的⽤法
using CountMap = map<string, int>;
using DictMap = map<string, string>;
using STDateType = int;
// using⽀持带模板参数的类型重定义
template<class Val>
using Map = map<string, Val>;
template<class Val>
using MapIter = typename map<string, Val>::iterator;
更多推荐
所有评论(0)