C++进阶---C++11
Posted 4nc414g0n
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++进阶---C++11相关的知识,希望对你有一定的参考价值。
C++11
1)初始化
①初始化
struct Point int _x; int _y; Point(int x, int y) :_x(x) , _y(y) ; class Date public: Date(int year, int month, int day) :_year(year) , _month(month) , _day(day) cout << "Date(int year, int month, int day)" << endl; private: int _year; int _month; int _day; ;
可以这样初始化:
//int a[] = 1, 2, 3, 4 ; int a[] 1, 2, 3, 4 ; //Point p = 1, 2 ; Point p 1, 2 ; int* p1 = new int(0); int* p2 = new int[5]1,2,3,4,5; Point* p3 = new Point[3]1, 1, 2, 2 , 3, 3 ; //需要有初始化列表初始化,底层未封装这种初始化 Date d1(2022, 3, 13); Date d2 = 2022, 3, 15 ; Date d3 2022, 3, 15 ; Date2022,3,15; int i = 1; int j = 2 ; int k 3 ;
C++11里面扩展了初始化使用,基本都可以使用它来初始化,但是建议还是按旧的用法来使用,一般new[]建议使用它来初始化
②std::initializer_list
initializer list用来接收,同时支持迭代器
例如:auto il = 10, 20, 30 ; initializer_list<int> il= 10, 20, 30 ; //支持迭代器: std::initializer_list<int>::iterator it; // same as: const int* it for ( it=il.begin(); it!=il.end(); ++it) cout<< *it <<endl;
以vector为例:
vector的constructor函数vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());
vector中要实现新的成员函数vector(initializer_list l)
//C++11实现的initializer_list typedef T* iterator; vector(initializer_list<T> l) _start = new T[l.size()]; _finish = _start + l.size(); _endofstorage = _start + l.size(); iterator vit = _start; typename initializer_list<T>::iterator lit = l.begin(); while (lit != l.end()) *vit++ = *lit++;
2)声明
①auto
auto最初是在C语言中的关键字,用于定义一个自动类型,在栈上,不用自动销毁,现已废弃
在C++中auto为自动推导类型
auto最常用在省略迭代器类型,比如:
initializer_list<T> l; auto lit = l.begin();
②decltype
关键字decltype将变量的类型声明为表达式指定的类型
比如:
const int x = 1; double y = 2.2; decltype(x * y) ret; // ret的类型是double decltype(&x) p;// p类型是int*
decltype的一些使用使用场景
template<class T1, class T2> void F(T1 t1, T2 t2) decltype(t1 * t2) ret; cout << typeid(ret).name() << endl;
③nullptr
在传统C中 ‘
NULL
’ 是 头文件 stddef.h 中的一个宏,可能被定义为字面常量0
,或者被定义为无类型指针(void*)
的常量,C++98也用的NULL#ifndef NULL #ifdef __cplusplus #define NULL 0 #else #define NULL ((void *)0) #endif #endif
所以我们在遇到这样的代码:
void f(int) cout<<"f(int)"<<endl; void f(int*) cout<<"f(int*)"<<endl; int main() f(NULL); f((int*)NULL); return 0;
第一次打印的是f(int)与我们想打印
空指针
相悖,只有第二次强制类型转换才可以达到预期效果
所以在C++中引入了专门用来表示空指针的nullptr
注意
:
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的
- 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同
- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr
3)范围for
底层其实就是迭代器,参考:C++初阶—string类的模拟实现的iterator迭代器 部分
4)智能指针(MARK一下)
5)STL变化
array<int> a1;
和int a2[10];
底层一样,除了实现了迭代器,同时array加了越界assert强制检查,而对于数组,编译器是抽查forward_list
无实际的用处,仅仅比list少用了一个指针,且只支持push_front、pop_frontunordered_map/unordered_set
,map和set的hash封装,较多数据时效率更高- 插入函数的
右值引用
版本(见下)
6)右值引用、移动语义
复习左值引用:C++初阶—C++基础入门概览引用部分
左值/左值引用:
- 左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址
- 左值引用就是给左值的引用,给左值取别名
无论左值引用还是右值引用,都是给对象取别名
右值/右值引用:
- 右值也是一个表示数据的表达式,如:字面常量、表达式返回值,传值返回函数的返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址
- 右值引用就是对右值的引用,给右值取别名
例如:
double x=1.1,y=1.2;//x,y是左值 //以下都是右值 12; x+y; min(x,y); //以下是右值引用 int&& a=10; double&& b=x+y; double&& c=min(x,y);
注意:
赋值运算符的左操作数必须为左值
- 右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置(
这个别名就变成左值了
),且可以取到该位置的地址- 加了const 的右值引用不可被修改(同左值)
左值右值比较:
- 左值引用只能引用左值,不能引用右值,但是const左值引用既可引用左值,也可引用右值
- 右值引用只能右值,不能引用左值,但是右值引用可以move以后的左值,比如下面的代码:
int a=2; int&& s=2; //下面的语句会报错: message : 无法将左值绑定到右值引用 int&& s1=a; int&& s2=s; //使用move()将a转换为右值 int&& s3=std::move(a);
右值的应用场景:
(以以前我们模拟实现的string为例)
对于只有左值引用形参的函数时,f(1)和f(a)均会匹配void f(const int& a)
,而当有右值引用形参的函数时f(1)会优先匹配void f(int&& a)
void f(const int& a) cout << "void f(const int& a)" << endl; void f(int&& a) cout << "void f(int&& a)" << endl; int a = 10; f(1); f(a);
移动构造
:// 移动构造 string(string&& s) :_str(nullptr) , _size(0) , _capacity(0) cout << "string(string&& s) -- 移动拷贝" << endl; //this->swap(s); swap(s);
注意
:出了作用域,如果返回对象不在了,不能使用引用返回(左值引用和右值引用都不可以)
以上是关于C++进阶---C++11的主要内容,如果未能解决你的问题,请参考以下文章
我的C/C++语言学习进阶之旅收集关于MODERN C++ 11/14/17/20/23 的一些资料
托管 C++ 中的逐字字面量? (就像 C# 的 @"blah")