此文档用于记录编写或者阅读C++代码中遇到的知识点。
- std::forwrad 与 std::move
在了解forward与move语义之前,需要了解c++11中增加的一个新类型右值引用(T &&)。左值表示表达式结束之后依然存在的持久对象,右值指表达式结束时就不存在的临时对象。
可以通过移动构造函数避免拷贝构造函数中的深拷贝,提高性能。
右值引用最大的应用在于消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率;
#include<iostream>
using namespace std;
class A{
public:
A():m_ptr(new int(0)){
cout << "construct function" <<endl;
}
~A(){
cout << "destruct function" << endl;
delete m_ptr;
}
// 对于含有堆内存的类,需要提供深拷贝的拷贝构造函数,避免默认的拷贝构造使用浅拷贝导致堆内存的重复删除。
A(const A& a):m_ptr(new int(*a.m_ptr)){
cout << "copy construct function" << endl;
}
// 通过移动构造,A&&作为函数参数,只使用浅拷贝避免临时对象的拷贝
A(A&& a): m_ptr(a.m_ptr){
a.m_ptr = nullptr;
cout << "move construct" << endl;
}
private:
int* m_ptr;
};
// get函数中存在通过一个临时变量拷贝构造了一个新的对象返回,临时变量在拷贝构造完成之后就销毁了,因此会带来额外的性能损耗。
A get(){
A a;
return a;
}
int main(){
// 默认的拷贝构造函数是浅拷贝,get中的临时变量和main中的a会对同一个new的对象进行两次析构,也就是堆内存进行了重复的删除。
A a = get();
}
std::move语义就是将左值转换为右值,将对象的状态或者所有权从一个对象转移到另一个对象,并且只是转移没有内存拷贝。
例如容器的拷贝,如果使用move语义可以避免对象的拷贝。
std::list<std::string> tokens;
std::list<std::string> t = std::move(tokens);
void ProcessValue(int& i) {
std::cout << "LValue processed: " << i << std::endl;
}
void ProcessValue(int&& i) {
std::cout << "RValue processed: " << i << std::endl;
}
int main() {
int a = 0;
ProcessValue(a);
// 通过move将左值转变成右值
ProcessValue(std::move(a));
}
右值引用还适用于这样的场景:需要将一组参数原封不动的传递给另一个函数
std::forward语义,如果一个右值引用做为函数的参数,在函数内部在转发该参数的时候就会又变成一个左值,c++11提供的forward就是为了解决这种问题,按照参数本身的类型而转发。
- decltype:用来字编译时推导出一个表达式的类型
例如:
int x = 0;
decltype(x) y = 1;
decltype(x+y) z = 2;
decltype多用于泛型编程当中
- using:类型别名
众所周知的C++可以使用typedef为类型重命名:
typedef unsigned int uint_t;
typedef的局限在于无法重定义一个模板,
例如有如下场景:
typedef std::map<std::string, int> map_int_t;
typedef std::map<std::string, std::string> map_string_t;
typedef就需要定义两次,如果使用using关键字就可以写为:
template<typename V>
using str_map_t = std::map<std::string, V>;