您现在的位置是:首页 >其他 >【C++】C++11新特性的讲解网站首页其他
【C++】C++11新特性的讲解
新特性讲解第一篇~
前言
一、较为重要的新特性
1.统一的列表初始化
{}初始化相信大家应该并不陌生,比如int a[] = {1,2,3,4},而在c++11中,万物均可用{}进行初始化,并且还可以省略赋值符号。
int main()
{
int x1 = 1;
int x2{ 55 };
return 0;
}
下面我们再演示一下自定义类型:
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 main()
{
Date d1(2023, 1, 9);
Date d2{ 2024,5,1 };
return 0;
}
自定义类型也没有问题,下面我们再看看list,因为list和vector的意义不太一样:
为什么说意义不一样呢,因为我们刚刚的内置类型用{}初始化是调用构造函数,自定义类型也一样。那么vector和list里面的参数都是可变的,这是怎么支持的呢?这是因为c++11增加了std::initializer_list的类,下面我们看看:
我们可以看到这个花括号的类型是一个initializer_list,下面我们看看这个可以修改吗:
我们可以看到initializer_list指向的内容是不可以被修改的,因为initializer_list是存在常量区当中的。那么STL是如何支持用initializer_list初始化的呢?其实也很简单,就是增加一个支持用initializer_list初始化的构造函数,如下图所示:
下面我们再看看其他初始化的用法:
v3的初始化是先用里面的{}构造一个匿名对象,然后再调用initializer_list初始化。
2.decltype关键字
int main()
{
const int x = 1;
double y = 2.2;
vector<decltype(x* y)> ret;
return 0;
}
这个关键字的作用就这么多我们就不再演示了。
3.右值引用和移动语义
int main()
{
// 10 一个常量
// x + y 一个表达式
// fmin(x,y) 一个函数返回值
return 0;
}
下面我们先看一下左值引用可以引用右值吗:
那么表达式呢?
同样不行,但是我们说过向函数的返回值这些都是临时变量具有常性,所以我们可以加上const:
没错,我们的左值引用既可以引用左值也可以引用右值。下面我们用右值引用试试:
int main()
{
int&& a1 = 10;
double x = 10, y = 20;
double&& ret = x + y;
return 0;
}
刚刚我们的左值引用既可以引用左值也可以引用右值,下面我们看看右值引用能否引用左值呢?
很明显右值引用是无法引用左值的,在这里我们说一个小细节:右值引用可以给move后的左值取别名:
move是什么意思呢?move可以将一个值变成将亡值,比如上图中我们的a变量,这个变量的声明周期本来在这个main函数内,但是经过move后a的声明周期变成了200行这一行,这就是move的作用,也就是move后一定是右值。
下面我们先对比一下左值引用和右值引用,然后我们就进入右值引用+移动语义的学习。
首先我们可以看到左值引用和右值引用是可以构成重载的,下面我们调用一下看看:
void func(int& a)
{
cout << "func(int& a)" << endl;
}
void func(int&& a)
{
cout << "func(int&& a)" << endl;
}
int main()
{
int x = 10, y = 20;
func(x);
func(x + y);
return 0;
}
可以看到编译器是可以正确识别左值和右值的,下面我们用string类做一下演示:
我们可以看到库中的string类是支持右值的,下面我们讲讲这里支持右值的好处:
本来s1+s2的返回值会调用一次拷贝构造构造一个匿名对象,然后再用这个匿名对象调用拷贝构造来给ret(注意这里不是赋值,因为ret是一个新的对象,赋值只针对已经定义过的对象),所以这里耗费的资源是很大的,而有了右值引用+移动语义后这里就变成了直接将返回值和ret交换,也就是说ret直接拿到了s1+s2返回值的资源。下面我们用自己实现的string来试试:
namespace sxy
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
//cout << "string(char* str)" << endl;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
// s1.swap(s2)
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
// 拷贝构造
string(const string& s)
:_str(nullptr)
{
cout << "string(const string& s) -- 深拷贝" << endl;
string tmp(s._str);
swap(tmp);
}
// 赋值重载
string& operator=(const string& s)
{
cout << "string& operator=(string s) -- 深拷贝" << endl;
string tmp(s);
swap(tmp);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)
{
if (_size >= _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
++_size;
_str[_size] = '