1.2、constexpr:
近似const, 可以修饰变量,也可以修饰函数
const int global = 100;
int main ()
{
int temp = 100;
constexpr int a = 1; //right
constexpr int b = global; //right
constexpr int c = temp; //wrong
}
既可以赋值字面常量也可以赋值const变量
重点:constexpr修饰的函数,生效于编译时而不是运行时, 重点应用于修饰函数使其在编译期大幅度被解释
被constexpr修饰的函数,无论是普通函数,还是类成员函数,必须是编译器可计算得到结果,即字面常量,不可是运行时才能获取的内容
constexpr int calc_in_compile_0 ()
{
return 100;
}
constexpr int calc_in_compile_1 (int a)
{
return a * 100;
}
constexpr int calc_in_compile_2 (int b, int c)
{
return c * calc_in_compile_1(b);
}
int main()
{
std::cout<< calc_in_compile_0() << std::endl;
constexpr int a = 1;
std::cout<< calc_in_compile_1(a) << std::endl;
std::cout<< calc_in_compile_2(a, calc_in_compile_1(a)) << std::endl;
return 0;
}
//100
//100
//10000
//代替了"const _max = INT_MAX"
static constexpr int max ()
{
return INT_MAX;
}
static constexpr int min ()
{
return INT_MIN;
}
constexpr int _max = max(), _min = min();
class Calc
{
double a_;
public:
/*构造函数在这里,必须用constexpr修饰,因为类成员函数是用constexpr修饰的*/
constexpr Calc(double a):a_(a) {}
constexpr double GetAbs() const
{
return std::abs(a_);
}
constexpr double GetSquare() const
{
return a_ * a_;
}
};
int main()
{
constexpr Calc calc(5.1);
constexpr double _fabs = calc.GetAbs();
///_fabs = 10.0;
std::cout << "fabs: " << _fabs <<std::endl;
double _abs = calc.GetAbs();
std::cout << "abs: " << _abs <<std::endl;
_abs = 10.0;
std::cout << "abs: " << _abs <<std::endl;
double _square = calc.GetSquare();
std::cout << "square: " << _square <<std::endl;
_square = 10.0;
std::cout << "square: " << _square <<std::endl;
}
1.3、using取代typedef:
typedef double db; //c99
using db = double; //c++11
typedef void(*function)(int, int);//c99,函数指针类型定义
using function = void(*)(int, int);//c++11,函数指针类型定义
using kvpairs = std::map<std::string, std::string>; //c++11
using CompareOperator = std::function<int(kvpairs &, kvpairs &)>; //c++11
template<class T> using twins = std::pair<T, T>; //更广泛的还可以用于模板
1.4、auto & decltype:
int main()
{
//auto让编译器通过初始值来推算变量的类型。当然,其定义的变量必须要有初始值
auto a = 1;
//decltype(变量)可以获取变量的类型
decltype(a) b = 2;
//注意下,decltype((a))的结果是引用,此时创建新的变量就将会报错,或者说:
//int &b = a;
//decltype(b) c;//也报错,因为b是a的引用,decltype(b)就会报错,效果同decltype((a))
}
1.5、字符串和数值类型的转换
以前的atoi、itoa等等成为历史
to_string:itoa成为历史
stoi、stol、stoul、stoll、stoull、stof、stod、stold:atoX成为历史
1.5、random_device
生成随机数,免去了以前需要自行调用srand初始化种子的步骤
#include<random>
int main()
{
std::random_device rd;
uint32_t randint = rd();
}
1.6、std::ref和std::cref
分别对应变量的引用和const引用,主要用于作为c++11函数式编程时传递的参数
1.7、std::chrono时间相关
1.8、原子变量
std::atomic<XXX>
用于多线程资源互斥操作,属c++11重大提升,多线程原子操作简单了许多
事实上基于c++11实现的无锁队列,让boost::lockfree无锁队列也将成为历史
1.9、正则表达式std::regex
恶心的C正则(regex.h)和boost正则成为历史
1.10、编译期断言static_assert
static_assert是用于涉及模板的assert,编译期就能发现不满足的情况,无需等到运行时
如下最后一个被注掉的static_assert如果放开,则无法通过编译。
template<class T>
class C
{
T data1_;
int data2_;
public:
C(T data1, int data2):data1_(data1), data2_(data2)
{
static_assert(sizeof(T) >= 4, "sizeof(T) is not larger than 4");
static_assert(data2_ >= 10, "condition must could be calced in compling!");
}
};
int main()
{
C<double> c(1.1, 1);
return 0;
}
2、容器
2.1、tuple 和 花括号初始化
元组的出现,和python拉齐了,c++也实现了函数可以多个返回值
2.2、hash正式进入stl
unordered_map、unordered_set、unordered_multimap、unordered_multiset。extstl扩展方式的使用hash成为历史。
2.3、emplace:
对于标准类型没有变化,如std:;vector<int>,push_back和emplace_back效果一样
2.4、shrink_to_fit
能减少不少无意义的内存空间占用
push、insert这类操作会触发容器的capacity,即预留内存的扩大,实际开发时往往这些扩大的区域并没有用途
int main()
{
std::vector<int> v{1, 2, 3, 4, 5};
v.push_back(1);
std::cout << "before shrink_to_fit: " << v.capacity() << std::endl;
v.shrink_to_fit();
std::cout << "after shrink_to_fit: " << v.capacity() << std::endl;
}
//before shrink_to_fit: 10
//after shrink_to_fit: 6
3、对于类
3.1、构造函数
1、default
2、delete关键字禁止拷贝构造、禁止赋值构造、禁止自定义参数的构造函数
注意析构函数不可由delete修饰
c++11以前的方式,是把需要禁止的构造函数,放在private里使外部无法调用;
3、委托构造函数
4、移动构造函数:
属于c++11的右值引用的衍生效果之一,首先描述右值引用std::move
std::move主要解决拷贝性能问题
类似于python的深拷贝和浅拷贝, python中的对象赋值和copy.copy都是浅拷贝, 赋值的都是对象的引用, copy.deepcopy则是深拷贝
直接定位到实际应用上(程序中尽量不要出现"int &&a = 1"这样的东西,炫技容易搞出错误)
用途为:
1、减少内存复制成本
2、将不再需要的变量,取消它对原先持有变量(内存)的持有(修改)权限
移动构造函数,最大的用途避免同一份内存数据的不必要的变成两份甚至多份、过程中的变量传递导致的内存复制,另外解除了栈变量对内存的引用;
例三:c++11风格的新老容器的数据移交:
如果一个老容器如vector容器oldv,需要将其内部数据复制给新容器如vector容器newv,且老容器后面无用,数据量很大;
那么c++11的std::make_move_iterator将派上用场,它可以将一个普通迭代器,如oldv.begin(),转化为"move式迭代器",配合std::copy,将老容器内全部数据的引用,move给新容器同时取消老容器对数据的持有权。这就是c++11风格的高速数据拷贝方式。
int main()
{
std::vector<std::string> oldv = {"123", "456", "789"};
std::vector<std::string> newv(oldv.size());
for (auto &i: oldv)
{
std::cout << i << "\t";
}
std::cout << std::endl;
//c++11做法,move引用
std::copy(std::make_move_iterator(oldv.begin()), std::make_move_iterator(oldv.end()), newv.begin());
//std::copy(oldv.begin(), oldv.end(), newv.begin()); //传统做法,复制
for (auto &i: oldv)
{
std::cout << i << "\t";
}
std::cout << std::endl;
for (auto &i: newv)
{
std::cout << i << "\t";
}
std::cout << std::endl;
}
//第一次打印:老容器正常打印
//第二次打印:老容器无法打印了
//第三次打印:新容器正常打印
//123 456 789
//123 456 789
5、继承构造函数
近似于委托构造函数原理,如下:
struct A
{
int a;
A(int _a):a(_a + 100){}
};
struct B : public A
{
int b;
B(int _b):A(_b), b(_b + 10000){}
};
3.2、override和final
作用于虚函数,更多的作用是:显式的标识是否应该多态继承或不应该
1、override:子类用override修饰其虚函数,表示要多态继承基类的虚函数。不可以修饰非虚函数。写在函数后面
virtual bool FullMergeV2() const override {}
2、final:基类用final修饰其虚函数,其子类不可以多态继承该虚函数
class father
{
public:
int a_;
int GetA() {return a_;}
virtual void SetA(int a) final {a_ = a;}
public:
father(int a):a_(a) {}
};
class Son: public father
{
int b_;
public:
Son(int a, int b):father(a), b_(b) {}
//virtual void SetA(int a) override {a_ = a;}
};
如father基类的SetA实现为"virtual void SetA(int a) final {a_ = a;}",则子类Son再多态继承实现SetA方法就会报错了。
3.3、建议:
构造与析构:全部的复制构造、赋值构造、移动构造、自定义构造函数,以及全部的赋值运算符、移动运算符,尽可能自行全部都实现
继承:子类的虚函数多态实现要加override显式的表明,不让子类多态实现的虚函数也要记得加入final;
宗旨:让c++11的编译器更多的帮助发现问题
4、lambda、bind、function:
函数式编程是c++11重要亮点之一
5、智能指针
reset自动析构再重新构造,get判断是否有效、支持放在容器内;
6、多线程与互斥同步(互斥锁,条件变量)
这也是c++11的一个重要亮点