当前位置:   article > 正文

C++中auto_c++ auto

c++ auto

题目描述

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

[

[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

数据范围:矩阵的长宽满足 0<=n, m<=500 , 矩阵中的值满足 0≤val≤10^9
进阶:空间复杂度 O(1) ,时间复杂度 O(n+m)

示例1

输入:

7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:

true

说明:

存在7,返回true   

示例2

输入:

1,[[2]]

返回值:

false

示例3

输入:

3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值:

false

说明:

不存在3,返回false   

这题有个解法:

暴力搜索
解题思路: 逐行逐列的搜索二维数组,判断是否存在目标值。

复杂度分析:
时间复杂度:O(MN)
空间复杂度:O(1)

  1. class Solution {
  2. public:
  3. bool Find(int target, vector<vector<int> > array) {
  4. for(auto i : array)//c++11语法
  5. {
  6. for(auto j : i)
  7. {
  8. if(j == target) return true;//找到目标值
  9. }
  10. }
  11. return false;
  12. }
  13. };

其中使用了auto

auto : 类型推导. 在使用c++的时候会经常使用, 就像在考虑STL时迭代器类型, 写模板的时候使用auto能少写代码, 也能帮助我们避免一些隐患的细节.

auto初始化

  1. 使用auto型别推导要求必须在定义时初始化, 毕竟需要根据对象的类型推导左值对象的型别.
  1. 但是auto型别推导会忽略引用和顶层const, 所以要对对象加上想要的修饰.
  1. C11之前只能通过()=对变量初始化, C++11增加了对定义的对象初始化的方法,可以使用{}对变量初始化

C++ 11之前的auto

早在C++ 98标准中就存在了auto关键字,那时的auto用于声明变量为自动变量(自动变量在控制流进入变量作用域时系统自动为其分配存储空间,并在离开作用域时释放空间),拥有自动的生命周期。但是该作用是多余的,变量默认拥有自动的生命周期。

  1. int a = 10; // 自动生命周期
  2. auto int a = 10; // 自动生命周期

在C++ 11中,已经删除了该用法,取而代之的作用是:自动推断变量的类型。

C++ 11及以后的auto

自从C++ 11开始,编译器可以使用auto关键字自动推断被初始化的变量的类型。auto关键字也被称为占位类型说明符(placeholder type specifier)。

通常用法

auto从初始值的类型推断变量本身的类型,变量得到一个初始值的副本。

  1. int i;
  2. auto j = i; // j is int contains a copy of i

auto会将引用类型推断为值类型。为了确保是引用类型,使用auto&

  1. int m = 0;
  2. int& i = m; // i is an alias to m
  3. auto j = i; // j is int contains a copy of i
  4. auto& k = m; // k is int& and alias to m

auto不能推断出const修饰符,为了确保可以推断出,使用const auto

  1. int i =0;
  2. const int m = 0;
  3. auto j = m; // j is int (non-const)
  4. const auto k = m; // k is const int
  5. const auto l = i; // l is const int, holds a copy of i

auto&可以推断出const修饰符。

  1. const int i =0;
  2. auto& j = i; // const int&, alias for i

字面常量(Literals)

auto可以识别出以下类型:

  1. auto i = 1; // int
  2. auto j= 7ul; // unsigned long
  3. auto x = 2.0; // double
  4. auto y = 2.0f; // float
  5. auto c = 'A'; // char
  6. auto s = "hi"; // char const*
  7. auto b = true; // bool

从C++ 14开始,可以识别出字符串常量:

  1. using namespace std; // This is necessary
  2. auto s = "hellow"s // std::string, note the s operator.

从C++ 23开始,可以识别出size_t和有符号的size_t类型。

  1. auto k = 1uz; // size_t
  2. auto m = 1z; // signed size_t

可以识别出自定义的类:

  1. class LongNameClass {};
  2. auto a = new LongNameClass(); // a is LongNameClass*

可以识别出list类型:

auto l = {1,2,3}; // l is std::initializer_list<int>

类型名字比较长的类型也可以被auto轻松代替:

  1. std::map<std::string, std::string> m;
  2. auto n = m;
  3. auto p = std::make_unique<int>(); //unique pointer
  4. std::vector<double> vec(100);
  5. auto& r = vec;

vector的iterator类型也可以被代替:

  1. // type of i is std::vector<double>::iterator
  2. for (auto i = vec.begin(); i != vec.end(); i++){
  3. cout<< *i;
  4. }

也可以很容易获得vector中的元素:

  1. std::vector<unsigned long long int> vec = {1, 2, 3};
  2. for (auto& x : vec)
  3. cout << x ;

auto与for

auto最常见的就是与for联用, 特别是类型特别复杂的时候. 但是auto又有多种选择, 如 : auto, auto &等, 不同的选择其效率也不一样.

  1. auto, 即 for(auto i:range) . 这使range中的每一个元素都会产生一个副本, 所以即使修改了 i 也不会实际影响到range.
  2. const auto, 及for(const auto i : range). 这也会是range的每一个元素产生一个副本, 但是这个副本竟不能被修改.
  3. auto &, 即for(auto &i : range). 引用, 因为i 直接引用range里面的元素, 所以并不会产生一个副本, 但是 i 的修改也会影响range里元素的值. 通常我们需要修改range是会考虑用到.
  4. const auto&, 即for(const auto &&i : range). i 直接引用range里面的元素, 所以并不会产生一个副本, 并且i 也不能修改. 一般初始化的是一个左值时而且是读取range里的元素时都是用const auto&而不用auto, 因为前者不会产生副本, 效率要高当然一般初始化的是一个左值时效率低, 但是如果是右值还是使用const auto效率高, 因为const auto &需要把 i 存储在内存中的一个位置,间接访问会更消耗时间
  5. auto&&, 即for(auto &&i : range). 如果初始化是左值, 那么 i 就是左值引用, 如果初始化是右值, 那么 i 就是右值引用,

还有const auto &, 当然具体的选择还是看具体的情况而定.

最后, 当用auto推导多维数组的时, 保证除最内层循环外, 其他的外层循环都应该是引用类型, 否则很容易出错, 即 :

  1. int a[10][10][10];
  2. for (const auto &i : a)
  3. for(const auto &j : i)
  4. for(const auto k : j)
  5. ;

最好使用auto型别推导

1. 初始化

  • 在定义对象的时候可能或多或少会忘记对变量进行初始化, 但当我们使用该变量的时候就会出错, 而且问题也不好找出来, 但是使用auto定义对象就要求必须初始化有时还能减少代码量, 上面我们已经分析过了.
  • 使用auto初始化在平台上还有一点好处, 比如 :
  1. vector<int> v;
  2. unsigned size = v.size(); // size()返回size_t型别
  3. auto sizet = v.size();

​ 在32的平台unsigned代表的是32位, size_t是32位, 在64的平台unsigned代表的也是23位, 但是size_t却是64位, 这样平台差异可能就会带来问题, 使用auto代替就没有这样的问题.

不过只有这几点可能不会让人心动, 下面我们还有auto的好处.

2. STL使用型别推导

还记得在前言中个说过调用STL最好使用auto推导型别, 如果你还记得mappair 吗? 是这样 map<pair<key, type>>? 还是map<pair<const key, type>>? 答案是最后一种, 那么现在我们就来分析的使用auto推导还是显示型别比较好.

看到上面的例子毫无问题, 但是深究起来显示型别还是些不完美. 我们知道map的key不能被改变, 所以显示型别的string与map的const string不是匹配, 编译器就会将map对象都会产生一个临时对象再隐式的转为string, 等等. 是不是注意到有一点了, 为了型别匹配赋值会产生临时变量, 那岂不是每一循环都会产生一个临时变量, 但是auto型别推导就是精确匹配的, 不会产生临时变量.

可能觉得将显示型别的key改为const string就能解决这个问题了, 确实是这样, 但是如果没有注意到这一点细节, 那就会损失效率了, 使用auto可以完全不想这些问题啊.

总结

本节对C11的auto用法做了一个浅显的分析, 分别对使用auto的好处, 定义时注意{}对象也必须初始化, auto在与for连用的时候要根据实际参数确定选择哪种实现, 这样效率才会达到最大, 当然一般都使用const auto&auto&&. 最后还对auto与函数返回值关联, 可以将返回型别放在函数名尾也可以, 这样的做法一般在模板中将模板参数作为返回值才考虑用, 平时也不必这样定义函数.

参考:

C++ auto keyword makes your life easier

C++ keyword: auto - cppreference.com

Placeholder type specifiers (since C++11) - cppreference.com

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/888709
推荐阅读
相关标签
  

闽ICP备14008679号