当前位置: 首页 > news >正文

站长网站素材苏州做网站的专业公司

站长网站素材,苏州做网站的专业公司,佛山新网站制作渠道,做我姓什么的网站文章目录 前言一、新的类功能——新的默认成员函数1. 编译器默认生成的移动构造与移动赋值2. 手动提供移动构造或移动赋值的影响 二、可变参数模板1. 多参数1. 可变参数模板的基础 2. 可变参数模板的展开(1)递归展开参数包(2)使用…

在这里插入图片描述

文章目录

  • 前言
  • 一、新的类功能——新的默认成员函数
    • 1. 编译器默认生成的移动构造与移动赋值
    • 2. 手动提供移动构造或移动赋值的影响
  • 二、可变参数模板
    • 1. 多参数
      • 1. 可变参数模板的基础
    • 2. 可变参数模板的展开
      • (1)递归展开参数包
      • (2)使用逗号表达式展开参数包
    • 3. 实际应用场景
  • 三、STL容器中的empalce相关接口函数
    • 1. empalce_back用法
    • 2. emplace_back与push_back与优缺
  • 四、包装器
    • 1. function包装器
      • (1)function是什么?
      • (2)function语法
      • (3)function应用
    • 2. bind
      • (1)bind语法
      • (2)bind用处
      • (3)对于成员函数
  • 总结


前言

接下来我们接着看C++11的新功能吧~


一、新的类功能——新的默认成员函数

在这里插入图片描述

这里是你的内容经过格式优化后的版本,保证清晰、易读,并符合技术文档的风格:


C++11 新增:移动构造函数与移动赋值运算符

C++11 新增了移动构造函数移动赋值运算符重载,它们主要用于资源所有权转移,提高性能,减少不必要的拷贝操作。

1. 编译器默认生成的移动构造与移动赋值

在特定条件下,编译器会自动生成默认的移动构造函数和移动赋值运算符。但需要注意以下几点:

  1. 默认移动构造函数
    • 如果类没有定义
      • 析构函数
      • 拷贝构造函数
      • 拷贝赋值运算符
    • 那么编译器会自动生成默认的移动构造函数
    • 其行为:
      • 对于内置类型(如 intdouble),执行按字节逐成员拷贝
      • 对于自定义类型,如果该类型实现了移动构造,则调用其移动构造;否则调用拷贝构造
        参考代码——string类
namespace jyf
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(const char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;//string tmp(s._str);//swap(tmp);}string(string&& s):_str(nullptr){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}string& operator=(string&& s){cout << "string& operator=(string && s) -- 移动拷贝" << endl;swap(s);return *this;}~string(){delete[] _str;_str = nullptr;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}

这里我们采用如下代码来进行学习:

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}//想让他强制生成就用关键字default,如下所示//Person(Person&& p) = default;//Person(const Person& p) = default;private:jyf::string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

如上述代码所示,person类没有实现析构,拷贝构造,拷贝赋值,因此传右值的时候就因该调到移动拷贝。

在这里插入图片描述


  1. 默认移动赋值运算符
    • 如果类没有定义
      • 析构函数
      • 拷贝构造函数
      • 拷贝赋值运算符
    • 那么编译器会自动生成默认的移动赋值运算符
    • 其行为:
      • 对于内置类型,执行按字节逐成员拷贝
      • 对于自定义类型,如果该类型实现了移动赋值,则调用其移动赋值;否则调用拷贝赋值

注意:默认移动赋值运算符的行为与默认移动构造函数完全类似。

2. 手动提供移动构造或移动赋值的影响

如果类手动实现了

  • 移动构造函数
  • 移动赋值运算符

那么编译器不会自动提供:

  • 拷贝构造函数
  • 拷贝赋值运算符

如果你的类需要支持拷贝与移动,必须手动实现拷贝构造与拷贝赋值,否则可能导致拷贝操作被禁止(=delete)。


二、可变参数模板

1. 多参数

C++11 引入了可变参数模板,允许创建可以接受任意数量参数函数模板类模板

1. 可变参数模板的基础

在可变参数模板中,参数列表使用 ... 省略号表示。例如:

template <class ...Args>
void ShowList(Args... args)
{}

在上面的代码中:

  • Args...模板参数包,它可以包含**0 到 N(N ≥ 0)**个模板参数。
  • args...函数形参包,它与 Args... 一一对应。

可变参数模板的一个主要特点是:
不能直接获取参数包中的元素,只能通过展开参数包的方式访问每个参数。这也是可变参数模板最难理解的地方,因为语法不支持 args[i] 这种方式直接访问参数。


2. 可变参数模板的展开

由于无法直接访问参数包中的元素,常见的展开方式有:

(1)递归展开参数包

递归展开是最常见的方式,我们通过递归终止函数来控制递归的结束:

#include <iostream>
using namespace std;// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}// 递归展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

执行结果:

1
1 A
1 A sort

原理:

  • 递归终止函数:当 Args... 为空时,调用 ShowList(const T&) 结束递归。
  • 递归展开:每次调用 ShowList(T value, Args... args),打印 value,然后递归调用 ShowList(args...) 继续展开。

也可以这样:

void _ShowList()
{// 结束条件的函数cout << endl;
}template <class T, class ...Args>
void _ShowList(T val, Args... args)
{cout << val << " ";_ShowList(args...);
}//args代表0-N的参数包
template <class ...Args>
void CppPrint(Args... args)
{_ShowList(args...);
}int main()
{CppPrint();CppPrint(1);CppPrint(1, 2);CppPrint(1, 2, 2.2);CppPrint(1, 2, 2.2, string("xxxx"));// ...return 0;
}

(2)使用逗号表达式展开参数包

逗号表达式可以用于参数包展开,不需要额外定义递归终止函数:

#include <iostream>
using namespace std;template <class T>
void PrintArg(T t)
{cout << t << " ";
}// 直接展开函数
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... }; // 逗号表达式展开参数包cout << endl;
}int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

执行结果:

1 
1 A 
1 A sort 

原理:

  • PrintArg(args) 依次执行 PrintArg(arg1), PrintArg(arg2), PrintArg(arg3)...
  • 由于 int arr[] = { (PrintArg(args), 0)... }; 是一个初始化列表,所以所有 PrintArg(args)构造数组时就被执行,从而展开参数包。


3. 实际应用场景

我们来看一个实用一点的实际场景:

class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){cout << "Date构造" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date拷贝构造" << endl;}private:int _year;int _month;int _day;
};template <class ...Args>
Date* Create(Args... args)
{Date* ret = new Date(args...);return ret;
}int main()
{Date* p1 = Create();Date* p2 = Create(2023);Date* p3 = Create(2023, 9);Date* p4 = Create(2023, 9, 27);Date d(2023, 1, 1);Date* p5 = Create(d);return 0;
}

如下图所示:有了多参数之后,我们只要提供多参数的构造,再将构造函数写缺省,就可以更加灵活多变的传参来创建对象,和传统对象的创建相比,这个方法不需要创建额外的对象,参数包传过去会自动匹配构造函数,省去了一层拷贝,提高了效率!

在这里插入图片描述

也可以直接传日期类对象,参数包接收就会掉拷贝构造:
在这里插入图片描述

在这里插入图片描述


三、STL容器中的empalce相关接口函数

1. empalce_back用法

http://www.cplusplus.com/reference/vector/vector/emplace_back/
http://www.cplusplus.com/reference/list/list/emplace_back/

在这里插入图片描述

在这里插入图片描述

我们可以看到每一个容器都新增了empalce系类的内容,那么它有什么用呢?

template <class... Args>
void emplace_back (Args&&... args);
首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对insert和
emplace系列接口的优势到底在哪里呢?

2. emplace_back与push_back与优缺

int main()
{list< std::pair<int, char> > mylist;// emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象// 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别mylist.emplace_back(10, 'a');mylist.emplace_back(20, 'b');mylist.emplace_back(make_pair(30, 'c'));mylist.push_back(make_pair(40, 'd'));mylist.push_back({ 50, 'e' });for (auto e : mylist)cout << e.first << ":" << e.second << endl;return 0;
}

主要是这样:mylist.emplace_back(20, 'b');,这样就不用先创建临时对象在进行拷贝构造,而是直接走参数包,有一定性能提升的。而push_back只能传对象。


我们再来看下一个场景:

int main()
{// 下面我们试一下带有拷贝构造和移动构造的jyf::string,再试试呢// 我们会发现其实差别也不大,emplace_back是直接构造了,push_back// 是先构造,再移动构造,其实也还好。std::list< std::pair<int, jyf::string> > mylist;mylist.emplace_back(10, "sort");mylist.push_back(make_pair(30, "sort"));std::list<Date> lt;Date d(2023, 9, 27);// 只能传日期类对象lt.push_back(d);// 传日期类对象// 传日期类对象的参数包// 参数包,一路往下传,直接去构造或者拷贝构造节点中日期类对象lt.emplace_back(d);lt.emplace_back(2023, 9, 27);return 0;
}

因为有移动构造的存在,所以对于深拷贝的类其实差别不大,对于浅拷贝有一定提升,但因为浅拷贝本来就没有多少资源,所以也影响不大。

在这里插入图片描述


四、包装器

1. function包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

那么,function有什么用呢?

function在#include <functional>里面~


(1)function是什么?

我们来看下面一段代码:

#include<iostream>
using namespace std;
#include <functional>template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double f(double i)
{return i / 2;
}struct Functor
{double operator()(double d){return d / 3;}
};int main()
{// 函数指针cout << useF(f, 11.11) << endl;// 函数对象cout << useF(Functor(), 11.11) << endl;// lambda表达式cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;return 0;
}

有三类:1.函数指针 2. 函数对象 3. lambda表达式 分别去调用useF函数,但是他们三个虽然都是起到类似定义函数变量的作用,但是他们却实例化出三份不同的函数,证据就是运行结果静态变量没有则会增加到3,而是有3个1。

在这里插入图片描述
那有没有什么方法可以包装一下这三类1.函数指针 2. 函数对象 3. lambda表达式呢?
有的兄弟,有的~
我们的包装器就要登场了——function


(2)function语法

语法:function<返回值类型(参数列表)> xxx = ???

像这样:

// 包装器 -- 可调用对象的类型问题function<double(double)> f1 = f;function<double(double)> f2 = [](double d)->double { return d / 4; };function<double(double)> f3 = Functor();

因此我们就可以吧三种不同类的函数放到同一个vector中,如下:
方法一:

vector<function<double(double)>> v = { f1, f2, f3 };

方法二:

vector<function<double(double)>> v = { f, [](double d)->double { return d / 4; }, Functor() };

因此我们就可以这样取调用它:

vector<function<double(double)>> v = { f, [](double d)->double { return d / 4; }, Functor() };double n = 3.3;
for (auto f : v)
{cout << f(n++) << endl;
}

有了function以后,我们就可以同一类型,解决实例化多份的问题,如下图,三类只实例化出一份。

#include <functional>
template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{// 函数名std::function<double(double)> func1 = f;cout << useF(func1, 11.11) << endl;// 函数对象std::function<double(double)> func2 = Functor();cout << useF(func2, 11.11) << endl;// lamber表达式std::function<double(double)> func3 = [](double d)->double { return d /4; };cout << useF(func3, 11.11) << endl;return 0;
}

在这里插入图片描述


(3)function应用

我们来通过一道习题展示他的应用:
https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/submissions/

在这里插入图片描述

这是我们之前写的代码,要判断多次运算符而且冗余:

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> s;for (size_t i = 0; i < tokens.size(); i++){if (tokens[i] == "+" ||tokens[i] == "-" ||tokens[i] == "*" ||tokens[i] == "/" ){int right = s.top();s.pop();int left = s.top();s.pop();switch(tokens[i][0]){case '+':s.push(left + right);break;case '-':s.push(left - right);break;case '*':s.push(left * right);break;case '/':s.push(left / right);break;}}else{s.push(atoi(tokens[i].c_str()));}}return s.top();}
};

有了function之后我们就可以这样做~

class Solution {
public:int evalRPN(vector<string>& tokens) {map<string, function<int(int, int)>> cmdFuncMap ={{"+", [](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& e : tokens){if (cmdFuncMap.count(e)){//运算int right = st.top();st.pop();int left = st.top();st.pop();st.push(cmdFuncMap[e](left, right));}else{st.push(stoi(e));}}return st.top();}
};

代码看起来是不是清晰很多呢~


2. bind

(1)bind语法

  1. 对于函数:bind(函数名, placeholders::_x, placeholders::_x, ...)
  2. 对于静态成员函数:bind(作用域::静态成员函数, placeholders::_x, placeholders::_x, ...),也可以写成:bind(作用域::&静态成员函数, placeholders::_x, placeholders::_x, ...)
  3. 对于普通成员函数:bind(作用域::&普通成员函数, '&对象'或'匿名对象',placeholders::_x, placeholders::_x, ...)

(2)bind用处

bind(绑定)是什么呢?
简单来说就是传参的时候我们可以改变参数的顺序,以此达到我们想要的效果:

int Sub(int a, int b)
{return a - b;
}int main()
{function<int(int, int)> sub1 = bind(Sub, placeholders::_2, placeholders::_1);cout << sub1(10, 5);
}

在这里插入图片描述
他的原理是这样的:
在这里插入图片描述
传参的时候第一个函数会去匹配placeholder::_1,第二个会去匹配_2,而bind却是按照参数的顺序绑定的,因此我们可以更加灵活调整传参数顺序。

如果有其他参数放到对应位置正常写即可,就像这样:
在这里插入图片描述


(3)对于成员函数

class SubType
{
public:static int sub(int a, int b){return a - b;}int ssub(int a, int b, int rate){return (a - b) * rate;}
};

对于静态成员函数是这样的:
在这里插入图片描述
这个&可加可不加,建议加上。

对于普通成员变量:
在这里插入图片描述
第一个前必须加&,后面可以匿名对象,也可以&对象。其实就是.还是->问题。


总结

C++11语法持续更新中,还有智能指针等章节在后续讲解中,谢谢大家支持!

http://www.cadmedia.cn/news/11543.html

相关文章:

  • 东莞公司网站建设公司网络优化工程师有多累
  • 镇江佳鑫网络科技有限公司网站seo关键词优化
  • 做视频网站企业文化标语经典
  • wordpress嵌入qq群朝阳seo搜索引擎
  • 系统软件开发培训机构抖音seo是什么意思
  • wordpress app生成二维码重庆关键词优化服务
  • 微信开发者工具怎么用seo优化关键词放多少合适
  • 京山网站设计公司百度账号注册入口
  • 一套完整的vi设计手册太原seo关键词排名
  • 外贸建站代理百度快速排名优化工具
  • 公司网页制作html代码seo实战密码第三版pdf
  • 杭州市建设厅网站九易建网站的建站模板
  • 建设企业外贸网站优化大师手机版下载
  • 广州网站备案会员营销
  • 农村做网站赚钱九幺seo工具
  • 最新新闻热点事件2022最好用的手机优化软件
  • 网站建设项目设计表郑州计算机培训机构哪个最好
  • 58同城长沙回收网站建设推广团队
  • 信息平台怎么做债务优化是什么意思
  • 网络营销策划总结南京seo培训
  • 西安网站优化平台seo网站关键词快速排名
  • 河南seo网站开发沈阳seo排名优化软件
  • 网站信息化建设建议和意见南昌seo计费管理
  • 保定网站建设优化最近中国新闻热点大事件
  • 网站单页站群黄冈网站建设收费
  • 做独立销售网站怎么做网站?
  • 安州区建设局网站代运营电商公司
  • 推广普通话写好规范字手抄报seo推广专员工作内容
  • 赌博网站建设东莞建设网
  • 建站之星模板的使用qq推广官网