C++11 的一些新特性

一.std::initializer_list(列表初始化)

这是官方库里的声明,他是含有一个参数的类模板,当我们在用{}来实例化对象时会自动生成initializer_list<T>临时对象,当需要实例化的对象有接收initializer_list<T>类型的构造函数时,便可以用他来实例化。

我们可以从官方库里看到list,vector等stl容器的构造函数确实有initializer_list<T>类型的参数

下面我们来实现一下;

template<T>
class list
{
        list(initializer_list<T> lt)
		{
			empty_init();
			for (const auto& e : lt)
			{
				push_back(e);
			}
		}





省略其他的代码
}

int main()
{
    list<int> l={1,1,1,1};
}

这里我们来分析一下过程:首先{1,1,1,1}生成了一个 initializer_list<int>的临时对象,传递给list(initializer_list<T> lt)的构造函数,构造函数再用initalizer_list<int>对象取出数据进行构造,从而实现。

这里我们再讲解一个经常让人费解的情形

int main()
{
	
	map<string, string> dirt1 = { {"s1","1"},{"s2","2"} };
}

map的构造函数如下: 

这里的value_type指的是pair<const string,string>,我们来分析过程:

首先:{{"s1","1"},{"s1",“2”}会生成pair<const char*,const char*>,然后用 pair<const char*,const char*>构造生成pair<const string,string>,最后生成initializer_list<pair<const string,string>>传入构造函数。

这里pair<const char*,const char*>能转化到pair<const char*,const char*>主要pair里的一个特殊构造构造

namespace bit
{
	// 16:10分
	template <class T1, class T2>
	struct pair
	{
		// pair<const char*, const char*> kv3("sort", "排序");
		pair(const T1& t1, const T2& t2)
			:first(t1)
			, second(t2)
		{}

		// 	pair<const string, string> kv4(kv3);
		template<class U, class V>
		pair(const pair<U, V>& kv)
			: first(kv.first)
			, second(kv.second)
		{}

	private:
		T1 first;
		T2 second;
	};
}

该构造函数新增了俩个模板参数,从而实现特殊构造。

二.右值引用和移动语义

2.1 左值引用和右值引用

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋 值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引 用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能 取地址。右值引用就是对右值的引用,给右值取别名。

int main()
{
double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);
// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10 = 1;
x + y = 1;
fmin(x, y) = 1;
return 0;
}

俩者的区别在于:左值大部分能改变,右值不能,左值可以取地址,右值不能。

2.2移动构造和移动赋值

我们在库里经常的看到类似这样的构造函数,这就是移动构造。

我们先了解下右值的分类 

我们用代码讲解: 

int main()
{
    vector<int> v1={1,1,1};
    vector<int> v2=move(v1);
}

move函数的作用是将左值变成右值,move后生成临时对象,按照普通的拷贝构造流程是:将临时对象的资源拷贝一份,不改变临时对象的资源。移动拷贝则是:将临时对象的资源转移到自己的对象上,拷贝。对于将王值来说,他的资源将释放,被移动也不会有影响。因此对于右值来说运用移动构造和移动拷贝是更适合,提高效率的。下面我们来简单string中的移动构造和移动赋值实现。

namespace bit
{
	class string
	{
	public:
		
		// 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;

			_str = new char[s._capacity+1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}

		// 移动构造 -- 右值(将亡值)
		string(string&& s)
		{
			cout << "string(string&& s) -- 移动拷贝" << endl;
			swap(s);
		}

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

			return *this;
		}

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

			return *this;
		}

		


省略其他代码


	private:
		char* _str = nullptr;
		size_t _size = 0;
		size_t _capacity = 0; // 不包含最后做标识的\0
	};
 2.3 std::forward

我们要注意的对右值进行引用,引用后的值变成了左值,这是为了能改变引用后的右值。而std::forward 可以让引用后的值保持原有的特性。

三.可变参数模板包

3.1定义

template <class ...Args> void ShowList(Args... args) {}

例子:

template <class T> 
void ShowList(const T& t)
 { 
    cout << t << endl; 
} 
// 展开函数 
template <class T, class ...Args> 
void ShowList(T value, Args... args) 
{ 
    cout << value <<" "; ShowList(args...); 
}
 int main()
 { 
    ShowList(1);
     ShowList(1, 'A'); 
    ShowList(1, 'A', std::string("sort"));
     return 0;
 }

 运行结果:

3.2emplace_back  

C++11的各个STL容器还新增了emplace_back这个新成员函数,声明如下:

template <class... Args> void emplace_back (Args&&... args);

我们看代码来分析作用

int main()
{
	list<Date> l1;
	l1.push_back({1,1,1});
	l1.emplace_back(1, 1, 1);
}

首先我们先看push_back(),push_back({1,1,1})先在外边生成Date对象传给l1,l1在内部拷贝构造(或者移动构造)一个新Date对象,再插入到后续节点。emplace_back(1,1,1)则是直接将参数传给l1内部,在l1内部调用构造函数生成Date插入后续节点。比较来看emplace_back更能提高效率。

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值