C++11的更新内容--auto--右值引用和移动构造--1114

1 初始化相关

1.1 {}初始化

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自 定义的类型。

struct Point
{
 int _x;
 int _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 main()
{
     //内置类型比如int
     int x1 = 1;
     int x2={ 2 };
     //数组
     int array1[]{ 1, 2, 3, 4, 5 };
     int array2[5]{ 0 };
     //自定义类型 
     Point p{ 1, 2 };
     Date d1(2022,11,14);//用构造函数初始化
     Date d2{2022,11,14};//列表初始化
     // C++11中列表初始化也可以适用于new表达式中
     int* pa = new int[4]{ 0 };
     return 0;
}

1.2 std::initializer_list

C++11中对{1,3,4}这样的列表定义了一个新的类型  --  initializer_list

auto il = { 10, 20, 30 };

cout << typeid(il).name() << endl;

 并在库中的容器的里支持了用initializer_list支持的构造函数。vector、list、map等都可以用它进行初始化。

vector v = { 1,2,3,4 };

list lt = { 1,2 };

map dict = { {"sort", "排序"}, {"insert", "插入"} };

自定义类型可以支持多个对象初始化,只需要增加initializer_list类型的构造函数即可

2 声明

2.1 auto

自动推断类型。

auto不能推导函数参数的类型,因为在函数编译阶段,还没有传递参数,就无法推演出形参的实际类型。

C+11中已经去除了auto声明自动类型变量的功能,只可以用来进行变量类型推导。

2.2 decltype

关键字decltype将变量的类型声明为表达式指定的类型。

int main()
{
    const int x = 1;
    double y = 2.2;
    decltype(x * y) ret; // ret的类型是double
    decltype(&x) p;      // p的类型是int*
    cout << typeid(ret).name() << endl;
    cout << typeid(p).name() << endl;
    return 0;
}

2.3 nullptr

3 新增容器

array --->静态数组

forward_list --->单链表

unordered_map --->哈希表

unordered_set --->哈希表

4 右值引用和移动语义

4.1 左值和右值

左值:可以取它的地址的就是左值。(左值除了被const修饰的 其余都可以修改)

右值:不能出现在赋值运算符左边的,不能被取地址的。

例如:字面常量、表达式返回值、函数返回值等。        

                10          x+y        func(x,y)

 4.2 右值引用

int main()
{
    10;
    x + y;
    Func(x, y);
    int&& rr1 = 10;
    double&& rr2 = x + y;
    double&& rr3 = Func(x, y);
}

 tip:右值不能被获取地址,但是一旦被引用之后,就可以通过对引用的取地址/修改来影响右值。但这不是重点。

无论左值引用还是右值引用,都是给对象取别名。

4.3 左值引用和右值引用的比较

左值引用只能引用左值,不能引用右值。但左值引用加了const后就可以引用右值了。

const int& ra3 = 10;

右值引用只能引用右值,不能引用左值。但是可以引用move后的左值。

int&& r3 = std::move(a);

 4.4右值引用的使用场景

之所以使用左值引用,目的是:

1、减少函数参数调用以及做返回值时的拷贝构造,以提高效率。

2、做输出型参数,修改返回对象。

短板:但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回, 只能传值返回

在我们以前的实现中,当我的返回值类型非常复杂时,比如vector<vector<int>> 或者是 红黑树、哈希表时,消耗仍然大。既然我们这个临时变量马上就要析构,C++11对于此方面进行了右值引用的优化方案。

5.移动构造

拿一个string类进行说明。主要关注对象是移动构造、和移动拷贝。

        // 拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 拷贝构造(深拷贝)" << endl;

			string tmp(s._str);
			swap(s);
		}

		// 移动构造
		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 资源转移" << endl;
			swap(s);
		}

拷贝构造传入的参数是引用类型,以后还需要继续使用。所以不能直接交换,需要创建一个对象,用这个对象进行交换。

既然局部对象拷贝后,本来就需要销毁。那还为什么要创建一个新的临时变量以完成交换呢?我们直接使用这个局部对象进行交换不就好了吗?

如果实现了移动构造,在局部对象即将出作用域的时候,就会被识别称为将亡值,从而调用移动构造,减少了一次创建对象并拷贝的过程。

namespace chy
{
	class string
	{
	public:

		// 拷贝赋值
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 拷贝赋值(深拷贝)" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

		// 移动赋值
		string& operator=(string&& s)
		{
			cout << "string& operator=(string s) -- 移动赋值(资源移动)" << endl;
			swap(s);

			return *this;
		}
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}
		string to_string(int value)
        {
	        bool flag = true;
	        if (value < 0)
	        {
		        flag = false;
		        value = 0 - value;
	        }

	        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;
        }
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。

当我们调用to_string()函数时,编译器会将其的返回值识别成右值,从而调用类型最匹配的移动拷贝。如果没实现移动构造和移动赋值,那依然走的是深拷贝。

int main() 
{
    chy::string ret; 
    ret=to_string(-3456); //移动赋值
    chy::string ret2=chy::to_string(-1234);//移动拷贝    
    return 0;   
}

 一些例子

int main()
{
	chy::string str1("hello");//拷贝构造
	chy::string str2(str1); //  拷贝构造
	chy::string str3(move(str1)); // 移动构造


	std::string s1("hello world");//拷贝构造
	std::string s2(s1); // 拷贝构造

	// std::string s3(s1+s2);
	std::string s3 = s1 + s2; // 移动构造
	std::string s4 = move(s1);//移动构造

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值