c++2.0 新特性(c++11/c++14)
语言新特性
1、variadic template 变参模板
template <typename T, typename... Types> // ...为模板参数包
// sizeof...(args)获取参数个数
void printX(const T& firstArg, const Types&... args) { // ...函数参数类型包
cout << firstArg << endl;
printX(args...); // ...函数参数包
}
void printX() {
}
// 使用方法
printX(7,5, "hello", bitset<16>(377), 42);
2、nullptr
解决重载问题
void f(int);
void f(void*);
// 使用
f(0); // f(int);
f(NULL); // 无法确定调用哪个
f(nullptr); // f(void*)
3、auto与deltype
auto:
-
根据初始值推断类型,所以必须初始化
-
忽略顶层const、忽略引用
-
实际执行语句or表达式
deltype:
-
根据deltype()里面的表达式推断、不执行语句、无需初始化
-
保留所有的属性如const、引用
-
deltype(())为引用类型
-
函数返回值
// 模板
template <typename T1, typename T2>
deltype(x+y) add(T1 x, T2 y);
// lambda
[...](...) mutable throwSepc -> retType {}
-
变量声明、特别是模板元编程
-
传递lambda变大之的类型
auto cmp = [](const Person& p1, const Person& p2) {...};
std::set<Person, deltype(cmp)> coll(cmp);
4、初始化列表
vector<int> v{1,2,3};
vector<string> v2{"hello", world}
// 原理为:编译器看到{t1,t2...tn}会做出一个initializer_list<T>,并关联到array<T, b>
// 调用函数f(函数f一般为构造函数)时将array内的元素逐一传给函数
// 但是如果函数f接受的参数为initializer_list<T>,则直接传入无需一一传递
// initializer_list<T>
// 初始化操作
int i; // i没有初值
int j{}; // j=0
int* p; // p没有初值
int* q{}; // q=nullptr;
// 强类型检查
int x = {5.3} // error、gcc为warning
int x = (5.3) // x = 5;
// 自己使用
class P {
public:
P(int a, int b) {
cout << a << " " << b <<endl;
}
// initializer_list可以认为是容器
p(initializer_list<int> initlist) {
for (auto i : initlist) {
cout << i << " ";
}
cout << endl;
}
}
// 底层原理
template <typename E>
class initializer_list {
public:
typedef E value_type;
typedef const E& reference;
typedef const E& const_reference;
typedef size_t size_type;
typedef const E* iterator;
typedef const E* const_iterator;
private:
iterator _M_array; // 数组头部
size_type _M_len; // 长度
// 编译器可以调用私有的构造函数
constexpr initializer_list(const_iterator a, int size):
_M_array(a), _M_len(size) {};
public:
constexpr initializer_list() noexcept
: M_array(0), _M_len(0) {}
}
constexpr:
https://blog.csdn.net/weixin_40087851/article/details/82754189
5、explicit
主要用来构造函数、去除掉隐形构造
class P {
public:
explicit P(int a, int b) {
cout << a << " " << b <<endl;
}
// initializer_list可以认为是容器
p(initializer_list<int> initlist) {
for (auto i : initlist) {
cout << i << " ";
}
cout << endl;
}
}
P p1 = {77, 5}; // 调用的是P(a, b),而不是初始化列表,由于执行explicit,因此报错
6、for循环
编译器进行替换
for (auto& e : vc) {
//
do_something();
}
7、=default、=delete
=default:让编译器生成Big-Five
=delete:不允许使用某个函数
8、alias template
template <typename T>
using Vec = std::vector<T, Alloc<T>>; // 不能对alias template做偏特化或者全特化
//使用
Vec<int> coll --> vector<int, Alloc<int>> coll;
// 可否使用define or typedef
// 难道只是为了少打几个字?
// 自然不是,可以实现之前无法实现的函数接口
// 详情看下节
9、template template parameter
template <typename T,
template<class> class Container>
class XCIs {
private:
Container<T> c;
public:
XCIs() {
for (long i = 0; i < SIZE; ++i)
c.insert(c.end(), T);
Container<T> c1(c);
Container<T> c2(std::move(c));
}
}
// 使用
XCIs<MyString, vector> c1;
// 会报错,为何?
// 模板模板参数不能使用默认值
// 解决方案如下:
template<typename T>
using Vec = vector<T, alloc<T>>;
XCIs<MyString, Vec> c1;
10、type alias(similar to typedef)
// typedef void(*func)(int, int)
using func = void(*)(int, int);
11、noexcept
1、当使用noexcept时,std::teminate()函数会被立即调用,而不是调用std::unexpected(); 因此,在异常处理的过程中,编译器不会回退栈,这为编译器的优化提供了更大的空间
// 用法:声明函数不抛出异常
// 第一种
void foo() noexcept; -> void foo() noexcept(true);
// 第二种 带条件
void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y))) {
x.swap(y);
}
2、非常重要:
-
移动构造函数必须用noexcept声明、特别是vector
-
只有这样,vector和deque在扩容过程中,才会使用移动构造函数,否则调用拷贝构造函数
12、override
虚函数重写,记得与重载overload区分开
13、final
不允许被重写
// 1、函数中使用
struct Base {
virtual void f() final;
}
struct Devide {
void f() {
// 会报错
}
}
// 类使用
struct Base final {};
struct Devide : public Base {}; // 会报错
标准库新特性
1、lambda表达式
-
可以用来当做参数或者object,匿名的function object
-
没有默认构造函数、也没用赋值操作符
格式为:
[...](...) mutable throwSpec -> retType {}
// 赋值
[=]
// 引用
[&]
// 使用1
int id = 0;
auto f = [id]() mutable {
cout << id << endl;
++id;
}
id = 42;
f(); // PS: 0
f(); // PS: 1
f(); // PS: 2
// 使用2
int id = 0;
auto f = [&id]() {
cout << id << endl;
++id;
}
id = 42;
f(); // PS: 42
f(); // PS: 43
f(); // PS: 44
// 使用3
int id = 0;
auto f = [id]() { // [error] increment of read-only variable 'id'
cout << id << endl;
++id;
}
id = 42;
f(); //
f(); //
f(); //
2、variadic templates
templates分为
-
function templates
-
class templates
变化是指两种
-
参数个数:variadic num、
-
参数类型
case1
void printX() {
}
template<typename T, typename...Types>
void printX(const T& firstArg, const Types&... arg) {
cout << firstArg <<endl;
printX(args...);
}
// 使用
printX(7.5, "hello");
case2
// 使用
cout << make_tuple(7.5, "hello") -> 预期输出[7.5, hello]
// 实现
template <typename... Args>
ostream& operator<<(ostream& os, const tuple<Args...>& t) {
os <<"[";
print_tuple<0, sizeof...(Args), Args...>::print(os, t);
os <<"]";
}
// 泛化递归
template<int IDX, int MAX, typename...Args>
struct print_tuple {
static void print(ostream& os, const tuple<Args...>& t) {
os << get<IDX>(t) <<(IDX+1==MAX?"":",");
print_tuple<IDX+1, MAX, Args...>::print(os, t);
}
}
// 偏特化
template<int MAX, typename...Args>
struct print_tuple<MAX, MAX, Args...> {
static void print(ostream& os, const tuple<Args...>& t) {}
}
case3 - 递归继承(tuple)
case4-递归符合
template<typename Head, typename...Tail>
class tuple<Head, Tail...> {
typedef tuple<Tail...> composited;
//
composited m_tail;
Head m_head;
tuple() {}
tuple(Head v, Tail... vtail) : m_tail(vtail), m_head(v) {}
//
Head head() {return m_head;}
composited& tail() {return m_tail;}
}
3、右值引用
lvalue:可以出现在operator=左侧
rvalue:只能出现在operator=右侧,临时对象,表达式计算值,函数返回值
lvalue reference: 左值引用,&
rvalue referenc:右值引用,只能绑定右值,&&
const reference:常引用,可以绑定右值,const &
3.1、完美转发
一个接受右值的函数、再调用其他函数的时候,传递的右值会变为左值,这是不完美的转发
void process(int &&);
void process(int &);
void forward(int && x) {
process(x);
}
// 使用
forward(2); // process调用的是左值版本,这就是不完美的参数转发
std::forward 完美转发函数
-
自己实现类,若包含移动语义,存在指针,移动之后,来源端的把指针设置为null
MyString opertaor=(MyString&& str) noexcept {
if (this != &str) {
if (_data) delete _data;
_len = str._len;
_data = str._data;
str._len = 0;
str._data = nullptr; // 非常重要,不然str析构的时候出问题
}
}
-
自己实现类,若包含移动语义,存在指针,析构函数必须进行判断
~MyString() {
if (_data) delete _data; // 和第一点配合,完成移动语义
} -