C++ 2.0 //C++ 11
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没有默认构造函数和赋值构造函数。