目录
新的默认成员函数
C++11在6个默认成员函数基础上又加了两个:移动构造函数和移动赋值函数
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
小结:
(1) 生成默认移动构造的条件比较严苛:必须是没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,才会生成默认移动构造函数。
(2)没有移动构造函数,编译器会自动调拷贝构造函数:
例如,有一个person类:
不写拷贝构造编译器会自动生成拷贝构造,同样不写构造,拷贝构造,移动构造,编译器才会生成默认移动构造:
假设我们把其他类里的移动构造函数屏蔽调,编译器就调不了,没有移动构造函数,编译器会自动调拷贝构造函数:
假设我们给person类加一个析构函数:我们发现,它就调不了默认移动构造了:
default
假设我们要释放一下内存,调了析构,那么编译器就不会再生成默认移动构造函数。
我们可以利用default强制编译器生成默认移动构造函数:
但是强制生成默认移动构造函数会导致编译器不再生成默认拷贝构造函数:
这个时候有两个选择:
1.手动写一个拷贝构造函数:2.default强制编译器再生成一个默认拷贝构造函数:
delete
可以强制性禁止生成默认函数。
例如现在不让生成右值:
可变参数模板
我们之前知道可变参数函数printf:
C++11把可变参数扩展到了模板。格式如下:
可变参数模板可以识别参数类型,个数:
可以通过sizeof()计算参数包的个数:
获取参数包的内容
直接获取
取参数包的每个内容,像下面这样for循环遍历可以吗?
不可以,可变模板参数不支持for循环遍历取参数包:
小结:
我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数
正确语法:
递归函数方式展开参数包
逗号表达式方式展开参数包
可变模版参数的实用
有些STL容器的尾插函数有一个empalce版本,一个push_back()版本:
在只有一个参数,这两者没有什么区别:
lambda表达式
C++ Lambda 表达式是一种允许在不定义函数名的情况下创建匿名函数的语法。一次性使用,不需要在其他地方调用,由捕获列表,参数列表,返回值类型,和函数体构成;
其基本语法为:
lambda表达式可以对自定义类进行排序
有下面这样一个自定义类:
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;
}
};
int main()
{
vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
3 }, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(), ComparePriceGreater());
return 0;
}
Good这个类是没有办法排序的,因为是自定义类型:
因此,要想排序有两种方法:
(1)运算符重载
(2)仿函数
使用运算符重载进行比较则需要在sort函数中传入一个函数指针,这样会使得代码更加复杂和难以理解。另外,使用函数对象进行比较还可以提高代码的灵活性,因为可以定义多个不同的比较规则,而使用运算符重载则只能定义一种比较规则。因此,在这种情况下,使用函数对象进行比较更加合适。因此我们这里使用 仿函数进行比大小,然后再用sort()进行排序。
而用lambda表达式的话就可以直接排序:
// 组合排序:先按价格降序,再按评价升序
std::sort(v.begin(), v.end(),
[](const Goods& g1, const Goods& g2) {
if (g1._price != g2._price) {
return g1._price > g2._price; // 价格降序
}
return g1._evaluate < g2._evaluate; // 评价升序
});
c++11引入了lambda表达式。,我们用下面一个列子来展示Lambda表达式的结构和用法:
lambda还可以用于conncet里连接信号和槽
如下代码为主线程执行中启动子线程,connect函数绑定start信号和lambda。lambda内部为子线程工作内容。
// 创建一个线程对象
QThread* thread = new QThread();
// 连接线程启动信号到lambda表达式(作为槽函数)
QObject::connect(thread, &QThread::started, [this]() {
// 这个lambda会在子线程中执行
qDebug() << "子线程ID:" << QThread::currentThreadId();
// 模拟耗时操作(例如网络请求或文件处理)
for (int i = 0; i < 5; ++i) {
qDebug() << "子线程工作中..." << i;
QThread::sleep(1); // 暂停1秒
}
// 操作完成后,退出线程
thread->quit();
});
// 连接线程结束信号到清理函数
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// 启动线程
qDebug() << "主线程ID:" << QThread::currentThreadId();
thread->start();
// 30秒后退出应用程序(确保有足够时间完成)
QTimer::singleShot(30000, &a, &QCoreApplication::quit);
当lamda在类内部的时候默认捕捉this指针
class MyClass : public QObject {
Q_OBJECT
private:
int m_value = 42; // 类的成员变量
public:
void doWork() {
// 在类成员函数中定义 lambda,默认捕获 `this` 指针
auto lambda = [](int x) {
// 直接访问类的成员变量(需通过 `this` 指针)
qDebug() << "类成员变量 m_value: " << this->m_value; // 正确:默认捕获 `this`
qDebug() << "传入参数 x: " << x;
};
// 调用 lambda
lambda(10);
}
};
funtion
function是包装器,用来包装可调用对象。
可调用对象有三种:
1.函数指针
2.仿函数
3.lambda
为什么要封装它们呢?
函数指针比较反人类,写起来不好写,仿函数解决替代了函数指针,但是仿函数太繁重了,需要专门在全局写个类。lambda改善了仿函数繁重的现象,但是类型却是匿名的。
比如说我们分别用函数指针,仿函数,lambda写一个交换函数:
#include<iostream>
#include<map>
using namespace std;
void swap_1(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
struct swap_2
{
void operator()(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
};
int main()
{
int x = 1, y = 2;
auto swap_3 = [](int& a, int& b)
{
int temp = a;
a = b;
b = temp;
};
return 0;
}
包装函数指针,我们分别调用函数指针,仿函数,lambda:
//调用函数指针
function<void(int&, int&)> f1 = swap_1;
f1(x,y);
cout << x << " " << y << endl;
//调用仿函数
function<void(int&, int&)> f2 = swap_2();
f2(x, y);
cout << x << " " << y << endl;
//调用lambda
function<void(int&, int&)> f3 = swap_3;
f3(x, y);
cout << x << " " << y << endl;
为什么要把它们封装呢?
假设有一种需求,需要把函数指针,仿函数,lambda都放进一个容器里,那我们就可以用function来把它们封装起来:
map<string, function<void(int&, int&) >> cmppp =
{
{"函数指针",swap_1}
,{"仿函数",swap_2()}
,{"lambda",swap_3}
};
调用的时候我就可以这样调用:
cmppp["函数指针"](x, y);
cout << x << " " << y << endl;
cmppp["仿函数"](x, y);
cout << x << " " << y << endl;
cmppp["lambda"](x, y);
cout << x << " " << y << endl;
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto str:tokens)
{
if( str=="+"
||str=="-"
||str=="*"
||str=="/"
)
{
int right=st.top();
st.pop();
int left=st.top();
st.pop();
switch(str[0])
{
case '+':
st.push(left+right);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
如果用function包装是这样写的:
function封着一个key,value,key值是操作符,value值是lambda表达式,用来计算:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
map<string,function<int(int,int)>> cmdop=
{
{"+",[](int x,int y){return x+y;}},
{"-",[](int x,int y){return x-y;}},
{"*",[](int x,int y){return x*y;}},
{"/",[](int x,int y){return x/y;}}
};
stack<int> st;
for(auto str:tokens)
{
if( cmdop.count(str))
{
int right=st.top();
st.pop();
int left=st.top();
st.pop();
st.push(cmdop[str](left,right));
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
function包装类成员函数
比如说我Plus类里面写了两个plusi,plusd函数:
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
我想在外面用function调用它们就要突破类域:
int main()
{
function<int(int, int)> f1 = Plus::plusi;
cout << f1(1, 2) << endl;
return 0;
}
但是调用plusd函数直接像调用plusi函数一样调用会出错:
function<double(double,double)> f2 = Plus::plusd;
cout << f2(1.1, 2.2) << endl;
提示我们要加上取地址符号,但是即便是加上取地址符号仍然是编译不通过的:
编译的提示参数类型是this指针,而我们的是double,double。类型不匹配。
所以需要修改代码如下:
function<double(Plus*,double,double)> f2 = &Plus::plusd;
Plus ps;
cout << f2(&ps,1.1, 2.2) << endl;
第二种写法:
function<double(Plus,double,double)> f2 = &Plus::plusd;
cout << f2(Plus(),1.1, 2.2) << endl;
调整参数位置
比如说我写一个Sub函数,用function调用一下:
#include<iostream>
#include<string>
#include<functional>
using namespace std;
int Sub(int a,int b)
{
return a - b;
}
int main()
{
function<int(int,int)> f1 = Sub;
cout << f1(10, 5) << endl;
return 0;
}
现在我想把参数10,5的位置换一下,但是又不想去动调用的部分,那就可以在绑定的时候写死。
bind()函数就叫做绑定函数,在bind()函数下有个叫 placeholdersd的接口:
placeholders是一个命名空间,指定了未指定数量的对象:_1,_2,_3……,用于在调用bind函数时指定占位符。
那我们就用bind函数,套placeholders接口,把占位符调换一下位置:
function<int(int,int)> f1 = bind(Sub,placeholders::_2,placeholders::_1);
cout << f1(10, 5) << endl;
调整参数个数
#include<iostream>
#include<string>
#include<functional>
using namespace std;
int Sub(int a,int b)
{
return a - b;
}
int main()
{
function<int(int)> f1 = bind(Sub,20,placeholders::_1);
cout << f1(5) << endl;
return 0;
}
只绑死其中一个参数
比如说我只想把中间的那个参数绑死:
#include<iostream>
#include<string>
#include<functional>
using namespace std;
void fun1(int a,int b,int c)
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
int main()
{
function<void(int, int)> f1 = bind(fun1, placeholders::_1, 10, placeholders::_2);
f1(1,3);
return 0;
}