C++ 2.0 //C++ 11总结

1 篇文章 0 订阅
1 篇文章 0 订阅

1.variadic template(数量不定的模板)

用… 表示数量不定

#include<cstdlib>
#include<iostream>
#include<string>
using namespace std;
void print() {
	//相当于递归出口
}
template<typename T,typename...Types>
void print(T first ,Types... args) {
	cout << first << endl;
	print(args...);
}
int main() {
	print(1, 2.3, "hello");
	system("pause");
	return 0;
}
1
2.3
hello
请按任意键继续. . .

通过递归继承来实现简单tuple

#include<cstdlib>
#include<iostream>
#include<string>
using namespace std;
template<typename... Values> class tup;

template<>
class tup<>{};


template<class Head,typename... Tail>
class tup<Head,Tail...>:private tup<Tail...>{
	typedef tup<Tail...> inherited;
public:
	tup() {}
	tup(Head v, Tail... vtail) :m_head(v), inherited(vtail...) {}

	Head head() { return m_head; }
	inherited& tail() {return *this;}
protected:
	Head m_head;
};

int main() {
	
	tup<int, double, string> v(2, 3.23, "hehe" );
	cout << v.head() << " " << v.tail().head() << " " << v.tail().tail().head()<< endl;
	system("pause");
	return 0;

}
2 3.23 hehe
请按任意键继续. . .

以复合的形式实现简易tuple

#include<iostream>
#include<string>
using namespace std;
template<typename... Values> class tup {};

template<typename Head,typename...Tail>
class tup<Head, Tail...> {
	typedef tup<Tail...> composited;
public:
	tup(Head v, Tail... vtail) :m_head(v), m_tail(vtail...) {}
	Head head() {
		return m_head;
	}	
	composited tail() {
		return m_tail;
	}
protected:
	Head m_head;
	composited m_tail;
};

int main(){
	tup<int, double, string> v{ 2,3.2,"heh" };
	cout << v.tail().tail().head() << endl;
	cout << v.tail().head() << endl;
	cout << v.head() <<endl;
	system("pause");
	return 0;
}
heh
3.2
2
请按任意键继续. . .

2.auto 自动类型推导

list<string> c;
list<string>::iterator ite;
ite = find(c.begin(),c.end(),target);


list<string> c;
...
auto ite = find(c.begin(),c.end(),target);


list<string> c;
...
auto ite; //错误的
ite = find(c.begin(),c.end(),target);

auto主要用在比较复杂的类型上,让编译器自己去推到,比如lambda,容器的迭代器等

auto  l = [](int x)->int{return x + 1; };

3.ranged-base for

for(decl:coll){
	statement
}

注意有引用和没引用的区别,有引用的可以改变原来的值

vector<double> vec;
for(auto elem : vec){}//对原来值的copy
for(auto& elem :vec){}//对每一个元素的引用 修改elem也会改变原vector的值

4.nullptr and std::nullptr_t

c++11使用nullptr而不是0或NULL 来表示一个空指针
nullptr是一个新关键字。它会自动转换为每种指针类型,但不会转换为任何整数类型。它的类型为std :: nullptr_t,因此对于传递空指针的情况,甚至可以重载操作。

#include<cstdlib>
#include<iostream>
#include<string>
using namespace std;
void f(int) {
	cout << "void f(int)" << endl;
}
void f(void *) {
	cout << "void f(void *)" << endl;
}
int main() {
	f(0);// call void f(int)
	f(nullptr);//call void f(void *)
	f(NULL);//call void f(int) 会ambiguous
	system("pause");
	return 0;
}
void f(int)
void f(void *)
void f(int)
请按任意键继续. . .

从上述代码可以看出f(NULL)调用的是和f(int) ,所以会产生歧义,所以新标准要求我们使用nullptr来表示空指针。

5.Uniform Initialization

1.一致性初始化

C++11之前初始化时存在多个版本{},(),=。,让使用者使用时比较混乱,C++11提供一种万用的初始化方法,就是使用大括号{}。

原理解析:当编译器看到大括号包起来的东西{t1,t2…tn}时,会生成一个initializer_list(initializer_list它其实是关联一个array<T,n>)。

1.调用函数(例如构造函数ctor)时该array内的元素可被编译器分解逐一传给函数;元素逐一分解传递给函数进行初始化 2. 但是如果调用函数自身提供了initializer_list参数类型的构造函数时,则不会分解而是直接传过去。直接整包传入进行初始化。所有的容器都可以接受这样的参数
int i; // i 初始化为未定义值.
int j{}; // j 初始化为 0 (大括号可以用来设初值)
int * p; // p 初始化为未定义值.
int * q{}; // q 初始化为 0 (大括号可以用来设初值)
 
// 窄化(精度降低或造成数值变动)对大括号而言是不成立的.
int x0(3.4); // ok.
int x1 = 3.4; // ok.
int x2 { 3.4 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)
int x3 = { 3.4 }; // wrong.(不允许窄化数据处理,其实我的编译器只给警告)
std::vector<int> v1 { 1, 2, 3 }; // ok.
std::vector<int> v2 { 1.1, 2.2, 3.3 }; // wrong.

{}初始化列表会对内置类型或自定义类型进行默认初始化,同时{}对数据更为严格,不允许类型的窄化。

2.initializer_list使用举例:

initializer_list是一个class(类模板),这个必须类型要一致,跟模板不定的参数类型相比,模板不定的参数类型可以都不一样。

initializer_list类似于容器的使用方法

class P {
public:
	//版本一
	P(int a, int b) {
		cout << "P(int a, int b) a=" << a << ",b=" << b << endl;
	}
	//版本二
	P(initializer_list<int> L) {
		cout << "P(initializer_list<int> L) ,values= ";
		for (auto i : L) {
			cout << i << " ";
			
		}cout << endl;
	}
};
int main() {
	P p(2, 3);
	P q{ 3,4 };
	P r{ 3,4,5 };
	P s = { 2,3 };
	//P t(2, 3, 4); //wrong

	system("pause");
	return 0;

}
P(int a, int b) a=2,b=3
P(initializer_list<int> L) ,values= 3 4
P(initializer_list<int> L) ,values= 3 4 5
P(initializer_list<int> L) ,values= 2 3

如果没有版本2,只有版本1。q和s不变,但是会分为两个参数传入。r将会不成立。
只要编译器遇到大括号里面有一些数,再传值的时候都会去生成一个initializer_list去处理;
initializer_list这个和前面一章节提到的不定参数模板相比,这个必须类型要一致,而后者则可以类型随意组合

3.源码分析:

1.initializer_list背后有array数组支撑,initializer_list它其实是关联一个array<T,n>

2.array是个指针,只是一个浅拷贝动作,比较危险,两个指针指向同一个内存

3、Initializer_list的源码定义(来自VS2017)

template<class _Elem>
	class initializer_list
	{	// list of pointers to elements
public:
	typedef _Elem value_type;
	typedef const _Elem& reference;
	typedef const _Elem& const_reference;
	typedef size_t size_type;

	typedef const _Elem* iterator;
	typedef const _Elem* const_iterator;

	constexpr initializer_list() noexcept
		: _First(nullptr), _Last(nullptr)
		{	// empty list
		}

	constexpr initializer_list(const _Elem *_First_arg,
		const _Elem *_Last_arg) noexcept
		: _First(_First_arg), _Last(_Last_arg)
		{	// construct with pointers
		}

	_NODISCARD constexpr const _Elem * begin() const noexcept
		{	// get beginning of list
		return (_First);
		}

	_NODISCARD constexpr const _Elem * end() const noexcept
		{	// get end of list
		return (_Last);
		}

	_NODISCARD constexpr size_t size() const noexcept
		{	// get length of list
		return (static_cast<size_t>(_Last - _First));
		}

private:
	const _Elem *_First;
	const _Elem *_Last;
	};

源码分析:根据侯捷老师所讲,编译器首先会调用constexpr initializer_list()进行构造,但是在调用前initializer_list的背后已经关联好了array<T,_Size>数组,而函数中的_First和_Last会分别为指向array数组的起始元素和末尾的元素。(这里没有采用侯捷老师讲课视频中的源码,视频中那一版源码与VS2017提供的稍有不同,视频中,initializer_list类的成员函数不是_First和_Last这两个指针,而是一个iterator类的变量用来指向array数组的首元素和表示数组大小的size_type类型的变量,其实道理时一样的),需要注意的是这些元素都被包含在array数组中,initializer_list中没有包含这些元素,它提供的是指向array的指针,所以如果进行拷贝动作,只是两个initializer_list的指针指向同一个array,本质上是做的一个浅拷贝。还需要注意的是,size()的返回值为size_t,不是int,这里将_Last与_First相减,结果被static_cast转化为了size_t类型。

6.explicit

1. explicit for ctors taking one argument(构造函数一个实参)

class Complex {
public:
	int real, imag;
	Complex(int re, int im = 0) :real(re), imag(im) {}
	Complex operator+(const Complex& x) {
		return Complex(real + x.real, imag + x.imag);
	}
};
int main() {
	
	Complex c1(2, 3);
	Complex c2 = c1 + 5;//这里5会转变成Complex(5);
	cout << c2.real << " " << c2.imag << endl;
	system("pause");
	return 0;

}
7 3
请按任意键继续. . .
class Complex {
public:
	int real, imag;
	explicit Complex(int re, int im = 0) :real(re), imag(im) {}
	Complex operator+(const Complex& x) {
		return Complex(real + x.real, imag + x.imag);
	}
};

在前面加了个explicit 就会报错 无法进行类型转换
错误 二进制“+”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)
错误 “Complex”: 没有合适的默认构造函数可用

2.explicit for ctors taking more than one argument(构造函数超过一个实参)

class P {
public:
	P(int a, int b) {
		cout << "P(int a, int b) " << endl;
	}
	 P(int a,int b, int c) {
		cout << "P(int a,int b, int c)" << endl;
	}
	
};
int main() {
	
	P p( 2, 3);
	P s = { 2,3,4 };
	system("pause");
	return 0;

}
P(int a, int b)
P(int a,int b, int c)
请按任意键继续. . .
class P {
public:
	P(int a, int b) {
		cout << "P(int a, int b) " << endl;
	}
	//加了个explicit
	 explicit P(int a,int b, int c) {
		cout << "P(int a,int b, int c)" << endl;
	}
	
};
int main() {
	
	P p( 2, 3);
	P s = { 2,3,4 }; //出错 “初始化”: 无法从“initializer list”转换为“P”		
	system("pause");
	return 0;

}

此时{}会形成一个initializer_list 不允许转换。

7.=default =delete

=default 只可以用于构造函数,拷贝构造,拷贝赋值,析构函数,移动构造,移动赋值构造上,不可以用于类的普通的函数上.
         
=delete  可以用于类的普通的函数上

如果我们定义了自己的构造函数,这时编译器就不会我们创建一个默认的构造函数,如果我们想要默认的构造函数就可以使用=default
如果我们不想要编译器为我们提供的默认拷贝构造,拷贝赋值,移动构造(&&) 移动赋值等,我们可以用=delete 来让编译器不在自己生成。

=default 用在普通函数身上 等于是个空函数。
=delete 用在普通函数身上会报错

一般c++的class分为带指针的和不带指针的,在不带指针的class我们可以使用编译器给的默认的这几个(big three/five)函数。如果有指针,我们就需要自己去设计我们的big three ,以为默认的copy赋值/构造是一个bit一个bit的copy,会导致浅拷贝。

8.Alias Template

9.using noexcept override final

using 几种用法

using namespace std;
using std::cout;
using INT = int;//别名

noexcept(不发出异常)

void foo() noexcept;
void foo() noexcept(true) //只有在里面的表达式为真时才不发出异常

如果异常不被处理,它就会往函数的调用者传递,如果一直没有被处理,就会一直往上传递,最终还没有被处理的话,就会被std::terminate()处理,这个函数里面是调用std::abort()。

在有&&(move) 的情况下,最好构造函数要有noexcept
在这里插入图片描述

override final

override主要时让编译器检查你对你要重写的函数是否有语法错误等
在这里插入图片描述
final 主要两种用法 1表示不能别继承 2不能被重写
在这里插入图片描述

10.decltype

可以解析出某个对象的类型

template<typename T1,typename T2>
decltype(x + y)add(T1 x, T2 y) {
	return x + y;
}

上面的代码会出错,需要将返回类型放在后面

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

11.Lambdas

是一种inline函数 通常作为一个参数或者local object
在这里插入图片描述
在这里插入图片描述

	int a = 10;
	auto l = [a](int x)mutable noexcept->decltype(x + a) {
		cout << ++a << endl;
		return a + x;
	};
	cout << l(2) << endl;
	cout << a << endl;
11
13
10

lambda没有默认构造函数和赋值构造函数。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值