C++11如何优化代码写法

1 auto关键字

使用auto声明的变量必须马上初始化,以让编译器推断出他的实际类型,在编译时期将auto占位符替换为真的类型

	auto x = 10;
	const auto *v = &x;
	static auto y = 0.0f;
使用方式

注意事项:

  • auto不能用于函数参数
  • 不能用于非静态成员变量
  • auto使用时机:
    stl的迭代器
	 std::unordered_multimap<int, int> resultmap;
	 std::pair<std::unordered_multimap<int, int>::iterator , std::unordered_multimap<int, int>::iterator> range = resultmap.equal_range(key);

使用auto类型推导可以直接改成

	 std::unordered_multimap<int, int> resultmap;
	 auto range = resultmap.equal_range(key);

2 decltype关键字

decltype用于编译时推导出一个表达式的类型,语法格式如下:

	decltype(exp) // exp表示一个表达式

举例子:

	int x = 0;
	decltype(x) y = 1;   // y->int
	decltype(x+y) z = 2; // z->int
decltype推导规则
  • exp是标识符、类访问表达式:返回类型和exp一致
  • exp是函数调用:返回类型和函数返回值的类型一致
  • exp是左值:返回类型是exp的一个左值引用
  • 其他情况:返回类型就是exp类型

decltype的实际应用:

	template<typename t>
	class a
	{
	private :
		decltype(t.begin()) m_it;
		//typename t::iterator m_it;   //这种用法会出错
	public:
		void func(t& container)
		{
		   m_it=container.begin();
		}
	};
返回类型后置语法

C++11增加返回类型后置语法,将decltype和auto结合起来使用

	template <typename T, typename U>
	auto add(T t,U u)->decltype(t + u)
	{
		return t + u;
	}

3 模板的别名

C++中可以使用typedef重定义一个类型:typedef unsigned int uint_t; 但是不支持重定义函数模板
C++11新增重定义模板别名语法

	template <typename val>
	using str_map_t = std::map<std::string, Val>;
	str_map_t<int> map1;

C++11的using语法和typedef一样,不会创造新类型,只是写法不一样

4 列表初始化

C++11中的列表初始化可以用于任何类型的对象,不仅限于之前的普通数组和POD类型。

	class C
	{
	public:
		C(int){};
	private:
		C(const C&);
	}
	int main()
	{
		C A{123};   
		C B = {123};// 列表初始化,私有拷贝构造不会影响
		
		int a = {3};
		int a {3};
	}
任意长度的初始化列表

为类添加一个std::initializar_list构造函数,就可以拥有任意长度的初始化能力

	class C
	{
	public:
		C(std::initializer_list<int>){}
	}
	C c = {1, 2, 3, 4, 5}; // OK

5 基于范围的for循环遍历

auto自动推导出的是容器的value_type而不是迭代器

	std::vector<int> arr {1, 2, 3};
	for(auto & a: arr)    // 去掉&代表拷贝遍历,有&代表可以修改,加上const减少拷贝
	{
		a++;
	}

注意:

  • set内部元素不能改变,set < int >使用auto&a 会自动转换const int &,在遍历内部修改值会报错
  • 遍历时修改容器可能引起迭代器失效,基于范围的for循环倾向于在循环开始前确定好迭代范围,而不是每次迭代之前都去调用一次arr.end()

6 std::function 和bind绑定器

std::function

C++中函数指针的用途非常广泛,例如回调函数接口类的设计等,但它只能指向全局或静态函数,无法用在类成员函数、lambda表达式或其他可调用对象,所以可以理解std::function是高级版的函数指针,作用范围更广。

可调用对象定义:

  • 函数指针
  • 具有operator()成员函数的类对象(仿函数)
  • 可被转换为函数指针的类对象
  • 类成员(函数)指针

使用代码示例:

	typedef std::function<void ()> PrintFinFunction;
	void print(const char *text, PrintFinFunction callback) 
	{
	    printf("%s\n", text);
	    if (callback)
	        callback();
	}
	// 普通函数
	void printFinCallback() 
	{
	    cout << "Normal callback" << endl;
	}
	// 类静态函数
	class Test 
	{
	public:
	    static void printFinCallback() 
	    {
	        cout << "Static callback" << endl;
	    }
	};
	// 仿函数,重载()运算符
	struct Functor {
	    void operator() () 
	    {
	        cout << "Functor callback" << endl;
	    }
	};
	
	print("test 1", printFinCallback);
	print("test 2", Test::printFinCallback);
	print("test 3", Functor());
	
	// 直接传入lambda表达式
	print("test 4", [] () {
		cout << "Lambda callback" << endl;
	});
std::bind绑定器

std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。
主要两大作用:

  • 将可调用对象与其参数一起绑定成一个仿函数
  • 将参数个数大于1的可调用对象转成1员或者n-1元可调用对象,即只绑定部分参数

举个栗子比喻一下,可调用对象就像一个枪,而参数就是子弹,一个函数的完整调用就是装子弹开枪的过程,而绑定器就相当于装子弹,装完子弹,放到一边,给枪命个名(std::function),比如biubiu枪,然后去做别的事情,当我要开biubiu枪(调用函数)的时候就不用装弹了,直接拿起来就开枪了。

代码示例:

	class A
	{
	public:
		 int i = 0; // C++11支持非静态成员变量就地初始化
		 void output(int x, int y)
		 {
			 std::cout << x << " "<< y <<std::endl;
		 }
	}
	int main()
	{
		A a; // 声明一个对象
	
		// f绑定了实例化的a,因为非静态成员函数需要传入this指针,所以需要传入a的地址,然后两个参数进行占位,等到真正调用的时候再传入参数
		std::function<void(int, int)> f = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2); 
		
		fr(1, 2); //传入参数1,2 整个过程就跟a.output(1,2);一样 输出:1 2
	
		// fr绑定类A的成员变量i,然后把对象a的地址传入fr,就相当于fr == a.i;
		std::function<int&(void)> fr = std::bind(&A::i, &a);
		 
		fr_i() = 123; // 等同于a.i = 123;
		std::cout <<a.i<<std::endl;//输出:123
	}

lambda表达式

lambda定义:就地定义一个匿名函数,并且可以捕获一定范围内的变量语法形式:

[ 捕获列表 ] ( 参数列表 ) 函数选项 -> 返回值类型 { 表达式 } ;

在C++11中,lambda表达式的返回值是通过前面介绍的返回值后置语法来定义的,所以大多数情况lambda表达式的返回值是非常明显的,所以C++11允许省略lambda表达式的返回值定义,变成如下形式:

[ 捕获列表 ] ( 参数列表 ) { 表达式 } ;

没有参数列表的时候还能省略空参数列表,变成如下形式:

[ 捕获列表 ] { 表达式 } ;

代码示例:

	auto f = [](int a)->int{return a + 1;};
	f(1);
	auto f1 = [](int a){return a + 1; };
	f1(2);
	auto f3 = []{return 3;};
	f3();
  • 捕获范围变量以及意义
写法含义
[ ]不捕获任何变量
[&]引用捕获外部作用域所有变量,在表达式内部可以改变外部变量
[=]按值捕获外部作用域所有变量,无法在表达式内部改变外部变量
[this]捕获当前对象的this指针,表达式内部就可以调用对象的成员变量成员函数
  • 没有捕获任何变量的lambda表达式可以转换为普通函数指针
	using func_t = int(*)(int);
	func_t f = [](int a){return a ;};
	f(123);
lambda表达式相关用法

1.简化库函数调用

	std::vector<int> v = {1,2,3,4,5};
	int even_count = 0;
	for_each(v.begin(),v.end(),[&even_count](int val)
	{
		if(!(val & 1))
		{
			++even_count;
		}
	});
	std::cout << "nums of even " << even_count <<std::endl;

2.std::function

	typedef std::function<void ()> FinFunction;
	void f(FinFunction callback) 
	{
	    if (callback)
	        callback();
	}
	int main()
	{
		f([]()
			{ 
			   std::cout << "lambda" <<std::endl;
			});
	}

3.std::thread实现小闭包线程

	#include <thread>
	#include <iostream>
	int main()
	{
	    std::thread thread1([](){
	    std::cout<<"lambda thread called." <<std::endl;
	    });
	    thread1.join(); // join会阻塞main函数,detach不是阻塞的,但是thread1出了main的作用域就被析构了
	    return 0;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值