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

好用的c++11语言特性

欢迎来到博主的专栏——c++杂谈
博主ID:代码小豪


文章目录

    • auto
    • initializer_list(初始化列表)
    • 范围for

auto

c++11标准新增了一个auto关键字,允许变量或者对象完成类型的自动推导。

auto num1 = 44;//int 
cout << typeid(num1).name() << endl;
auto num2 = 3.14;//double
cout << typeid(num2).name() << endl;

实际上auto是C语言时期就存在的关键字,但是作用完全不同,在C语言中,auto和static都是修饰变量的生命周期。auto修饰的变量具有局部生命周期,但是我们几乎不用auto,因为在C语言当中,非static的变量(非全局变量)都会默认是auto修饰的。

由于auto需要根据初值来判断变量或对象的类型,因此必须为auto修饰的变量进行初始化

auto i;//error,编译器无法判断i的类型

还可以在auto前面加上其他关键字修饰。

const auto num3 = 44;//const int
//num3 = 11;//error,num3是常量
static const auto num4 = 3.14;//static const double
//num4是一个静态的常量

当然了,这种程度的类型推导我们并不需要交给编译器工作,auto真正好用的点在于可以推导较为复杂的类型(比如迭代器),这样可以省去我们推导的时间。

	vector<int> v1;vector<int>::iterator it1 = v1.begin();auto it2 = v1.begin();//auto=vector<int>::iterator

不过使用auto肯定会有缺点的,最大的问题在于可读性变低了。比如程序当中有一个变量用错了,如果这个程序的源代码很不幸的全使用了auto关键字,那么在查bug的时候估计很麻烦。总而言之,auto不能滥用,否则省下来的效率会在未来的某一天如数奉还。

initializer_list(初始化列表)

实际上这个列表我们在认识它之前就已经用过了。不信?那你看看下面的代码。

int a[10] = { 1,2,3,4,5,6 };
int* a = new int[10]{ 1,2,3,4,5,6 };

这个大括号({})就是初始化列表,但请注意:不是所有的大括号都是初始化列表!,大括号有时候还能表示构造函数的参数,匿名对象,隐式转换等。只有可以任意控制参数数量的才是初始化列表。

class test
{
public:test(int a, int b){}void print(const test& t1){cout <<"void print(const test& t1)";}
};
int main()
{test t1{ 10,20 };//调用构造函数初始化t1对象test{ 10,20 };//匿名对象t1.print({ 10,20 });//隐式类型转换
}

c++11直接将初始化列表写成了一个模板类。我们可以在c++手册当中找到initializer_list的使用说明。initializer_list成为了一个模板类、并且为这个类设计了几个接口

template<class T> class initializer_list;
initializer_list的模板原型

我们可以用这个模板实例化出一个初始化列表对象

initializer_list<int>il1 {1, 2, 3, 4, 5, 6, 7};
initializer_list<int>il2 = { 1, 2, 3, 4, 5, 6, 7 };

但这并不是c++11将initializer_list写成模板类的真正目的,initializer_list最强大的功能是可以作为函数的参数(一个类的对象当然可以作为参数了)。

void print(initializer_list<int> lt1)
{for (auto i = lt1.begin(); i != lt1.end(); ++i){cout << *i << ' ';}
}int main()
{print({ 1,2,3,4,5,6,7,8,9 });
}

这里的i,其类型是initializer_list<int>::iterator。说明auto还是蛮好用的(hh)。

c++11标准新增了initializer_list模板类后。STL的许多容器也支持使用initializer_list来构造容器,比如vector,list。让容器初始化变得简单了。(关于STL有什么容器支持initializer_list构造,可以去c++手册当中查看)

vector (initializer_list<value_type> il,       const allocator_type& alloc = allocator_type());
//c++11新增的vector构造函数
vector<int> v1{ 1,2,3,4,5,6,7,8 };//v1构造
list<int> l1{ 1,2,3,4,5,6,7 };//l1构造

范围for

c++11新增了一个全新的for循环形式,可以自动的遍历给定的区间,主要是数组,容器,或者一些元素的集合。范围for的标准格式如下:

for(element:object)
{
statement
}

其中element要声明为object的元素的类型(主要根据迭代器的iterator *的返回值判断object的元素类型)。返回for会自动的遍历整个集合,并且执行循环体内的操作(statement),在遍历的过程中,object的每个元素的值都会传递给element。

比如我们用范围for循环整型数组

int a[] = { 1,2,3,4,5,6 };
for (int e : a)//e的类型与a中的每个元素相同,即int
{cout << e;//打印123456
}

如果你不想判断集合的每个元素都是什么类型,只需要将element声明为auto就行了。(实际上博主在用范围for的时候经常用auto类型的element)。

vector<string> v1{ "hello","world","hello","xiaohao" };
for (auto e : v1)
{cout << e<<' ';//hello world hello xiaohao
}

我们可以将element声明为引用类型,这样就可以通过引用传递的形式,迭代的修改集合中的元素。

int b[] = { 1,2,3,4,5,6 };
for (auto& e : b)
{e += 10;//b中的所有元素加了10.
}

将element声明为引用除了能修改集合的元素这一作用外,还有一个很重要的特性。那就是当集合内的元素是一个对象时,pass by value的方式会导致频繁的调用拷贝构造函数,而将element声明成reference则不会。如果你不想让element修改集合内的元素,就将其声明为const reference。

vector<string> v1{ "hello","world","hello","xiaohao" };
for (const auto& e : v1)
{cout << e << ' ';//这种方式的效率远高于pass by value
}

如果你对迭代器存在一定的了解,那么一定知道如下的迭代方式。

vector<string> v2{ "hello","world","hello","xiaohao" };
vector<string>::iterator it1 = v2.begin();
while (it1 != v2.end())
{cout << *it1 << ' ';it1++;
}

这种迭代方式和范围for的迭代方式是一样的,而且不止方式一样,甚至范围for的底层原理就是这种迭代方式。

如果我们为object提供成员函数begin()和end(),那么我们就可以用范围for遍历这个object集合内的元素(前提是你为object写了一个逻辑正确的迭代器。否则会因为缺乏iterator::operator++()而导致编译器报错)。

class myclass
{
public:myclass(initializer_list<T> lt){size = lt.size();array = new T[lt.size()];int i = 0;for (auto& e : lt){array[i] = e;i++;}}typedef T* iterator;//设计迭代器iterator begin() { return array; }//返回迭代器的beginiterator end() { return array + size; }//返回迭代器的end
private:T* array;size_t size;
};int main()
{myclass<string> m1{ "hello","world","hello","xiaohao" };for (const auto& e : m1)//返回for可以遍历自定义的对象。{cout << e << ' ';}return 0;
}

如果将begin()和end()去掉或者写错了函数名字(比如写大写的B),都不能让范围for正常工作,所以请注意这些问题

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python筑基之旅-文件(夹)和流
  • docker-compose 自动管理 数据库
  • 2024/05/25学习记录
  • 20240526每日后端---------分享一些开发必备网站
  • docker安装Elasticsearch(ES)详细教程
  • 2024电工杯参赛经历感受总结
  • PyTorch深度学习实战(44)——基于 DETR 的目标检测模型
  • C++设计模式之单例模式、模板模式、状态模式、原型模式、CRTP 模式、组件模式、观察者模式、发布-订阅模式、访问者模式
  • three.js能实现啥效果?看过来,这里都是它的菜(10)
  • string OJ题
  • 【ai】pycharm安装langchain 相关module
  • 关于linux磁盘告警问题
  • Hadoop 客户端 FileSystem加载过程
  • 前端工具vscode 提交代码git操作
  • vue项目input框使用lodash的debounce防抖
  • 【翻译】Mashape是如何管理15000个API和微服务的(三)
  • 03Go 类型总结
  • Apache的基本使用
  • css选择器
  • github指令
  • Mithril.js 入门介绍
  • Python进阶细节
  • React Transition Group -- Transition 组件
  • Spark学习笔记之相关记录
  • SpingCloudBus整合RabbitMQ
  • TypeScript实现数据结构(一)栈,队列,链表
  • vuex 笔记整理
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 从伪并行的 Python 多线程说起
  • 分享几个不错的工具
  • 服务器从安装到部署全过程(二)
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 力扣(LeetCode)56
  • 排序算法之--选择排序
  • 前端临床手札——文件上传
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • 阿里云ACE认证学习知识点梳理
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​浅谈 Linux 中的 core dump 分析方法
  • #nginx配置案例
  • #多叉树深度遍历_结合深度学习的视频编码方法--帧内预测
  • $refs 、$nextTic、动态组件、name的使用
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (ros//EnvironmentVariables)ros环境变量
  • (动态规划)5. 最长回文子串 java解决
  • (二)pulsar安装在独立的docker中,python测试
  • (九)c52学习之旅-定时器
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (一)ClickHouse 中的 `MaterializedMySQL` 数据库引擎的使用方法、设置、特性和限制。
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .Net Core 笔试1
  • .net 设置默认首页
  • .NET4.0并行计算技术基础(1)
  • .NET程序集编辑器/调试器 dnSpy 使用介绍