电信宽带做网站,大连网站策划,功能型pc端网站框架,健康网站模版一、列表初始化
C98#xff1a;
在C98中#xff0c;标准允许使用花括号{}对数组元素进行统一的列表初始值设定。
struct Simple1
{int _a;int _b;
};//C98
int main()
{int a1[] { 1,2,3,4,5,6 };int a2[7] { 0 };//本质是类型转换#xff08;构造拷贝构造 - 优化 …一、列表初始化
C98
在C98中标准允许使用花括号{}对数组元素进行统一的列表初始值设定。
struct Simple1
{int _a;int _b;
};//C98
int main()
{int a1[] { 1,2,3,4,5,6 };int a2[7] { 0 };//本质是类型转换构造拷贝构造 - 优化 直接构造Simple1 s1 { 1,2 };return 0;
}
C11:
C11扩大了用大括号括起的列表(初始化列表)的使用范围使其可用于所有的内置类型和用户自定义的类型使用初始化列表时可添加等号()也可不添加。列表初始化可以在{}之前使用等号其效果与不使用没有什么区别。
实际是因为C11新增了一个initializer_listinitializer_list是系统自定义的类模板该类模板中主要有三个方法begin()、end()迭代器以及获取区间中元素个数的方法size()。
多个对象想要支持列表初始化只需给该类(模板类)添加一个带有initializer_list类型参数的构造函数即可。 //C11
//一切都可以用列表初始化
int main()
{// 内置类型变量int x1 { 1 };int x2{ 1 };int x3 1 1;int x4 { 1 1 };int x5{ 1 1 };// 数组int arr1[5]{ 1,2,3,4,5 };int arr2[]{ 1,2,3,4,5 };// 动态数组在C98中不支持int* arr3 new int[5]{ 1,2,3,4,5 };// 标准容器vectorint v1 { 1,2,3,4,5 };vectorint v{ 1,2,3,4,5 };mapint, int m1 { {1,1}, {2,2,},{3,3},{4,4} };mapint, int m{ {1,1}, {2,2,},{3,3},{4,4} };//标准库支持单个对象的列表初始化Simple1 s{ 1, 2 };return 0;
}
二、声明
1、auto
C11中可以使用auto来根据变量初始化表达式类型推导变量的实际类型可以给程序的书写提供许多方便。将程序中it的类型换成auto程序可以通过编译而且更加简洁。
int main()
{mapstring, string m{ {menu, 菜单}, {delete,删除} };// 使用迭代器遍历容器, 迭代器类型太繁琐//mapstring, string::iterator it m.begin();//使用autoauto it m.begin();while (it ! m.end()){cout it-first it-second endl;it;}return 0;
}
2、decltype
auto使用的前提是必须要对auto声明的类型进行初始化否则编译器无法推导出auto的实际类型。但有时候可能需要根据表达式运行完成之后结果的类型进行推导因为编译期间代码不会运行此时auto也就无能为力。
而decltype是根据表达式的实际类型推导出定义变量时所用的类型。这个类型可以用来做实例化模版实参或者再定义对象。
int main()
{int a 11;float b 10.11;auto ret a * b;vectordecltype(ret) v{ 12,12.12,14.1 };for (auto e : v){cout e endl;}return 0;
} 三、范围for
范围for在底层实际是被替换成了迭代器。
int main()
{vectorint v{ 1,4,6,2,5,11 };for (auto e : v){cout e ;}cout endl;return 0;
} 四、智能指针
由于智能指针内容较多请查看智能指针 五、新增容器
静态数组array
arrayint,10 arr { 1,2,3,4,5 };
forward_list
forward_list实际上就是单链表区别于双向链表list
unordered系列
unordered_set/unordered_map
参考哈希 六、右值引用
1、左值与右值
一般认为 普通类型的变量因为有名字可以取地址都认为是左值。const修饰的常量不可修改只读类型的理论应该按照右值对待但因为其可以取地址(如果只是const类型常量的定义编译器不给其开辟空间如果对该常量取地址时编译器才为其开辟空间)C11认为其是左值。如果表达式的运行结果是一个临时变量或者对象认为是右值。如果表达式运行结果或单个变量是一个引用则认为是左值。
C11对右值进行了严格的区分
C语言中的纯右值比如ab, 100将亡值。比如表达式的中间结果、函数按照值的方式进行返回。
2、左值引用与右值引用
左值引用
int main()
{int* p1 new int[10]{ 1,2,3,4,5,6,7,8,9,99 };int* r1 p1;int a 1;int r2 a;const int r3 10;return 0;
}
右值引用
int main()
{int r1 10;int r2 1 3;int i 11;int r3 i 11;int rr move(i);return 0;
}
注右值引用可以给move(左值取别名。不过不要轻易使用move有时候会产生意想不到的结果。
3、移动构造/移动赋值
C11中新增了两个默认成员函数移动构造函数、移动赋值运算符重载。
如果我们没有实现移动构造函数且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一个都没有写那么编译器会 自动生成一个默认移动构造。默认生成的移动构造函数对于内置类型成员会逐成员按字节拷贝自定义类型成员则看它是否实现了移动构造若实现了则调用否则调用拷贝构造。移动赋值重载与其完全类似
不过在C11中可以在默认函数定义或者声明时加上default从而显式的指示编译器生成该函数的默认版本用default修饰的函数称为显式缺省函数。强制编译器生成
此外如果能想要限制某些默认函数的生成在C98中是该函数设置成private并且不给定义这样只要其他人想要调用就会报错。在C11中更简单只需在该函数声明加上delete即可该语法指示编译器不生成对应函数的默认版本称delete修饰的函数为删除函数。 C11提出了移动语义概念即将一个对象中资源移动到另一个对象中。
这样就解决了 由于函数返回值的生命周期函数返回时一般是创建一个临时对象用该临时对象构造接收函数返回值的变量问题无法使用引用返回 的问题。
而在C11中如果需要实现移动语义必须使用右值引用。
如
String(String s)
: _str(s._str)
{s._str nullptr;
}
注
移动构造函数的参数不能设置成const类型的右值引用因为资源无法转移而导致移动语义失效。在C11中编译器会为类默认生成一个移动构造该移动构造为浅拷贝因此当类中涉及到资源管理时用户必须显式定义自己的移动构造。右值被右值引用引用后的属性是左值。因为右值不能直接修改但是右值被右值引用后需要被修改否则无法实现移动构造和移动赋值。
当需要用右值引用引用一个左值时可以通过move函数将左值转化为右值。C11中std::move()函数位于 头文件中该函数名字具有迷惑性它并不搬移任何东西唯一的功能就是将一个左值强制转化为右值引用然后实现移动语义。 4、完美转发
完美转发
在函数模板中完全依照模板的参数的类型将参数传递给函数模板中调用的另外一个函数
函数模板在向其他函数传递自身形参时如果相应实参是左值它就应该被转发为左值如果相 应实参是右值它就应该被转发为右值。
C11通过forward函数来实现完美转发
void Fun(int x)
{cout lvalue ref endl;
}void Fun(int x)
{ cout rvalue ref endl;
}void Fun(const int x)
{cout const lvalue ref endl;
}void Fun(const int x)
{ cout const rvalue ref endl;
}templatetypename T
void PerfectForward(T t)
{ Fun(forwardT(t));
}
int main()
{PerfectForward(10); // rvalue refint a;PerfectForward(a); // lvalue refPerfectForward(move(a)); // rvalue refconst int b 8;PerfectForward(b); // const lvalue refPerfectForward(move(b)); // const rvalue refreturn 0;
} 七、可变参数模版
比如list中的emplace_back就是使用了它。 输出大小
//Args是一个模版参数包args是一个函数形参参数包
//声明一个模版参数包Args...args,这个参数包中可以包含0到任意个模版参数
templateclass ...Args
void show_list(Args... args)
{cout sizeof...(args) endl;
}int main()
{show_list(1, 1, 1);show_list(a,aa);show_list(1.1, a);show_list(1, 1.1, a, xx);return 0;
} 输出每个元素
void _show_list()
{cout endl;
}
//编译时推演
//第一个模版参数依次解析获取参数值
templateclass T,class ...Args
void _show_list(const T val, Args ...args)
{cout val ;_show_list(args...);
}templateclass ...Args
void show_list(Args... args)
{_show_list(args...);
}int main()
{show_list(1, 1, 1);show_list(a,aa);show_list(1.1, a);show_list(1, 1.1, a, xx);return 0;
} 八、 lambda表达式
lambda表达式解决了需要写仿函数重载operator()的问题尤其每次比较的逻辑不一样需要去实现多个类特别是相同类的命名。
语法
lambda表达式书写格式
[capture-list] (parameters) mutable - return-type { statement } lambda表达式各部分说明
[capture-list] : 捕捉列表该列表总是出现在lambda函数的开始位置编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda函数使用。(parameters)参数列表。与普通函数的参数列表一致如果不需要参数传递则可以连同()一起省略mutable默认情况下lambda函数总是一个const函数mutable可以取消其常量性。使用该修饰符时参数列表不可省略(即使参数为空)。-returntype返回值类型。用追踪返回类型形式声明函数的返回值类型没有返回值时此部分可省略。返回值类型明确情况下也可省略由编译器对返回类型进行推导。{statement}函数体。在该函数体内除了可以使用其参数外还可以使用所有捕获到的变量。
注意
在lambda函数定义中参数列表和返回值类型都是可选部分而捕捉列表和函数体可以为空。因此C11中最简单的lambda函数为[]{}; 该lambda函数不能做任何事情。捕捉列表描述了上下文中哪些数据可以被lambda使用以及使用的方式是传值还是传引用。 [var]表示值传递方式捕捉变量var []表示值传递方式捕获所有父作用域中的变量(包括this) [var]表示引用传递捕捉变量var []表示引用传递捕捉所有父作用域中的变量(包括this) [this]表示值传递方式捕捉当前的this指针父作用域指包含lambda函数的语句块语法上捕捉列表可由多个捕捉项组成并以逗号分割。捕捉列表不允许变量重复传递否则就会导致编译错误。 在块作用域以外的lambda函数捕捉列表必须为空。在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者非局部变量都会导致编译报错。lambda表达式之间不能相互赋值即使看起来类型相同
使用
int main()
{int a 1;int b 2;cout a b endl;auto f1 [](int a, int b){int tmp a;a b;b tmp;};f1(a, b);cout a b endl;return 0;
} int main()
{int a 1;int b 2;cout a b endl;//通过捕捉列表传引用auto f2 [a, b] {int tmp a;a b;b tmp;};f2();cout a b endl;return 0;
}
class Test
{
public:void func(){auto f []{cout _a _b endl;};}private:int _a 2;int _b 4;
};int main()
{Test t;t.func();return 0;
} 九、包装器适配器
1、function
function包装器包装的是函数指针类型用起来反人类、仿函数需在全局定义、lambda类型对我们是匿名的中的任意一个。
bool Comp(int a, int b)
{return a b;
}struct Comps
{bool operator()(int a, int b){return a b;}
};#includefunctional
#includemap
int main()
{auto complambda [](int a, int b) -bool{return a b;};map string, functionbool(int, int) m{{函数指针,Comp},{仿函数,Comps()},{lambda,complambda}};int x 1;int y 2;cout m[函数指针](x, y) endl;cout m[仿函数](x, y) endl;cout m[lambda](x, y) endl;return 0;
} 包装成员函数
静态成员函数static可以不用加“”。
成员函数取地址比较特殊需要加上类域和。
struct AAA
{
public:static void A(int a){a 0;}void AA(float a){a 1.1;}};int main()
{functionvoid(int) f1 AAA::A;f1(1);functionvoid(AAA*, float) f2 AAA::AA;AAA a;f2(a, 2.2); //注意不能使用匿名对象因为右值不可以取地址functionvoid(AAA, float) f3 AAA::AA;f3(AAA(), 2.2);return 0;
}
2.bind
bind是一个函数模板它就像一个函数包装器(适配器)接受一个可调用对象callable object生成一个新的可调用对象来“适应”原对象的参数列表。
struct AAA
{
public:static void A(int a){cout a endl;}void AA(float a){cout a endl;}};void aaa(int a, int b)
{cout a b endl;
}int main()
{functionvoid(int) f1 AAA::A;f1(1);functionvoid(AAA*, float) f2 AAA::AA;AAA a;f2(a, 2.2); //注意不能使用匿名对象因为右值不可以取地址functionvoid(AAA, float) f3 AAA::AA;f3(AAA(), 2.2);//调整传参顺序functionvoid(int, int) ff1 bind(aaa, placeholders::_2, placeholders::_1);ff1(3, 5);//调整参数个数functionvoid(int) ff2 bind(aaa, 22, placeholders::_1);ff2(5);functionvoid(float) ff3 bind(AAA::AA, AAA(), placeholders::_1);ff3(100.1);return 0;
} 可以将bind函数看作是一个通用的函数适配器它接受一个可调用对象生成一个新的可调用对 象来“适应”原对象的参数列表。 调用bind的一般形式auto newCallable bind(callable,arg_list); 其中newCallable本身是一个可调用对象arg_list是一个逗号分隔的参数列表对应给定的 callable的参数。当我们调用newCallable时newCallable会调用callable,并传给它arg_list中 的参数。 arg_list中的参数可能包含形如_n的名字其中n是一个整数这些参数是“占位符”表示 newCallable的参数它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对 象中参数的位置_1为newCallable的第一个参数_2为第二个参数以此类推。