网站正在备案中模板,如何写好一篇软文,做网站js还是jq,阳泉营销型网站建设1.统一的列表初始化
在C98中#xff0c;标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
//统一的列表初始化
struct Date
{int year;int month;int day;
};void test1()
{Date d1 { 2024,11,14 };int array1[] { 1, 2, 3, 4, 5 };int array2[5] { …1.统一的列表初始化
在C98中标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
//统一的列表初始化
struct Date
{int year;int month;int day;
};void test1()
{Date d1 { 2024,11,14 };int array1[] { 1, 2, 3, 4, 5 };int array2[5] { 0 };}
C11扩大了用大括号括起的列表(初始化列表)的使用范围使其可用于所有的内置类型和用户自 定义的类型使用初始化列表时可添加等号()也可不添加。
//统一的列表初始化
struct Date
{int year;int month;int day;
};void test1()
{Date d1{ 2024,11,14 };const Date d2{ 2024,11,14 };int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };}
1.1.std::initializer_list
std::initializer_list的介绍文档 http://www.cplusplus.com/reference/initializer_list/initializer_list/
std::initializer_list一般是作为构造函数的参数C11对STL中的不少容器就增加 std::initializer_list作为参数的构造函数这样初始化容器对象就更方便了。也可以作为operator 的参数这样就可以用大括号赋值。
//用std::initializer_list初始化容器
vectorint v1 {2024,11,4,17,30};
mapstring,string dict {{sort,排序},{sort1,排序1},{sort2,排序3}}; 2. 声明
c11提供了多种简化声明的方式
2.1.auto
C11中将auto用于实现自动类型推断。这样要求必须进行显示初始化让编译器将定义对象的类型设置为初始化值的类型。
vectorint v {2,0,2,4,11,14};//auto推导e为v的元素的类型
for(auto e : v)
{cout e ;
}//e自动推导为const char*类
auto e str;
2.1.decltype
关键字decltype将变量的类型声明为表达式指定的类型。
//decltype的使用场景
const int x 1;
double y 2.2;
decltype(x * y) ret; // ret的类型是double
decltype(x) p; // p的类型是int*
vectordecltype(x) v;
2.3.nullptr
由于C中NULL被定义成字面量0这样就可能回带来一些问题因为0既能指针常量又能表示 整形常量。所以出于清晰和安全的角度考虑C11中新增了nullptr关键字用于表示空指针。
3.范围for
3.1范围for的使用条件
1for循环迭代的范围必须是确定的 对于数组而言就是数组中第一个元素和最后一个元素的范围对于类而言应该提供 begin和end的成员函数返回其迭代器并且要实现operatorbegin和end就是for循环迭代的范围。
vectorint v {2,0,2,4,11,14};//范围for会依次取容器中的值
for(auto e : v)
{cout e ;
}for(int e : v)
{cout e ;
}
4.STL容器的变化
array //对静态数组的封装,operator[]会检查封装但vector完全够用
https://cplusplus.com/reference/array/
forward_list //单向链表
https://cplusplus.com/reference/forward_list/
unordered_map
https://cplusplus.com/reference/unordered_map/
unordered_set
https://cplusplus.com/reference/unordered_set/ 5.右值引用和移动语义
5.1.什么是左值、右值什么是左值、右值引用
简单来说
左值可以取地址我们可以获取它的地址可以对非const左值赋值
左值可以出现在赋值对象左边
右值不可以取地址通常为常量、临时对象、匿名对象
右值不可以出现在赋值对象左边
// 以下的p、b、c、*p都是左值int* p new int(0);int b 1;const int c 2;// 以下几个是对上面左值的左值引用int* rp p;int rb b;const int rc c;int pvalue *p;double x 1.1, y 2.2;// 以下几个都是常见的右值10;x y;fmin(x, y);// 以下几个都是对右值的右值引用int rr1 10;double rr2 x y;double rr3 fmin(x, y);// 这里编译会报错error C2106: “”: 左操作数必须为左值/表达式必须是可修改的左值10 1;x y 1;fmin(x, y) 1;
左值引用、右值引用之间的相互转换不能直接转换
// 左值引用只能引用左值不能引用右值。
int a 10;
int ra1 a; // ra为a的别名
//int ra2 10; // 编译失败因为10是右值
// const左值引用既可引用左值也可引用右值。
const int ra3 10;
const int ra4 a;
// 右值引用只能右值不能引用左值。
int r1 10;// error C2440: “初始化”: 无法从“int”转换为“int ”
// message : 无法将右值引用绑定到左值
int a 10;
int r2 a;// 右值引用可以引用move以后的左值
int r3 std::move(a);
//move的实质相当于
int rrx5 (int)a;
5.2.右值引用的意义
右值引用可以减少拷贝
左值引用解决的场景引用传参、引用传返回值
左值引用没有解决的场景传返回值
右值可以分为两种
1.纯右值内置类型右值
2.将亡值类、结构体类型右值生命周期只在当前作用域的
那右值引用怎么减少拷贝呢
移动构造
class string
{
public://移动构造string(string s):str(nullptr){//移动拷贝抢夺资源//所以右值引用即为标记//s为右值引用可能是将亡值会直接将s的资源抢给自己swap(s,*this);}
private:char* str;};
稍早一点的编译器C11之前在实践使用中要求避免对象传值返回为节省资源C11出现后彻底解决了这样的问题
当然右值引用使用的场景除了移动构造外还有移动赋值等即用此种方法实现operator()C11后STL中也用了这样的设计 另外还需注意右值引用本身的属性是——左值如果还是右值无法修改
为什么呢因为只有右值引用本身属性是左值才能传递他的资源
即传递过程中会退化
//r1的属性为左值
string r1 string(2222);
5.3.函数模版中的引用
左值引用、右值引用在模板中的使用值得注意
templateclass T //引用折叠
void Func(T x) //传左值即为左值引用传右值即为右值引用
{} // 模板中的不代表右值引用而是万能引用其既能接收左值又能接收右值。 // 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力 // 但是引用类型的唯一作用就是限制了接收的类型后续使用中都退化成了左值 // 我们希望能够在传递过程中保持它的左值或者右值的属性, 就需要用我们下面学习的完美转发 5.4.完美转发
完美转发要用的forward函数它包含在utility中。 forward - C Referencey
右值引用本身属性是——左值为什么呢
因为只有右值引用本身属性是左值才能转移他的资源。即右值在传递过程中会退化。
而在语法层我们无法判断一个引用本质引用的左值还是右值完美转发可以解决这个问题。
若t为左值保持属性直接传参给Fun。
若t为右值传入时右值引用属性会退化成左值forward会将其转换成右值再传给Func。
void Fun(int x) { cout 左值引用 endl; }
void Fun(const int x) { cout const 左值引用 endl; }
void Fun(int x) { cout 右值引用 endl; }
void Fun(const int x) { cout const 右值引用 endl; }templatetypename T
void PerfectForward(T t)
{//模板实例化左值引用,保持属性直接传参给Fun//模板实例化右值引用右值引用属性会退化成左值转换成右值再传给FuncFun(forwardT(t));
} 6.新的类功能
C11 新增了两个默认成员函数移动构造函数和移动赋值运算符重载。
移动构造函数和移动赋值运算符重载
如果你没有自己实现移动构造函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。那么编译器会自动生成一个默认移动构造。如果你没有自己实现移动赋值重载函数且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任 意一个。那么编译器会自动生成一个默认移动构造。如果你提供了移动构造或者移动赋值编译器不会自动提供拷贝构造和拷贝赋值。
默认移动构造会将内置类型按字节拷贝自定义类型实现了移动构造调移动构造没实现调用其拷贝构造。移动赋值与移动构造类似。
需要显式实现析构说明有资源需要释放则
说明需要显式写拷贝构造和赋值重载说明需要显式写移动构造和移动赋值
默认生成的移动构造对Date这样的没有申请资源的类没什么意义与拷贝构造功能一样对Person这样申请了资源的类很有意义。Person为右值时内部的string也是右值string就可以走移动构造提高效率。
强制生成默认函数的关键字default:
C11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数但是因为一些原 因这个函数没有默认生成。比如我们提供了拷贝构造就不会生成移动构造了那么我们可以 使用default关键字显示指定移动构造生成。
class HashTable
{
public:HashTable() default
}
禁止生成默认函数的关键字delete:
如果能想要限制某些默认函数的生成在C98中是该函数设置成private并且只声明补丁 已这样只要其他人想要调用就会报错。在C11中更简单只需在该函数声明加上delete即 可该语法指示编译器不生成对应函数的默认版本称delete修饰的函数为删除函数。 class HashTable
{
public:HashTable() delete
}
7.可变参数模板
C11的新特性可变参数模板能够让你创建可以接受可变参数的函数模板和类模板。可变参数模板的参数类型个数可变。
下面就是一个基本可变参数的函数模板
template class ...Args//Args是一个模板参数包
void ShowList(Args... args)//args是一个函数形参参数包
{//可变模板参数模板编译时解析cout sizeof...(args)endl;
}由于语法不支持使用args[i]这样方式获取可变 参数所以我们的用下面的方法获取参数包的值。 递归函数方式展开参数包
//解析方式1
//编译时递归推导解析参数
void Print()
{}
templateclass T,class ...Args
void Print(T x, Args ...args)
{cout x ;//参数包参数数量为零时会调用无参的重载函数Print(args...);
}
templateclass ...Arg
void showList(Args ...args)
{Print(args...);
}
逗号表达式展开参数包
expand函数中的逗号表达式(printarg(args), 0)也是按照这个执行顺序先执行 printarg(args)再得到逗号表达式的结果0。通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。由于是逗号表达式在创建数组的过程中会先执行逗号表达式前面的部分printarg(args) 打印出参数也就是说在构造int数组的过程中就将参数包展开了这个数组的目的纯粹是为了在数组构造的过程展开参数包。
template class T
void PrintArg(T t)
{cout t ;
}
//展开函数
template class ...Args
void ShowList(Args... args)
{int arr[] { (PrintArg(args), 0)... };//int arr[] {(cout (args) ,0)...};cout endl;
}emplace系列的接口支持模板的可变参数并且万能引用。emplace系列可以将参数包向下传递就可以直接调取底层元素的构造函数。 emplace系列总体而言更加高效推荐使用。特别是对于Date这种没有拷贝构造、移动构造概念的类很高效。
int main()
{std::list std::pairint, 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;
}
int main()
{// 带有拷贝构造和移动构造的bit::string// 其实差别也不大emplace_back是直接构造了push_back// 是先构造再移动构造。std::list std::pairint, bit::string mylist;mylist.emplace_back(10, sort);mylist.emplace_back(make_pair(20, sort));mylist.push_back(make_pair(30, sort));mylist.push_back({ 40, sort});return 0;
}8.lambda表达式
格式 捕捉列表 参数 可修改性 返回值 函数体 [capture_list] (parameters)mutable-returntype{statement} √ 可省 可省 可省 √ [capture-list] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda 函数使用。
(parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以连同()一起省略
mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量性。使用该修饰符时参数列表不可省略(即使参数为空)。
-returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推 导。
{statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。 // 最简单的lambda表达式, 该lambda表达式没有任何意义[]{}; // 省略参数列表和返回值类型返回值类型由编译器推导为intint a 3, b 4;[]{return a 3; }; // 省略了返回值类型无返回值类型auto fun1 [](int c){b a c; }; fun1(10)couta bendl;// 各部分都很完善的lambda函数auto fun2 [, b](int c)-int{return b a c; }; coutfun2(10)endl;// 复制捕捉xint x 10;auto add_x [x](int a) mutable { x * 2; return a x; }; cout add_x(10) endl; lambda表达式其本质为匿名函数对象 捕捉列表
int main()
{int a 1, b 2;//lambda只能用当前lambda局部域和捕捉的对象和全局对象//传值捕捉本质是一种拷贝并且const修饰了auto swap2 [a, b]()mutable//mutable慎用,mutable即——可修改的{ //mutable相当于去掉const属性可以修改了//修改不会影响外面被捕捉的值因为是一种拷贝int tmp a;a b;b tmp;};//引用捕捉默认可修改auto swap3 [a, b](){};}
[]//所有值的传值捕捉本质上只会捕捉函数体中使用了的对象 []//所有值引用捕捉本质上只会捕捉函数体中使用了的对象 [a,b]//混合捕捉 [,b]//除b外其他全部引用捕捉
底层
捕捉列表的对象是成员变量存储在lambda类对象中
捕捉的本质是构造函数的初始化列表
注意
1. 父作用域指包含lambda函数的语句块
2. 语法上捕捉列表可由多个捕捉项组成并以逗号分割。 比如[, a, b]以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量 [a, this]值传递方式捕捉变量a和this引用方式捕捉其他变量
3. 捕捉列表不允许变量重复传递否则就会导致编译错误。 比如[, a]已经以值传递方式捕捉了所有变量捕捉a重复
4. 在块作用域以外的lambda函数捕捉列表必须为空。
5. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者 非局部变量都 会导致编译报错。 f. lambda表达式之间不能相互赋值即使看起来类型相同
9.包装器
function包装器 function包装器 也叫作适配器。可以将1.函数指针 2.仿函数 3.lambda包装起来。
function - C Reference
function在头文件functional 模板参数说明 Ret: 被调用函数的返回类型 Args…被调用函数的形参 包装器使用方法如下
int f(int a, int b)
{return a b;
}
struct Functor
{
public:int operator() (int a, int b){return a b;}
};
class Plus
{
public:static int plusi(int a, int b){return a b;}double plusd(double a, double b){return a b;}
};int main()
{// 函数名(函数指针)std::functionint(int, int) func1 f;cout func1(1, 2) endl;// 函数对象std::functionint(int, int) func2 Functor();cout func2(1, 2) endl;// lamber表达式std::functionint(int, int) func3 [](const int a, const int b){return a b; };cout func3(1, 2) endl;// 包装静态成员函数std::functionint(int, int) func4 Plus::plusi;cout func4(1, 2) endl;//包装普通成员函数因为普通成员函数隐患this指针第一个参数传对象或指针//因为底层无论是对象或指针都是通过对象调成员函数std::functiondouble(Plus*, double, double) func5 Plus::plusd;cout func5(Plus(), 1.1, 2.2) endl;std::functiondouble(Plus, double, double) func6 Plus::plusd;cout func6(Plus(), 1.1, 2.2) endl;return 0;
}
还可以用map将对象与函数进行映射
//用operator[]进行访问
map string, functionint(int, int) opFuncMap; bind
一般用于绑定一些固定参数bind的返回值返回一个仿函数 using placeholders::_1;
using placeholders::_2;
int main()
{//调整参数顺序auto sub1 bind(sub, _1, _2);//_1代表第一个实参_2代表第二个实参以此类推auto sub2 bind(sub, _2, _1);cout sub2(10, 5) endl;//调整参数个数常用auto sub3 bind(sub, 100, _1);//绑定死第一个形参cout sub3(20) endl;}