微信上的网站怎么做网站开发怎样验收

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

微信上的网站怎么做,网站开发怎样验收,东莞seo建站优化工具,免费网站奖励自己的软件一、概述 C 的最初目标就是成为 “更好的 C”#xff0c;因此新的标准首先要对基本的底层编程进行强化#xff0c;能够反映当前计算机软硬件系统的最新发展和变化#xff08;例如多线程#xff09;。另一方面#xff0c;C对多线程范式的支持增加了语言的复杂度#xff0…一、概述 C 的最初目标就是成为 “更好的 C”因此新的标准首先要对基本的底层编程进行强化能够反映当前计算机软硬件系统的最新发展和变化例如多线程。另一方面C对多线程范式的支持增加了语言的复杂度通常一个范式就相当于其他的一门编程语言学习难度大。为了使能够让C吸引更多的用户避免“曲高和寡”的局面C标准组委会有指定了一些具体的设计目标 保持稳定性和兼容性尽量使用库而不是扩展语言来增加新特性对初级用户和高级用户都能提高良好的支持增强类型的安全性增强直接操作硬件时的效率和功能 新的C11/14 标准基本实现了这些目标而且依然较好地保持了与之前版本的兼容性。 二、左值与右值

  1. 定义 C11/14 标准描述了左值与右值的含义简略如下 所有表达式的结果不是左值就是右值左值lvalue是一个函数或者对象实例失效值xvalueexpiring value是生命周期即将结束的对象广义左值glvaluegeneralized lvalue包括左值和失效值右值rvalue包括失效值、临时对象、以及不关联对象的值如字面值纯右值pvalue是非失效值的那些右值 这里给一个简单的解释左值是一个可以用来存储数据的变量有实际的内存地址即变量名表达式结束后依然存在历史上它因在赋值操作符左边而得名而右值更准确来说是“非左值”是一个“匿名”的“临时”变量它在表达式结束时生命周期终止不能存放数据可以被修改以科研不被修改被const 修饰。基于此我们可以总结出鉴别左值和右值的简单方法 左值可以用取地址操作符 “” 获取地址右值则无法使用 “” 报错 int x 0; //对象实例有名x是左值 int* p x; //可以取地址x是左值 x 10; //前置返回的是左值可以赋值 p x; //后置返回的是一个临时对象不能取地址或者赋值是右值报错因为右值是“临时”的生命周期即将结束之后无人会关系它的值所以我们可以把它的所有内容转移到其他对象中消除昂贵的拷贝代价。
  2. 右值引用 有了右值的概念右值引用也应运而生C11/14 标准使用 “T” 的形式表示右值引用而原来的 “T” 则表示左值引用两者可以简称为右引用和左引用分别表示右值对象和左值对象但在C98 不能引用右值。对一个对象使用右值引用意味着显式地标记这个对象是右值可以被转移来优化。同时也为它添加了一个“临时的名字”生命周期得到了延长不会在表达式结束时消失而是与右值引用绑定到了一起。它们都可以被 const 修饰“const 是一个”万能引用“可以引用任何对象但增加了常量性虽然”const T 是正确的但因为右值引用对象是“临时的”即将消失对它增加常量性会使得它无法修改也无法转移没有实际意义。 int r1 x; //左值引用 int r2 x; //右值引用引用了自增对象后的临时对象xvalue const int r3 x; //常量左值引用也可以引用右值 const int r4 x; //常量右值引用无意义 cout r2 endl; //右引用延长生命期右值对象在表达式结束后仍然存在C11/14对此做出了新的规定——引用折叠对引用类型 TR可能是左引用或右引用再进行左引用操作是 T右引用操作则不变化仍然是TR因此函数参数如果使用 T 的形式将总保持不变。
  3. 转移语义 因为右值引用对象可以被转移进而优化代码所以C11/14标准头文件 utility 里专门定义了便捷函数 std::move()来实现“转移”对象它是一个模板函数声明如下 templateclass T typename remove_referenceT::type move(T t) noexcept;move() 函数其实并没有任何“转移”操作只是把一个对象明确地转换为“匿名”的右值引用也即是说对该对象确认是右值对象可以被安全地转移相当于 static_castT(t); //静态强制转换 转型为右值引用C11/14 标准为class 新增加了参数类型为 “T 的转移构造函数和转移赋值函数只要类实现了这两个特殊函数就能够利用右值对象”零成本“构造这就是转移语义。即现代C语言优化性能的重要手段只要对象被move()标记为右引用就可以转移资源不会进行”深拷贝“。以下实现转移构造函数和转移赋值函数 class moveable { private:int x; public:moveable() {}; //缺省构造函数moveable(moveable other) //转移构造函数{std::swap(x, other.x);}moveable operator(moveable other) //转移赋值函数{std::swap(x, other.x);return *this;} public:static moveable create() //工厂函数创建对象{moveable obj; //栈上创建对象return obj; //返回临时对象即右值会引发转移语义} };moveable类里有一个工厂函数它直接返回函数内的局部变量obj在函数返回时是一个临时对象也就是右值(无需再使用std::move()而moveable类被定义了转移构造函数所以可以直接使用临时对象的值来创建对象避免了拷贝的代价如 moveable m1; //缺省构造函数创建对象 moveable m2(std::move(m1)); //条用转移构造函数m1被转移 moveable m3 moveable::create(); //调用转移赋值函数C标准库里的string、vector、deque等组件都实现了转移构造函数和转移赋值函数可以利用转移语义优化所以现在在函数里返回一个大容器对象是非常高效的。这些标准容器std::array除外还特别增加了emplace() 系列函数可以使用语义直接插入元素进一步提高了运行性能如 vectorcomplexdouble v; //标准序列容器 v.emplace(3, 4); //直接使用右值插入元素无需构造再拷贝mapstring, int m; //标准映射容器 m.emplace(metroid, prime); //直接使用右值插入元素无需构造再拷贝4. 完美转发 标准头文件 utility 里还有一个函数std::forward()用于泛型编程时实现”完美转发“可以把函数的参数原封不动的转发给其他函数如下声明 template class T T forward(T t) noexcept; template class T T forward(T t) noexcept;forward()在使用时必须指定模板参数它引用C11/14 标准的引用折叠规则。 void check(int) //左值引用 {cout lvalue endl; }void check(int) //右值引用 { cout rvalue endl; }templatetypename T void print(T v) {check(std::forwardT(v)); //完美转发依据函数参数类型调用不同的函数 }int x 10; print(x); //传递左值引用输出 lvalue print(std::move(x)); //传递右值引用输出 rvalue 三、自动推导类型 C11/14 标准增加了两个关键字 auto 和 decltype 。它们可以推到出表达式的类型信息它们的推到能力可以极大的简化代码。
  4. auto C是一种强静态语言任何变量、表达式都要有明确的类型例如 long x 0L; //声明x为long类型 const char* s hello; //声明s为字符指针类型 对于简单的变量可以很容易写出它的类型但由于类、命名空间、模板等技术的扩展应用变量的类型逐渐变得越来越复杂有的类型名字甚至很难写出正确的类型 mapstring, string::iterator iter m.begin(); //迭代器 ??? f bind1st(std::lessint(), 2); //很难推导出正确的函数对象类型事实上编译器是知道这些表达式的类型的但在C11/14 之前这些信息都隐藏在编译器内部。C11/14 标准重新定义了auto关键字的语义能够在编译器自动推导出表达式的类型。 auto x 0L; //x为long类型 auto s hello; //s为字符指针类型 auto iter m.begin(); //迭代器 auto f bind1st(std::lessint(), 2); //推导出正确的函数对象类型auto的用法相当简单但也有几个需要注意的地方 auto 只能用于赋值语句里的类型推导不能直接声明变量auto 总数能推导出值类型非引用auto 运行使用”const / volatile / / * 等修饰符修饰从而得到新类型auto 总是推导出引用类型 下列代码示范了auto的更多用法: int x 0; const long y 100; volatile string s(one punch); auto a1 x; //值类型int auto a2 x; //引用类型 auto a3 y * y; //值类型long auto a4 y; //引用类型 auto a5 std::move(y); //值类型long右引用被忽略 auto a6 std::move(y); //引用类型const long const auto a7 x x; //常引用类型const int auto* a8 y; //const long*, auto本身推导为值类型 auto a9 s; //引用类型volatile string auto a10; //不是赋值初始化无法推导编译错误auto还可以用于函数的返回值声明处自动推导函数的返回值类型 auto func(int x) { return x * x; } //int现代C编程中应当尽量使用auto它不会右任何的效率损失而且带来了更好的返回值类型和可读性。
  5. decltype auto关键字能够在赋值语句里推导类型但这只是C语言里一种很少见的应用场景要想在任意场合都能得到表达式的类型需要使用另一个关键字decltype。它在技术和用法上与sizeof非常相似因为都需要编译器在编译期计算类型但sizeof返回的是整数而decltype返回的是类型。 decltype(expreesion) //获取表达式类型编译器计算decltype 可以像 auto 一样用在赋值语句但可以根据表达式的结果类别和表达式的性质推断出引用或非引用能够更精确地控制类型 decltype(x) d1 x; //int decltype(x) d2 x; //int* decltype(x) d3 x; //int decltype(x y) d4 x y; //long decltype(y) d5 y;//const long除了赋值语句decltype 还可以用在变量声明、类型定义、函数参数列表、模板参数列表等任意的地方因为它实际上就是一个编译器的类型名只是通过表达式计算得到 decltype(std::lessint()) func; //声明一个函数对象注意不是赋值语句decltype(0.0f) func(decltype(0L) x) {return x * x;} //用于函数返回值和参数声明typedef decltype(func)* fun_ptr; //简单地定义函数指针裂隙vectorint v; decltype(v)::iterator iter; //计算v的类型再取其迭代器类型templatetypename T class demo {};//模板类demodecltype(v) obj; //模板参数使用decltypeauto 和 decltype 用法一样但同样有语法细节需要注意 decltype(e) 的形式获得表达式计算结果的值类型decltype((e)) 的形式获得表达式计算结果的引用类型类似 auto 的效果 int x 0; //int const volatile int y 0; //直接对它所在内存读取数据而不是使用保存在寄存器的的备份 decltype(x) d1 x; //int decltype((x)) d2 x; //int decltype(y) d3 y; //const volatile int decltype((y)) d4 y; //const volatile int下面示例更好的解释decltype(x) 和 decltype((x)) 的区别 decltype(p-x) d5 42; //int decltype((p-x)) d6 p-x; //int decltype(p-x) d7 p-x; //报错这里我们声明了一个volatile的指针pdecltype(p-x) 只能得到的值类型int失去了volatile修饰而decltype((p-x)) 可以得到p-x 的真正引用类型。
  6. decltype(auto) auto和decltype这两个关键字都可以推导类型但用法有差异。auto的使用更加方便但用途有限只能用在赋值语句里 decltype用法广可以任意推导表达式的类型但使用时必须在括号内写全表达式用法不便。C14 标准增加了一种新的语法运行将两者结合起来即 “decltype(auto)” 使用decltyoe 的语义推导但用的却是 auto 语法如 //仅 C 14 decltype(auto) x 6; //int decltype(auto) y 7L; //long decltype(auto) z x y; //long 四、面向过程编程 C语言继承了C语言的传统支持最基本的面向过程编程这个编程范式里C11/14 的变化并不多但增加的新特性却可以改进程序甚至改变我们的编程思维。
  7. 空指针 一直以来在C/C 语言里空指针都使用宏 NULL 表示它的定义通常是 “0”即 #define NULL 0 //空指针宏NULL定义但NULL存在严重的缺陷它实际上是一个整数而不是真正的指针所以有时候会造成语义混淆例如重载函数的参数。C11/14 增加了新的关键字“nullptr 彻底解决了这个问题增加了安全性。 nullptr明确地表示空指针的概念可以完全替代NULL它可以隐式转化为任意类型的指针也可以与指针进行比较运算但绝不能转化为非指针都其他类型 int* p1 nullptr; //初始化为空指针 vectorint* p2 nullptr;//assert(!p1 !p2); // assert(10 nullptr); //报错nullptr 与 NULL 有重要区别它的强类型的但类型不是int或者void而是一个专用的类型nullptr_t 其利用decltype 的能力: typedef decltype(nullptr) nullptr_t; //nullptr_t的定义 还需要注意nullptr 并不是指针而是一个类型为nullptr_t 的编译期常量实例只是其行为很像指针所以我们也可以使用nullptr_t 任意定义与nullptr 等价的空指针常量如 nullptr_t nil; //初始化一个新的空指针常量 double p3 nil; //使用nil初始化空指针 assert(nil p3); //完全等价2. 初始化 C中初始化是一个基本操作但C98 标准并没有非常明确的定义而且初始化的语法也不一致。C11/14 标准对这个问题给出了完整的解决方案统一使用花括号 ”{}“ 初始化变量称为 ”列表初始化“例如 int x{}; //x缺省初始化值为0 double y{ 2.13 }; // string s{ hellow }; // complexdouble c(1, 1); // int a[] { 1, 2, 3 }; // vectorint v { 4, 5, 6 }; //在函数里也可以使用 ”{ … } 作为值返回类型自动推导 setint get_set() {return { 2, 3, 9 }; //直接使用花括号返回一个集合容器 }实际上花括号形式的语法会生成一个类型为 std::initialized_list 的对象它定义在头文件 initializer_list 里具有类似标准容器的接口只要实现对它的构造函数就可以支持列表的初始化。
  8. 新式for循环 遍历并操作 array、vector等容器里的元素是C 里常见的操作通常我们会使用循环语句利用容器的首尾位置来完成例如 int a[] {1, 2, 3, 5}; for(inti 0 ;i 4;i ) //按int索引遍历 {cout a[i] ; }vectorint v {12, 25}; for(auto iter v.begin(); iter ! v.end(); iter ) //使用迭代器遍历 {cout *iter ; }C11/14 引入了一种更简单便捷的方式无需显式使用迭代器首尾位置也无需解引用迭代器就可以直接访问容器序列里的元素如 for (auto x : v) cout x ; //直接访问无须遍历 for (const auto x : v) cout x ; //推导为常引用类型这种新式for循环的正式名称是“基于范围遍历的for”range-based for使用两个“” 分割了两个表达式第一个是遍历容器时的元素类型通常我们使用auto来自动推导第二个是目标容器。 在声明元素类型时使用auto推导出类型有拷贝代价也不能修改元素所以可以为auto添加修饰如“const auto / auto 来避免拷贝或者使用 auto 来修改元素的值。 for(auto x : v) cout x ; //推导为引用类型可修改值新式for循环支持C 内建数组和所有标准容器对于其他类型只要它具有begin()end()成员函数或者能够使用函数std::begin() 和 std::end() 确定迭代范围就可以应用于 for。注意新式for循环只是一种”语法糖“本质上还是使用迭代器来实现。 auto _range v; for (auto _begin std::begin(_range); //获得容器的引用_end ! std::end(_range); //确定范围迭代起点_begin ! _end; _begin); //确定范围迭代终点 {auto x *_begin; //解引用… }迭代范围已经在for循环开始前就确定好了所以在for循环里我们不能变动容器也不能增减容器里的元素否则会导致遍历的迭代器失败发生为定义的错误。
  9. 新式函数声明 C 1114 增加了一种新的函数语法允许返回类型后置它使用了auto 和 decltype 的类型推导能力基本形式为 auto func(…) - type {…} //语法有两处变化首先函数返回值必须使用auto来占位其次函数名后需要用 ”-type 的形式来声明真正的返回值类型这里的“type 可以说任意类型也包括 decltype如 auto func(int x) - declype(x) {return x * x; }这种语法看起来十分怪异但实际上在泛型编程时函数返回值的可能类型需要由实际的参数来决定所以有必要将返回值类型的声明”延后“如下 templatetypename T, typename U //目标参数列表 auto calc(Tt, U u) - decltype(t u) //后置函数声明 { reutrn t u; } //返回两个变量之和后置式函数声明语法虽然不常用但在关键时刻能够解决特定的问题 五、面向对象编程 面向对象编程是一种很重要的编程范式可以很好的控制类的封装、继承和多态
  10. default C 1114 重用了关键字default可以显示地声明类的缺省构造 / 析构等特殊成员函数能很好的表示代码意图。default 的用法于声明纯虚构函数的语法类似在构造 / 析构 等成员函数后面是用 ”default 就可以了如 class default_demo { public://显式知道构造函数和析构函数使用编译器的缺省实现default_demo() default; ~default_demo() default;//显式知道拷贝构造函数和拷贝赋值函数使用编译器的缺省实现default_demo(const default_demo) default;default_demo operator (const default_demo) default;//显式知道转移构造函数和转移赋值函数使用编译器的缺省实现default_demo(default_demo) default; default_demo operator (default_demo) default; };使用default 声明缺省构造函数后并不影响其他构造函数的重载于实现我们仍然可以编写其他形式的构造函数: class default_demo { public:… //之前的default 构造、析构函数int defautl_demo(int x); };2.delete 与defaule 类型在C11/14 里关键字delete 也增加了一种用法可以显式地禁用某些函数——通常是类的拷贝构造函数和构造函数以阻止对象的拷贝如 class delete_demo { public:delete_demo() default; //使用default 缺省实现~delete_demo() default;//显式禁用拷贝构造函数和拷贝赋值函数delete_demo(const delete_demo) delete;delete_demo operator (const delete_demo) delete; };delete_demo s1; //声明一个对象 delete_demo s2 s1; //无法拷贝赋值发生编译错误显式delete不仅可以用于类成员函数也可以作用于普通函数禁用某些形式的重载。
  11. override C 的类继承体系里有虚函数的概念它可以允许时动态绑定是实现多态的关键。虚函数的声明需要使用virtual 关键字如果一个成员函数是虚函数那么在后续派生类里的同名函数都会说虚函数无须再使用virtual 修饰。 但当继承关系较复杂或者派生类里的成员函数很多时阅读者很难分辨出哪些函数继承自基类哪些函数是派生类特有的增加了代码的维护成本且派生类可能无意使用了同名但签名不同的函数 “覆盖” 了基类的虚函数。 如下示例 struct base {virtual ~base() default; //虚析构函数virtual void f() 0; //纯虚函数virtual void g() const {}; //虚函数const修饰void h() {} };struct derived : public base //派生类 {virtual ~derived() default; //虚析构函数用default修饰void f() {} //虚函数重载void g() {} //不是虚函数重载签名不同无const修饰void h() {} //不是虚函数重载直接覆盖 };base类很清晰它定义了两个虚函数接口 f() 和 g()还有一个非虚函数 h()但是单独看derived类却信息有限f()、g()、h() 三个函数中只要 f() 是正确的虚函数重载g() 因为少了 “const” 修饰函数与基类不同是一个新的成员函数而h() 函数则与虚函数无任何关系直接是derived类自己专有的函数“覆盖”了base类的原函数实现。 unique_ptrbase p(new derived); //一个派生类对象使用基类指针// unique_ptr: 禁止拷贝和赋值只能 unique_ptrint p1(new int(20)); p-f(); //正确调用了派生类的f() p-g(); //错误调用了基类的g() p-h(); //调用了基类的h()未实现原意图C11/14里增加了一个特殊的标识符 “override 它可以显式地标记虚函数的重载明确代码编写的意图派生类里的成员函数如果使用了override 修饰则必须是虚函数而且签名也必须与基类的声明一致。即可修改代码为如下所示 void f() override{} //虚函数重载 void g() const override{} //虚函数重载4. final C的类体系非常灵活但这种灵活有时候会带来麻烦没有阻止类继承或者阻止重载虚函数的手段对于标准库里面的vector、list等容器设计者也不希望它们派生出子类但在C11/14 之前没有语言层面的强制保证。 与override一样它也增加了一个特殊的标识符 ”final”不仅可以控制类的继承也可以控制虚函数 在类名后面使用final显式地禁止类被继承即不能有派生类在虚函数后使用final显式地禁用该函数在子类里再被重载 override 和 final 可以婚姻更好的标记类的继承体系虚函数如下 struct interface {virtual void f() 0; //纯虚函数virtual void g() 0; };struct abstrace : public interface {void f() override final {} //f()不能再被继承和重载void g() override; //不能再被继承还可以重载 };struct last_final final : public abstrace {void f() {}; //不能重载void g() {}; //g() 仍然可以重载 };struct error : public last_final {}; //last_final 不能被继承final也不是关键字仅在类声明里有特殊含义但不建议再将它作为其他的标识符。
  12. 委托构造 有时候我们会声明多个不同形式的构造函数用于不同情况下创建对象这些代码大多都有初始化成员变量非常类似仅有少量不同但代码却不能复用导致代码冗余。而其常用的一种解决方法就是实现一个特殊的初始化函数通常是init然后在每个构造函数里调用它如 class demo { private:int x, y;void init(int x, int y) { x x, y y };public:demo() { //缺省构造函数init(0, 0);}demo(int a) { //但参数构造函数init(a, 0);}demo(int a, int b) { //双参数构造函数init(a, b);} };C 1114 标准引入了委托构造函数(delegating constructor的概念解决方法相同但不需要再次构造一个特殊的初始化函数而是可以直接调用本类的其他构造函数把对象的构造工作“委托”给其他构造函数来完成能够更好的简化代码如下 class demo { public:demo() : demo(0, 0) {} //缺省构造函数委托给双参数构造函数demo(int a) :demo(a, 0) {} //单参数构造函数委托给双参数的构造函数demo(int a, int b) { x a, y b; } //双参数构造函数被其他构造函数调用 private:int x;int y; };委托构造函数可以配合类成员在初始化是使用在类声明时先初始化成员变量然后再使用委托构造函数金星额外的构造操作进一步简化代码。 六、泛型编程 自从关键字template出现后泛型编程逐渐成为C的主流编程范式之一更扩展出了允许在编译器的模板元编程。泛型编程使用泛型容器、泛型算法成为可能深刻地改变了C语言同时也影响了C之外的语言。
  13. 类型别名 C 1114 扩展了using关键字的能力可以完成与typedef 相同的工作使用 “using alias type ” 的形式为类型起别名如 using int64 long; //long 类型的别名为int64 using ll long long; //long long 类型的别名为 ll它与typedef 的顺序恰好相反易于理解。但是不止于此它超越了typedef 可以结合template 关键字为模板类声明 “部分特化” 的别名如 templatetypename T //为标准容易map取别名 using int_map std::mapint, T; // int_mapstring m; //使用别名省略了一个模板参数templatetypename T ,typename U //自定义类两个模板参数 class demo final {} ; templatetypename T //保留一个模板参数 using demo_long demoT, long //别名第二个参数固定demochar, double; //原模板类给出两个参数 demo_longchar; //模板别名给出一个参数对于拥有复杂模板参数列表的类来说using的别名用法可以给出一些常用的形式简化模板类的使用增加代码可读性。
  14. 编译期常量
  15. 静态断言 C语言提供断言assert它是一个宏可以允许时验证某些条件是否成立有利于保证程序的正确运行但泛型编程主要工作在编译期assert 不起作用。而C11/14 增加了关键字static_assert 它是编译期的断言可以在编译期加入诊断信息提前检查可能发生的错误。其用法与assert 基本一致 static_assert(condition, message); //如果bool值表达式在编译期的计算结果为false那么编译器会报错并给出message的提示信息。static_assert 通常需要配合type_traits 库来使用 static_assert(sizeof(int) 4, it must be 32bit !!!);4. 可变参数模板 七、函数式编程 函数式编程functional programming是与面向过程编程、泛型编程并列一种编程范式它基于 “那麽达” 演算理论把计算过程视为数学函数的组合运算。引入了lambda 表达式更好支持函数式编程简化代码。
  16. lambda 表达式 C 1114 标准里lambda 表达式实际上是对函数对象的一种强化和扩展可以直接就定义“匿名”的函数对象所谓的“语法糖”。基本形式 {…} //这里的 [] 称为lambda 表达式引出操作符它之后的代码就是lambda 表达式形式如同一个标准的函数圆括号里是函数的参数而花括号内则是函数体可以实现任何功能。lambda 表达式的类型称为“闭包”无法直接写出所以通常需要使用auto 的类型推导功能来存储如下 auto f1 { return x * x; }; //末尾需要分号int a[] {9, 2, 3, 1, 6}; sort(a, a 5, { return x y; }); //排序lambda 表达式的返回值会自动推导但也可以使用新的返回值后置语法 auto f - long { …}; //指定返回值类型为 long2. 捕获外部变量 lambda 表达式的功能不止于此它还能捕获外部变量。其完整声明语法是 captures mutable - type {…} //操作符 [] 里的“captures” 称为“捕获对象”可以捕获表达式外部作用域的变量在函数式内部直接使用。 操作符功能[]无捕获函数式内不能访问任何外部变量[]以值拷贝的方式捕获所有外部变量函数体内可以访问但不能修改[]以引用的方式捕获所有变量函数体内可以访问并修改[var]以值拷贝的方式捕获某个外部变量函数体可以访问但不能修改[var]以引用的方式捕获某个外部变量函数体内可以访问并修改[this]捕获this指针可以访问类的成员变量和成员函数[, var]引用捕获变量var其他外部变量使用值捕获[, var]值捕获变量var其他外部变量使用引用捕获 下面的代码示范了这些列表的用法 int x 0, y 0; auto f1 { return x; }; //以值方式捕获使用变量不能修改 auto f2 {return x; }; //以引用方式捕获变量可修改 auto f3 x { return x; }; //值捕获x auto f4 x, y {y x; }; //值捕获x引用捕获y可修改y auto f5 , y {x y; }; //值捕获y其他外部变量为引用捕获 auto f6 {return x; }; //无捕获不能使用外部变量报错需要注意值捕获发生在lambda 表达式的声明之前如果使用值方式捕获即使之和变量发生变化lambda 表达式也不会感知仍然使用最新的值如想要使用外部变量的最新值就必须使用引用的不会方式担心变量的生命周期防止引用失败。 lambda 表达式还可以使用关键字 mutable 修饰它为值捕获添加了一个例外情况允许变量在函数体也能修改但这只是内部的拷贝不会影响外部的变量如 auto f {return x ;}; //可以在内部修改不影响外部变量lambda 表达式也可以转换为一个签名相同的函数指针但需要注意转换时它必须是无捕获列表的如 typedef void (*func)(); //函数指针类型 func p1 {cout endl; }; //直接赋值auto g { x; }; //有捕获的lambda 的表达式 func p2 g; //无法转换报错八、并发编程 并发编程是提高程序允许效率的一个必备手段C11/14 充分考虑了这个现实的需求以库的形式提供了较好的支持如 thread 、 mutex 、 atomic 等还新增了关键词 thread_local 它实现了线程的本地村村是一个与extern 、static 类型的变量类型存储指示标记。 线程本地存储是多线程编程里的概念是指变量在进程中拥有不止一个实例每个线程都会由于一个完全独立的、“线程本地化” 的拷贝多个线程对变量的读写互不干扰完全避免了竞、同步的麻烦如 extern int x; //外部变量实体存储在外部非本编译单元 static int y 0; //静态变量实体存在本编译单元内 thread_local int z 0; //线程局部存储每个线程都拥有独立的实体以上代码声明了三个变量 x 使用extern 修饰是一个外部变量 y 使用static 修饰是一个静态变量只能在本实现文件内访问在多线程下是不安全的 而 z 使用了 thread_local 关键字是线程安全的每个线程都有一份属于直接的独立的实例。下面代码验证了其结果 auto f { //lambda 表达式线程实际执行的函数y;z;cout y z endl; //输出值};thread t1(f); //启动两个线程 thread t2(f);t1.join(); //回收线程 t2.join();cout y z endl; //在主线程里输出变量值运行结果如下 可见静态变量y在进程里是唯一的两个线程都改变了y的值而z因为是thread_local 的两个子线程和主线程分别持有互相独立的三个实例所有子线程里均独立的加 1而主线程因为没有对z做任何操作所有值为0. thread_local 变量的声明周期比较特殊它在线程启动时构造在线程结束时析构也就是说仅在线程的声明周期里是有效的比static 变量的生命周期短但比普通局部变量的生命周期长。thread_local 仅适用于线程需要独立存储的情况当线程间需要共享资源访问时仍然需要使用互斥量等保护机制。 九、面向安全编程
  17. 无异常保证 C 的异常机制比较复杂允许程序抛出任意类型的对象作为异常为了规范异常的使用C 提出了 “ 异常规范” 的概念可以使用 throw (…)的形式来说明函数可能会抛出异常但用得较少。而C11/14 里 “异常规范” 被废弃保留了一个很小的功能声明函数不会抛出任何异常并且引入一个关键字 noexcept 来明确表明这个含义如 void func() noexcept; //函数决不会抛出异常使用它可以减少异常处理的成本提高运行效率。
  18. 内联命名空间 C 使用命名空间来解决命名冲突的问题关键字 namespace 可以声明一个专有的作用域其内的所有变量、函数或者类都不会与外部发生冲突但是使用时也必须加上命名空间的限定或者使用using打开 命名空间。它通常需要用一个名字来标识但C 也允许不使用命名空间来声明一个“匿名” 的命名空间这是相当与使用 static 静态初始化了命名空间里的成员如 namespace{int x 0; //具有静态属性 } assert(x 0); //可直接访问也可以增肌一个 inline 关键字修饰使其也不需要命名空间限定可直接访问 inline namespace tempint xx 0; } assert(xx 0);内联命名空间的这个特性对于代码的版本化很有用可以在多个子命名空间里实现不同的功能而且发布的时候对外只暴露一个实现隔离版本的差异有利于维护如 namespace release { //对外发布的命名空间namespace v001 { //旧版本void func() {}} inline namespace v002 { //使用inline内联void func() {}} }release::func(); //看不到子命名空间直接使用父命名空间3. 强枚举类型 enum enum color{ //弱枚举类型 a 1, b 2, c 3 };assert(b a 1); //可以运算 int a 1; // 报错编译错误强枚举类型可以使用 ”struct / class “ 的形式声明, 但它不能被隐式转化为整数 enum class color{ … } auto x color::a; //正确必须使用类型名 auto y a; //错误没有类型名限定 auto z color::a 1; //错误不能隐式转化为整数运算十、一些特性 标准预定义宏 ”_cplusplus 是一个整型常数可辨别编译器版本 cout C : _cplusplus endl;超长整数 uLL、LL auto a 52313LL ; auto b 2147483647uLL ;原始字符串 - 两边不超过16个字符 auto s Rhello \\ world; //不需要转义 auto b R(biographicldd infsfspl); //定界符 *** auto d R(Dark souls); //定界符 auto f 2.14f //后缀f 指示float auto s Lwide char; //前缀L指示wcahr_t auto x 0x199L; //前缀0x十六进制; 后缀Llong类型………这里就不一一列举了 至此结束总结