96SEO 2026-02-19 23:57 16
相关语法格式容器列表初始化的底层原理forward_list和array与类型相关的新特性decltype左值引用和右值引用什么是左值什么是右值左值和右值的本质区别右值引用如何理解右值引用std::move移动构造移动赋值万能引用和完美转发模板的可变参数default和delete的新用法lambda表达式语法格式捕获列表底层原理包装器std::bind总结说在前面

作为新年的第一篇博客。
在这里先祝大家新年快乐今天我们要介绍的是C11
首先如同一个人从一个一无所知的婴儿成长成为一个社会的精英是需要不断发展的。
C自从诞生的那一刻起一直都在不断进步不停更新。
对于C来说有两个比较重要的时间点。
一个就是C98,这个版本的C引入了强大的STL。
而另外的一个时间点就是我们接下来介绍的C11
在C11里面所有的东西都可以使用{}进行初始化你可能会看到很多有关C11的书籍里面可能都会有如下的demo代码
day22):_year(year),_month(month),_day(day){}int
}实际上花括号初始化的本质也是调用了构造函数我们可以对构造函数添加打印语句:
day22):_year(year),_month(month),_day(day){std::coutDate(int
}而因为增加了{}初始化的方式那么就能够解决在C98里面使用new构造一块连续的空间的同时无法顺带初始化的问题
在C98里面,对于一块连续空间的申请和初始化我们只能分开进行:
}从C11以后我们就可以使用花括号来初始化对应new的连续的数组空间不过相对来说这种方式用的还是相对比较少。
毕竟更多的情况下我们都是使用的STL提供的容器。
所以这里我们知道有这样一种用法就好了
STL里面的容器也提供了对应的列表初始化的方式真可谓是一切都可列表初始化所以我们才会说到了C11以后。
一切皆可使用列表初始化
这里我们用vector来进行演示。
基本上我们之前学习的容器都可以使用这样的初始化方式
day22):_year(year),_month(month),_day(day){std::coutDate(int
那么为什么能够这样给容器初始化呢在处理容器的{}初始化的时候编译器做了哪些工作呢?下面我们就来探究一下
首先官方文档就是我们学习C11最好的资料之一我们先来看看C11种vector对应发生了什么变化:
首先上面两个对应的构造函数是后面的知识点。
实际上真正能够让我们使用花括号初始化对应容器元素的就是最后一个构造函数!
l{1,2,3,4,5,77};//获取l变量的类型并以字符串的形式打印std::couttypeid(l).name()std::endl;
}这段代码是在Linux上获取的不过对应的就是文档手册里面的新的构造函数的参数的类型initializer_listT!
在C11以后{}被编译器识别成为新的initializer_list类型只要支持这个构造函数就可以使用花括号进行初始化
所以为了让我们自己实现的vector也能够支持{}初始化我们也添加这个构造函数
{//vector是一个模板类templatetypename
const_iterator;//...//给我们自己实现的vector添加对应的构造函数
//这个时候我们自己实现的vector也就可以支持{}初始化的方式了vector(
li){push_back(e);}}private:iterator
另外C11还新增加了几个容器。
我们之前介绍的哈希相关的容器就是C11新增的接下来我们将介绍两个C11里面存在感比较低的两个容器—
C11新增加了forward_list和array这两个新容器下面我们先来看forward_list。
首先先来看文档对应的说明
首先不得不说有的时候C对于类的命名不是特别能够让人见名知义。
这个forward_list其实就是我们之前在数据结构那里学习的单链表下面我们就来简单使用以下forward_list
a[]{1,2,3,4,5};//没有提供尾插接口for(auto
e:a){fl.push_front(e);}//也支持范围forfor(auto
}说实话这个容器的作用远远不及list,即使是我们手写一个简单的单链表也是可以的不过这个容器的倒是可以作为哈希桶悬挂的子节点结构
接下来我们来看一看另外一个容器array.首先我们先来看文档对应的说明:
从文档来看array就是一个固定大小的泛型线性数组。
下面我们就来简单使用一下array:
说实话C11这个array确实是没什么太大的作用它所有的功能vector基本都有所以这个容器大家仅仅就是作为一个了解就可以了。
不用过分去深究这个容器相应
C11还增加了一些有关类型推断的新特性。
比如我们经常使用的auto就是在C11以后才增加的类型推导的能力。
接下来介绍一个C11新增加的一个和类型相关的特性的一个操作符----decltype
{decltype(1020)x10;std::coutx变量的类型为:
}可以看到这个操作符确实把类型返回了。
注意这个操作符和sizeof操作符一样并不会去计算对应括号的表达式仅仅就是返回对应的表达式的类型
前面讲的特性大多是是C11相对来说属于锦上添花的。
而右值引用可以说式C11相对来说非常具有革命性意义的一个新特性。
它让C的高效又上了一个台阶
首先从字面上的意思来看左值就是出现在表达式左边的值右值就是出现在表达式右边的值。
比如下面的代码
a1020;//1020就是一个右值//但是下面怎么解释int
}最后一个例子不难看出这样的理解是不正确的,如果这样子理解左值和右值就太肤浅了。
那么左值和右值的本质区别到底是什么呢或者说左值具有什么特征右值又有什么特征呢
明确地给出一个结论:左值是可以取地址地值右值是不能取地址的值也就是说通过能否取地址就可以看出左值是真正分配了计算机内存空间的值而右值仅仅只是一个临时量
在C98以前我们所使用的引用都叫做左值引用。
从C11开始我们就要把引用分为左值引用和右值引用了。
下面我们就来通过代码来看这两个引用:
}无论右值引用还是左值引用只要是引用那么都是被引用对象的别名而右值引用自身是一个左值因为我们可以对右值引用的那个别名取地址了
//10是右值所以rrx是右值引用std::coutrrx的地址是:
从前面的结论可以看出本来即将消亡的右值通过右值引用的方式计算机也确实为了它开辟一块内存空间存储对应的值
也就是这个别名本质是一个左值所以右值引用是一个左值这个左值是右值的别名
那么有的时候我们需要把左值转换成为右值。
c11的设计者也考虑到这个点了所以提供了一个接口std::move—可以把左值转换成右值。
下面我们来看一段代码
左值引用只能引用左值const左值引用可以引用左值也可以引用右值
那么C11推出右值引用难道仅仅是我们前面那样使用吗答案显然不是在C98的时候C一直都很害怕这样的场景
i0;iv.size();i){if(v[i].find(tolower(ch))!string::npos){return
keyboard;keyboard.push_back(qwertyuiop);keyboard.push_back(asdfghjkl);keyboard.push_back(zxcvbnm);vectorstring
posgetRow(words[i][0],keyboard);//pos3,说明不在这一行继续寻找下一行//coutpos
ch:words[i]){if(keyboard[pos].find(tolower(ch))
string::npos){flagfalse;break;}}if(flag)
res.push_back(words[i]);}}return
};通过前面的学习我们知道传值返回会发生拷贝。
而对于vector这样的类发生的是深拷贝一旦这个vector管理的对象特别多并且每个对象又要进行深拷贝的情况下。
传值返回的效率就是一场灾难
C是一门十分注重效率的编程语言而右值引用便是为了解决这个问题应运而生的
我们知道右值编译器是没有为其分配空间的那么对于自定义类型的右值C又将其称为将亡值
既然是将亡值那么就意味着这个自定义类型的对象会马上调用析构函数清理自己了。
但是这个时候这个自定义类型身上的资源又是我所需要的。
下面就有如下两种做法
2.既然这个将亡对象有我想要的资源那么不妨把它的资源为我所用然后再让其自行销毁即可
显然我们都会选择第二种方式。
那么c11以后就新增加了两个相应的默认成员函数移动构造和移动赋值
public:A():_p(nullptr),_size(0){}A(int
size):_p(nullptr),_size(size){_pnew
a._size;i){_p[i]a._p[i];}_sizea._size;std::coutA(const
aa):_p(nullptr){std::swap(_p,aa._p);std::swap(_size,aa._size);std::coutA(
可以看到这里的a3和a1的成员发生了交换。
相比于先前我们只能这种场景我们只能进行深拷贝。
当对象很大的时候效率非常低而有了C11的移动构造以后传值返回的效率得到了大幅度的提升
而对于传值返回的情况现代的编译器也会进行一定的大胆的优化假设我们现在有一个to_string函数
str;while(val){//...复杂的转换逻辑}return
aa){std::swap(_p,aa._p);std::swap(_size,aa._size);return
*this;}既然是作为默认成员函数那么在特定的条件下编译器就会生成对应的默认移动构造和移动赋值,需要满足如下三个特性
默认生成的移动构造对于内置类型浅拷贝对于自定义类型如果提供了移动构造函数那么就会调用移动构造
不过多数情况下生成的默认移动构造没什么价值所以这两个默认成员函数一般都需要手动提供。
需要注意的是在c11以后原先将单个元素插入容器的函数接口都提供了对应的右值版本目的就是为了能够进一步提高运行效率
这个就是右值引用和模板结合到了一起以后。
既能够接受左值也可以接受右值因此我们就把这个特性叫做万能引用。
但是这个万能引用有一个很不好的缺陷接下来我们来通过一段代码进行演示
}我们发现move以后的a1本来应该是一个右值结果传参了以后变成了一个左值万能引用就会出现引用折叠的问题。
使用万能引用了以后所有的传递的T参数都会变成左值
但是有的时候我们希望能够把原来的特性保持下去所以c11又提供了一种新的方式----完美转发
}这里依旧调用的是移动构造函数很好保持了原来右值的特性。
所以如果要保持对应的原来的值的特性最好使用forward完美转发。
接下来我们来讲一讲模板的可变参数在C语言阶段我们经常使用如下的函数
format,...);函数声明里面带有省略号的我们称之为可变参数在C11里面引入了可变模板参数也就是说以后一个模板函数或者模板类不仅类型可变参数的数量也是可以变化的
typeid(val).name()std::endl;//可以使用sizeof获取传递参数的个数
sizeof...(args)std::endl;//参数解包PrintArgs(args...);
solve(std::move(a1));//可以传任意类型任意数目的参数
PrintArgs(1,2.3,c,std::string(hello
world));std::cout\n;PrintArgs(1.1,2);
}可变参数模板是通过递归实现的允许有0个或者是1个递归参数包我们这里使用的是模板的最佳匹配原则来确定递归的出口如果这类换成一个最佳匹配的函数也可以
sizeof...(args)std::endl;//使用数组进行解包
{PrintArgs(1,2.3,c,std::string(hello
}注意可变参数递归解包是在编译的时候完成的不能通过运行时的if逻辑来递归
有了可变模板参数的知识铺垫那么我们就可以很好讲解c11里面容器新增的emplace_back和emplace接口:
emplace系列接口使用的都是可变参数模板对应如果传递的参数能够匹配到自定义类型的构造函数就会变成直接构造提高效率不过有了移动构造以后emplace接口带来的效率提升也不是特别明显所以这个接口了解即可
在C11里面又对default和delete这两个关键字进行了功能的扩展。
在类和对象章节我们介绍过只有在不提供任何构造函数的情况下编译器才会生成默认构造函数
但是在c11如果已经提供了其他构造函数的情况下仍然想使用编译器生成的默认构造函数可以使用default关键字
//强制编译器生成默认构造函数A()default;A(int
size):_p(nullptr),_size(size){_pnew
a._size;i){_p[i]a._p[i];}_sizea._size;std::coutA(const
aa):_p(nullptr){std::swap(_p,aa._p);std::swap(_size,aa._size);std::coutA(
};不仅是默认构造函数其他构造函数都可以使用default强制编译器默认生成
不仅仅可以强制编译器生成我们还可以删除默认成员函数使用delete关键字
接下来我们来讲lambda表达式。
最早引进lambda表达式的语言式python后面其他语言看到这个语言很好用于是都纷纷加到了标准库里面。
c11以后同样也支持这样的用法
比如我们上京东购物有时想按照商品价格排序有时候想按照实际排序有各种各样的排序需求。
在c98里面我们就要写不同的仿函数来实现一旦随着项目的扩展这些仿函数就会越来越多非常难维护
所以我们就希望能够达到一种排序规则专为一种排序方法所用而lambda表达式恰好就是这样的一个工具下面我们就来看lambda表达式的语法
name):_age(age),_score(score),_name(name){}int
{{15,23,aa},{14,55,cc},{13,88,bb}
首先我们要知道一个点。
lambda是一个局部匿名函数既然是函数那么就代表者对应的内部是一个全新的作用域内部作用域不能直接使用外部的变量
{//lambda捕获列表//可以使用[]捕获对应的元素int
}这种值的捕获方式是值捕获默认是对捕获的值带了一个const想要更改值捕获的话就要使用mutable关键字不过即使更改内部的a,b也对外面没有影响所以这个mutable几乎没有
};//std::coutl()std::endl;//引用捕获auto
0;};l();std::coutretretstd::endl;引用捕捉和值捕捉可以混合来下面这三种写法都是可以的
l3[ret,](){};关于lambda的介绍我们就暂时讲到这里。
下面我们来看一看这个lambda对于编译器来说究竟是什么
可以看到lambda函数被编译器处理以后就是一个仿函数这个仿函数是lambda一个唯一的uuid所以即使两个lambda在我们看来一模一样。
它们也是不同的类型
值得一提的是lambda函数还可以和函数指针相互赋值不过我们并不建议这么去做
而如果当这些函数统统编程模板的时候。
同样的模板就会实例化出三分代码显然这三份代码还是有一定的冗余毕竟对于我们都是希望使用对应的()的功能
operator()(){std::coutFunc()std::endl;}
{std::coutlambdastd::endl;};void
{std::vectorstd::functionvoid()
fuctions;fuctions.push_back(func);fuctions.push_back(Func());fuctions.push_back(f);for(auto
}也就是说但凡是返回值和参数都相同的函数我们都可以直接使用一个function对象来接收接下来我们改造一下曾经的逆波兰表达式的题目用C11的方式改造:
unordered_mapstring,functionint(int,int)
str:tokens){if(hash.find(str)hash.end()){st.push(stoll(str));}else
rethash[str](left,right);st.push(ret);}}return
原因是成员函数有一个隐含的this指针那么有没有方式可以不需要这个this指针呢答案是肯定的解决的方法就是使用std::bind函数
std::bind(A::ASub,A(),std::placeholders::_1,std::placeholders::_2);//
f1(std::bind(A::ASub,A(),std::placeholders::_1,std::placeholders::_2));std::coutf1(2,3)std::endl;std::functionint(int,int)
f2(std::bind(A::ASub,A(2),std::placeholders::_1,std::placeholders::_2));std::coutf2(2,3)std::endl;}
bind还有一个作用就是改变参数的位置。
而我们代码里面的_1和_2就是我们要显式传递的参数的位置也就是占位符可以通过占位符来改变参数顺序:
f1(std::bind(Sub,std::placeholders::_1,std::placeholders::_2));
f2(std::bind(Sub,std::placeholders::_2,std::placeholders::_1));std::coutf1(2,3)std::endl;std::coutf2(2,3)std::endl;
以上就是C11的部分新特性的介绍。
而C11更多的新特性例如智能指针我们会在后续的文章里面重点介绍。
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback