目录
1、C++11简介
2、列表的初始化
2.1 {}初始化
2.2 initializer_list
3、auto与decltype
3.1 auto
3.2 decltype
4、范围for循环
5、右值引用和移动语义
4.1 左值引用和右值引用
4.1.1 左值引用
4.1.2 右值引用
4.2 右值引用使用场景和意义
1、C++11简介
C++11是C++语言的一个重要版本,于2011年发布。它引入了许多新特性和改进,以提高C++语言的表达能力、可移植性和性能。相比于 C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中 约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言, C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率。
2、列表的初始化
2.1 {}初始化
C++98,允许{}对数组和结构体元素进行初始化
//在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。
struct A
{int a = 0;int b = 0;
};
int main()
{int a[] = { 1,2,3,4,5,6 };A a={ 1,2 };return 0;
}
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自 定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
//在C++11中,扩大了用大括号括起的列表(初始化列表)的使用范围,
// 使其可用于所有的内置类型和用户自定义的类型
//使用初始化列表时,可添加等号(= ),也可不添加。template<class T>
struct A
{T a;T b;
};int main()
{A<int> a{1,2};int b{ 0 };return 0;
}
//创建对象时也可以使用列表初始化方式调用构造函数初始化
template<class T>
class Example
{
public:Example(T a,T b):_a(a),_b(b){}
private:int _a;int _b;
};
int main()
{Example<int> a{ 1,2 };Example<double> a={ 1.2,2.5 };return 0;
}
2.2 initializer_list
此类型用于访问 C++ 初始化列表中的值,该列表是 const T 类型的元素列表。此类型的对象由编译器根据初始化列表声明自动构造,该声明是用大括号括起来的逗号分隔元素的列表。
vector(initializer_list<T> l){_start = new T[l.size()];_finish = _start + l.size();_endofstorage = _start + l.size();iterator it = _start;typename initializer_list<T>::iterator it1 = l.begin();while (it1 != l.end()){*it++ = *it1++;}}vector<T>& operator=(initializer_list<T> l){//调用构造vector<T> tmp(l);std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstorage, tmp._endofstorage);return *this;}
3、auto与decltype
3.1 auto
#include<map>
int main()
{auto pf = strcpy;cout << typeid(pf).name() << endl;int i = 10;auto p = &i;cout << typeid(p).name() << endl;map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin(); //自动类型推断return 0;
}
3.2 decltype
decltype(expression) variable_name;
其中 expression
是一个表达式,variable_name
是待声明的变量名。decltype
将根据 expression
推导出 variable_name
的类型。
4、范围for循环
C++11引入了范围(range-based)for循环,它提供了一种简洁的方式来遍历容器(比如数组、向量、列表等)中的元素,其语法如下所示:
for (element : container)
{// 执行操作
}
其中,element
是容器中每个元素的临时变量,container
是要遍历的容器。在每次循环迭代中,element
都会被赋值为容器中的一个元素,直到遍历完整个容器。
范围for循环可以用于大多数标准容器,包括向量、列表、集合、映射等。如果你希望在遍历过程中修改容器中的元素,则应该使用引用类型的变量作为循环变量,如下所示:
#include <iostream>
#include <vector>int main()
{std::vector<int> numbers = {1, 2, 3, 4, 5};// 使用范围for循环修改整型数组中的元素for (int &num : numbers){num *= 2;}// 输出修改后的数组for (int num : numbers){std::cout << num << " ";}return 0;
}
5、右值引用和移动语义
4.1 左值引用和右值引用
4.1.1 左值引用
左值引用绑定到左值(lvalue),即可以取地址的表达式,例如变量、对象成员或返回左值的函数调用。左值引用用于引用可以修改的对象,常见的情况包括函数参数传递和赋值操作符的重载。左值引用使用 &
符号声明。
int x = 5; int& ref = x; // ref 是 x 的左值引用
int main()
{//左值int* ptr = new int[10]{ 0 };int a = 10;const int c = 20;//左值引用int*& pp = ptr;int& d = a;//定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。//c = 3; 不能赋值const int& rc = c;int& pvalue = *ptr;return 0;
}
4.1.2 右值引用
右值引用绑定到右值(rvalue),即临时对象或表达式的结果。右值引用通常用于移动语义和完美转发,它们允许我们有效地转移资源的所有权或延长临时对象的生命周期。右值引用使用 &&
符号声明。
int&& rref = 10; // rref 是 10 的右值引用
int main()
{int x = 1;int y = 2;//以下是常见的右值10;x + y;//右值引用int&& r1 = x + y;int&& r2 = 10;//不能取字面量10的地址,但是r2引用后,可以对r2取地址,也可以修改r2。r2 = 20;//如果不想r2被修改,可以用const int&& r2 去引用//const int&& r2 = 10;return 0;
}
int y = 2;int&& r3 = move(y);
4.2 右值引用使用场景和意义
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
namespace stringtest
{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(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) -- 移动语义" << endl;swap(s);}// 移动赋值string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动语义" << endl;swap(s);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] = '\0';}string operator+=(char ch)//string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}void func(stringtest::string s1)
{}
void func2(const stringtest::string& s)
{}int main()
{stringtest::string s1("hello world");func(s1);func2(s1);//左值引用减少了拷贝//string operator+=(char ch)s1 += 'h';return 0;
}
左值引用的短板:
但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。string to_string(int value) 函数中可以看到,这里只能使用传值返回,传值返回会导致至少1 次拷贝构造 ( 如果是一些旧一点的编译器可能是两次拷贝构造 ) 。
增加移动构造, 移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己 。
namespace stringtest
{stringtest::string to_string(int value){bool flag = true;if (value < 0){flag = false;value = 0 - value;}stringtest::string str;while (value > 0){int x = value % 10;value /= 10;str += ('0' + x);}if (flag == false){str += '-';}std::reverse(str.begin(), str.end());return str;}
}int main()
{stringtest::string s1 = stringtest::to_string(1235433);return 0;
}
通过观察