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

C++---C++11

一、列表初始化

1、C++98中的{}

在C++98中一般数组和结构体可以用{}进行初始化

struct Point
{int _x;int _y;
};
int main()
{int arry1[] = { 1,2,3,4,5 };int arry2[3] = { 0 };Point p = { 1,2 };
}

2、C++11中的{}

①在C++11中一切对象都可以用{}进行初始化

②{}初始化的过程中,可以省略=

struct Point
{int _x;int _y;
};
class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d):_year(d._year), _month(d._month), _day(d._day){cout << "Date(const Date& d)" << endl;}
private:int _year;int _month;int _day;
};
int main()
{//内置类型int x1 = { 2 };//等价于x1 = 2//自定义类型Date d1 = { 2025,8,13 };//可以省略掉=int x2{ 1 };Date d2{ 2025,1,1 };
}

二、右值引用和移动语义

1、左值和右值

①无论是左值引用还是右值引用,都是给对象取名

②左值是一个数据的表达式(如变量名或解引用的指针),可以取到它的地址

③右值也是一个数据的表达式,要么是字面值常量、要么是表达式求值过程中创建的临时对象等,右值可以出现在=右边,不能出现在左边,右值不能取地址

//左值:可以取地址
//下边的p b c *p s[0]都是常见的左值
int* p = new int(8);
int b = 1;
const int c = b;
*p = 10;
string s("11111");
s[0] = 'x';//右值:不能取地址
double x = 1.1, y = 2.2;
//下边的几个都是常见的右值
10;
x + y;
fmin(x, y);
string("111");

2、左值引用和右值引用

①Type& r1 = x; Type&& rr1 = y; 第一个语句是左值引用,左值引用就是给左值取别名,第二个是右值引用,右值引用就是给右值取别名

②左值引用不能直接引用右值,但是const左值引用可以引用右值

③右值引用不能直接引用左值,但是右值引用可以引用move(左值)

④move是库里边的一个函数模板,本质上内部是进行强制类型转换

⑤右值引用变量变量表达式的属性是左值

3、引用延长生命周期

右值引用可用于为临时对象延长声明周期,const的左值引用也能延长临时对象的生存期,但这些对象无法被修改

运行结果

4、左值和右值的参数匹配

c++11重载左值引用,const左值引用,右值引用作为形参的函数,实参是左值匹配左值引用,const左值匹配const左值引用,右值匹配右值引用

注意:const左值最好不要使用move

5、右值引用和移动语义的使用场景

(1)左值引用

左值引用的使用场景是在函数中左值引用传参和左值引用传返回值时减少拷贝,同时还可以修改实参和修改返回对象

(2)例外

有些场景不能使用传左值引用返回

示例:下边这个函数传值返回时需要拷贝

string addStrings(string num1, string num2)
{string str;int end1 = num1.size() - 1, end2 = num2.size() - 1;int next = 0;while (end1 >= 0 || end2 >= 0){int val1 = end1 >= 0 ? num1[end1--] - '0' : 0;int val2 = end2 >= 0 ? num2[end2--] - '0' : 0;int ret = val1 + val2 + next;next = ret / 10;ret = ret % 10;str += ('0' + ret);}if (next == 1)str += '1';reverse(str.begin(), str.end());return str;
}

(3)移动构造和移动拷贝(string为例)

①移动构造

//普通拷贝构造
string(const string& s):_str(nullptr)
{cout << "string(const string& s) -- 拷⻉构造" << endl;reserve(s._capacity);for (auto ch : s){push_back(ch);}
}
//移动构造
void swap(string& s)
{::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}
string(string&& s)
{cout << "string(string&& s) -- 移动构造" << endl;swap(s);
}

②移动赋值

//普通赋值
string& operator=(const string& s)
{cout << "string& operator=(const string& s) -- 拷⻉赋值" <<endl;if (this != &s){_str[0] = '\0';_size = 0;reserve(s._capacity);for (auto ch : s){push_back(ch);}}return *this;
}
// 移动赋值
string& operator=(string&& s)
{cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;
}

6、引用折叠

①C++中不可以直接定义引用的引用 int& && r = i;这样写会报错,通过模板或者typedef可以构成引用的引用

②折叠规则:右值引用的右值引用折叠成右值引用,其他的都折叠成左值引用

③typedef的折叠

typedef int& lref;
typedef int&& rref;
int n = 0;lref& r1 = n;//int&
lref&& r2 = n;//int&
rref& r3 = n;//int&
rref&& r4 = 1;//int&&

④模板的折叠

template<class T>
void f1(T& x)
{ }template<class T>
void f2(T&& x)
{ }

(1)无折叠->实例化为void f1(int& x)

f1<int>(n);
//f1<int>(0);//会报错,不能传右值

(2)折叠->实例化为void f1(int& x)

f1<int&&>(n);
//f1<int&&>(0);//报错
f1<int&>(n);
//f1<int&>(0);//报错

(3)折叠->实例化为void f1(const int& x)

f1<const int&>(n);
f1<const int&>(0);
f1<const int&&>(n);
f1<const int&&>(0);

(4)没有折叠->实例化为void f2(int&& x)

//f2<int>(n); // 报错
f2<int>(0);

(5)折叠->实例化为void f2(int& x)

f2<int&>(n);
//f2<int&>(0); // 报错

(6)折叠->实例化为void f2(int&& x)

//f2<int&&>(n); // 报错
f2<int&&>(0);

7、完美转发

forward:传什么值调用对应的函数重载

8、新的类功能

①C++新增了两个默认成员函数,移动构造函数和移动赋值运算符重载

②如果没有自己实现移动构造函数,且没有实现析构函数、拷贝构造、拷贝复制重载中的任意一个。那么编译器会自动生成一个默认移动构造数,对于内置类型成员会执 ⾏逐成员按字节拷⻉,⾃定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调⽤

移动构造,没有实现就调⽤拷⻉构造。

三、可变参数模板

1、基本语法

template<typename... Args>  // Args 是一个参数包(Parameter Pack)
void func(Args... args);    // args 是参数包实例
  • Args... 表示 类型参数包(可以匹配任意数量的类型)。

  • args... 表示 值参数包(可以匹配任意数量的参数)。

2、参数包

由于参数包无法直接遍历,通常使用递归模板

//终止条件
void print()
{cout << endl;
}
//递归展开参数包
template<class T, class... Args>
void print(T first, Args... args)
{cout << first << " ";print(args...);//递归调用
}int main()
{print(1, 2.4, "hello", 3);return 0;
}

四、lambda

1、表达式语法

①lambda表达式本质是一个匿名函数对象,跟普通函数不同的时它可以定义在函数内部

lambda没有类型,我们一般用auto或模板参数定义的对象去接受lambda对象

②lambda表达式

[capture - list] (parameters)->return type{function body
}

③[capture-list]:捕捉列表,可以捕捉上下文中的变量供lambda函数使用,全局函数不写也可以调用,其他的除参数列表内的参数,都无法自动调用

④(parameters):参数列表,和普通函数的参数列表功能类似,如果不需要参数传递,则可以连()一起省略

⑤->return type:返回值类型

⑥{function body}:函数体

2、示例

3、捕捉列表

①全局变量不用捕捉就能调用

int x = 0;
auto func1 = []()
{x++;
};

②在捕捉列表中的变量默认不能修改,想要修改前边加&

int a = 0, b = 1, c = 2, d = 3;
auto func1 = [a, &b]{//a++;//不能修改b++;//可以修改,因为b前边有&int ret = a + b;return ret;};
cout << func1() << endl;
//b=2

③隐式捕捉:调用了哪些变量就捕捉哪些变量

auto func2 = [=]{int ret = a + b + c;return ret;};

④隐式引用捕捉:用了哪些变量就捕捉哪些变量

auto func3 = [&]{a++;b++;c++;};

⑤混合捕捉1:除a和b外,其他都可修改

auto fucn4 = [&, a, b]{//a++;//b++;c++;d++;};

⑥混合捕捉2:除a和b外,其他都不可修改

auto func5 = [=, &a, &b]{a++;b++;//c++;//报错//d++;//报错};

mutable:相当于去掉const属性,可以修改了,但是不会影响外边的值

五、包装器

1、function

(1)定义 

function的实例对象可以包装存储其他的可以调用对象,包括函数指针、仿函数、lambda、bind等表达式,存储的可调用对象被称为function的目标。若function不含目标,则称它为空。调用空function会抛异常

(2)、示例

①对象

#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;
};

②调用演示

包装各种可调用对象

包装静态成员函数,成员函数要指定类域并且前边+&才能获取地址

包装普通成员函数,普通成员函数还有一个隐含的this指针参数,所以绑定时传对象或者对象指针过去都可以

(3)、算法对比

150. 逆波兰表达式求值 - 力扣(LeetCode)

①传统方式实现

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();}
};

②C++11实现

使⽤map映射stringfunction的⽅式实现  这种⽅式的最⼤优势之⼀是⽅便扩展,假设还有其他运算,我们增加map中的映射即可

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st;// function作为map的映射可调⽤对象的类型map<string, function<int(int, int)>> opFuncMap = {{"+", [](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; }}};for (auto& str : tokens){if (opFuncMap.count(str)) // 操作符{int right = st.top();st.pop();int left = st.top();st.pop();int ret = opFuncMap[str](left, right);st.push(ret);}else{st.push(stoi(str));}}return st.top();}
};

2、bind

(1)bind 是⼀个函数模板,它也是⼀个可调⽤对象的包装器,可以把他看做⼀个函数适配器,对接收 的fn可调⽤对象进⾏处理后返回⼀个可调⽤对象。 bind 可以⽤来调整参数个数和参数顺序。

bind 也在<functional>这个头⽂件中。

(2)调用bind的一般形式:auto newCallable = bind(callable,arg_list);

(3)使用

#include<functional>
using namespace std::placeholders;
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;}
};

bind有顺序

调整参数个数

(4)bind计算利息

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)> func3_1_5 = bind(func1, 0.015, _1, 3);
function<double(double)> func5_1_5 = bind(func1, 0.015, _1, 5);
function<double(double)> func10_2_5 = bind(func1, 0.025, _1, 10);
function<double(double)> func20_3_5 = bind(func1, 0.035, _1, 30);
cout << func3_1_5(1000000) << endl;
cout << func5_1_5(1000000) << endl;
cout << func10_2_5(1000000) << endl;cout << func20_3_5(1000000) << endl;

http://www.dtcms.com/a/336225.html

相关文章:

  • SpringCloud 02 服务治理 Nacos
  • (二)Python + 地球信息科学与技术 (GeoICT)=?
  • 机器学习--数据清洗
  • Python知识点汇总
  • 人工智能训练师复习题目实操题1.2.1 - 1.2.5
  • 4.Ansible自动化之-部署文件到主机
  • Mac(五)自定义鼠标滚轮方向 LinearMouse
  • 【网络通信】TCP/IP 协议全方位解析​
  • 计算机网络 TCP、UDP 区别
  • 云原生俱乐部-RH134知识点总结(2)
  • mediamtx v1.14.0版本全面解析:RTP流接收、IPv6支持与性能监控体系升级​
  • 如何做HTTP优化
  • Python 项目里的数据清理工作(数据清洗步骤应用)
  • 芯片行业主要厂商
  • Java 大视界 -- 基于 Java 的大数据分布式计算在气象灾害预警与应急响应中的应用
  • LeetCode 837.新 21 点:动态规划+滑动窗口
  • 动态规划法 - 53. 最大子数组和
  • MySQL数据库基础操作指南:从创建到管理的完整流程
  • Linux系统中6种替代top的工具
  • SparkSQL性能优化实践指南
  • ubuntu 24.04 安装
  • RAC环境redo在各节点本地导致数据库故障恢复---惜分飞
  • 云智智慧停充一体云-allnew全新体验-路内停车源码+路外停车源码+充电桩源码解决方案
  • 从零配置YOLOv8环境:RTX 3060显卡完整指南
  • 43.安卓逆向2-补环境-使用unidbg(使用Smali语法调用方法和使用方法地址调用方法)
  • n2n局域网搭建
  • 0-12岁幼儿启蒙与教育
  • Linux操作系统远程连接
  • 代码管理系统简介与部署
  • 《告别 if-else 迷宫:Python 策略模式 (Strategy Pattern) 的优雅之道》