C++11新特性

1.Initiallizer list

C++11 提供了统一的语法来初始化任意的对象,例如:

struct A {
    int a;
    float b;
};
struct B {

    B(int _a, float _b): a(_a), b(_b) {}
private:
    int a;
    float b;
};

A a {1, 1.1};    // 统一的初始化语法
B b {2, 2.2};

C++11 还把初始化列表的概念绑定到了类型上,并将其称之为 std::initializer_list,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:

#include <initializer_list>

class Magic {
public:
    Magic(std::initializer_list<int> list) {}
};

Magic magic = {1,2,3,4,5};
std::vector<int> v = {1, 2, 3, 4};

2. auto type

std::vector<int> vec = { 1,2,3,4 };
	//c++ 03
	for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); it++) {
		std::cout << *it << std::endl;
	}

	//c++ 11
	for (auto it = vec.begin(); it != vec.end(); it++) {
		std::cout << *it << std::endl;
	}

	auto a = 3 + 2;
	auto s = "123";

注意:auto 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板):

int add(auto x, auto y);

此外,auto 还不能用于推导数组类型:

#include <iostream>

int main() {
 auto i = 5;

 int arr[10] = {0};
 auto auto_arr = arr;
 auto auto_arr2[10] = arr;

 return 0;
}

decltype

decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 sizeof 很相似:

decltype(表达式)

在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
有时候,我们可能需要计算某个表达式的类型,例如:

auto x = 1;
auto y = 2;
decltype(x+y) z;

拖尾返回类型、auto 与 decltype 配合

你可能会思考,auto 能不能用于推导函数的返回类型。考虑这样一个例子加法函数的例子,在传统 C++ 中我们必须这么写:

template<typename R, typename T, typename U>
R add(T x, U y) {
    return x+y
}

这样的代码其实变得很丑陋,因为程序员在使用这个模板函数的时候,必须明确指出返回类型。但事实上我们并不知道 add() 这个函数会做什么样的操作,获得一个什么样的返回类型。

在 C++11 中这个问题得到解决。虽然你可能马上回反应出来使用 decltype 推导 x+y 的类型,写出这样的代码:

decltype(x+y) add(T x, U y);

但事实上这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,x 和 y 尚未被定义。为了解决这个问题,C++11 还引入了一个叫做拖尾返回类型(trailing return type),利用 auto 关键字将返回类型后置:

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

从 C++14 开始是可以直接让普通函数具备返回值推导,因此下面的写法变得合法:

template<typename T, typename U>
auto add(T x, U y) {
    return x+y;
}

3. foreach 写c++像是在写c#

std::vector<int> vec = { 1,2,3,4 };
	//c++ 03
	for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); it++) {
		std::cout << *it << std::endl;
	}

	//c++ 11
	//read only(只读)
	for (auto i : vec) {
		std::cout << i << std::endl;
	}
    //只读
    for (const auto& i : vec) {
		std::cout << i << std::endl;
	}
	//changes the values in v(修改)
	for (auto& i : vec) {
		i = 3;
	}

4. nullptr 代替了c++ 03的NULL

nullptr 出现的目的是为了替代 NULL。

在某种意义上来说,传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0。
C++ 不允许直接将 void * 隐式转换到其他类型,但如果 NULL 被定义为 ((void*)0),那么当编译char *ch = NULL;时,NULL 只好被定义为 0。

而这依然会产生问题,将导致了 C++ 中重载特性会发生混乱,考虑:

void foo(char *);
void foo(int);

对于这两个函数来说,如果 NULL 又被定义为了 0 那么 foo(NULL); 这个语句将会去调用 foo(int),从而导致代码违反直观。
为了解决这个问题,C++11 引入了 nullptr 关键字,专门用来区分空指针、0。

nullptr 的类型为 nullptr_t,能够隐式的转换为任何指针或成员指针的类型,也能和他们进行相等或者不等的比较。

当需要使用 NULL 时候,养成直接使用 nullptr的习惯。

5. enum class代替了c++03的enum,更安全

详情请点击这里

        //c++ 03
	enum ship {
		bulkship,
		tankership
	};

	//c++ 11
	enum class ship1 {
		bulkship,
		tankership
	};

6. override关键标识 for virtual function (更加安全,直观 )

class base {
public:
	virtual void fun1(int);
	virtual void fun2() const;
	void fun3(int);
};

class son :public base {
	//c++ 03 存在隐患
	/*
	void fun1(float);  //不小心写错了参数,ok 编译通过,create a new func
	void fun2();       //不小心少写了const,ok 编译通过,create a new func
	void fun3();
	*/

	// but in c++ 11 更安全清晰
	void fun1(float) override; //编译Error: no func to override
	void fun2() override;      //编译Error: no func to override
	void fun3() override;      //编译Error: no func to override			
};

7. final关键标识 ,主要是class 及virtual function

//this is means no class can be derived from CPoint2D
class CPoint2D final {
	//No class can override Draw
	virtual void Draw() final;
};

8. 关键字default标识, compiler generated default constructor

强制编译器生成默认构造函数

  • 编译器不再生成默认构造函数

    class CPoint2D {
    public:
    	CPoint2D(double x_,double y_) {
    		x = x_;
    		y = y_;
    	}
    	double x;
    	double y;
    };
    
    int main(){	
    	CPoint2D pt;//编译报错,不存在默认构造函数,因为编译器不再生成	
    }
    
    
  • 通过default force compiler to generate the default constructor

    class CPoint2D {
    public:
    	CPoint2D(double x_,double y_) {
    		x = x_;
    		y = y_;
    	}
    	CPoint2D() = default;//告诉编译器强制生成
    	double x;
    	double y;
    };
    
    int main(){	
    	CPoint2D pt;//ok	
    }
    

9. 关键标识 delete ,放在函数后面,表示函数不能被调用

  • 看以前的情形,构造函数只接受int,但是输入double也是可以的,因为会自动转换
    class dog {
    public:
    	dog(int age_) {
    		age = age_;
    	}
    	int age;
    };
    
    int main(){	
    	dog(2);   //ok
    	dog(4.5); //also ok,converted form double to int
    }
    
  • c++ 11 解决方案
    class dog {
    public:
    	dog(int age_) {
    		age = age_;
    	}
    	dog(double) = delete;
    	int age;
    };
    
    int main(){	
    	dog(2);   //ok
    	dog(4.5); //not ok,已经删除的函数
    }
    

10. lambda function, 让编程更优雅

  • 像c#一样优雅起来
    int main(){	
    	std::cout << [](int x, int y) {return x + y; }(3, 5) << std::endl;
    }
    
  • 我经常使用std::sort对结构体配合Lammda匿名函数使用,比如点的数组按照x升序排列
    class CPoint2D {
    public:
    	CPoint2D(double x1,double y1) {
    		x = x1; y = y1;
    	}
    	double x;
    	double y;
    };
    int main(){
    
    	std::vector<CPoint2D> Pts{ {1,1},{3,3},{2,2} };
    	std::sort(begin(Pts), end(Pts), [](const CPoint2D& pt1, const CPoint2D& pt2) {
    		return  pt1.x < pt2.x;
    		});
    }
    

11.smart pointer 智能指针

终于从内存的泥潭中解放出来了,参考另外一篇文章

12. tuple元祖

  • std::pair<std::string,int>的扩展版,可以当做一个通用的结构体来使用
  • tuple 某些情况下会让代码的易读性变差
  • 什么情况下使用tuple

std::pair<>的扩展版,可多个参数

std::pair<int, std::string> p = std::make_pair(2, "hello");
//std::pair的扩展版
std::tuple<int, std::string, char> t(2,"foo",'a');
auto t1 = std::make_tuple(0, "dog", 'b');
std::cout << std::get<0>(t) << std::endl;
std::cout << std::get<1>(t) << std::endl;
std::cout << std::get<2>(t) << std::endl;

易读性会变差(一个使用结构体,一个使用tuple)

结构体比较清晰 p1.name p1.age

tuple易读性差,通过 std::get<0>(t2) std::get<1>(t2)取值

你会想what stored in position 0&what stored in position 1?

struct person {
	std::string name;
	int age;
};
person p1;
std::tuple<std::string, int> t2;

//把上面的代码遮挡起来 tuple会导致代码的易读性降低
std::cout << p1.name << p1.age<<std::endl;
std::cout << std::get<0>(t2) << std::get<1>(t2) << std::endl;
//what stored in position 0?  what stored in position 1?

when to use tuple

(1) As a one-time only structure to transfer g group of data 比如作为函数的返回值使用,这样就不用声明结构体了

std::tuple<std::string,int> GetNameAge(){
	return std::make_tuple("lcl", 20);
}

(2)comparison of tuples

//hours, minutes, seconds

//comparison of tuples
std::tuple<int, int, int> time1, time2;  
if (time1 > time2) {
	//...
}

(3)Muti index map

std::map<std::tuple<int, char, double>, float> m;

插入几张图片 传统c++ 和 Modern c++
在这里插入图片描述在这里插入图片描述在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值