开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第N天,点击查看活动详情 . 完美转发 ♥️ 1.1 模版中&&万能引用 void Fun(int& x) { cout << "左值引用" << endl; } void Fun(const int& x) { cout << "const 左值引用" << endl; } void Fun(int&& x) { cout << "右值引用" << endl; } void Fun(const int&& x) { cout << "const 右值引用" << endl; }
template void PerfectForward(T&& t) { Fun(t); } int main() { PerfectForward(10);// 右值 int a; PerfectForward(a); // 左值 PerfectForward(std::move(a)); // 右值 const int b = 8; PerfectForward(b);// const 左值 PerfectForward(std::move(b)); // const 右值 return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值 模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力 但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值 ♥️ 1.2 std::forward 完美转发在传参的过程中保留对象原生类型属性 void Fun(int& x) { cout << "左值引用" << endl; } void Fun(const int& x) { cout << "const 左值引用" << endl; } void Fun(int&& x) { cout << "右值引用" << endl; } void Fun(const int&& x) { cout << "const 右值引用" << endl; }
template void PerfectForward(T&& t) { Fun(std::forward(t)); } int main() { PerfectForward(10);// 右值 int a; PerfectForward(a);// 左值 PerfectForward(std::move(a)); // 右值 const int b = 8; PerfectForward(b);// const 左值 PerfectForward(std::move(b)); // const 右值 return 0;
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
std::forward(t)在传参的过程中保持了t的原生类型属性。 ♥️ 1.3 完美转发实际中的使用场景 template struct ListNode { ListNode* _next = nullptr; ListNode* _prev = nullptr; T _data; }; template class List { typedef ListNode Node; public: List() { _head = new Node; _head->_next = _head; _head->_prev = _head; } void PushBack(T&& x) { //Insert(_head, x); Insert(_head, std::forward(x)); } void PushFront(T&& x) { //Insert(_head->_next, x); Insert(_head->_next, std::forward(x)); } void Insert(Node* pos, T&& x) { Node* prev = pos->_prev; Node* newnode = new Node; newnode->_data = std::forward(x); // 关键位置 // prev newnode pos prev->_next = newnode; newnode->_prev = prev; newnode->_next = pos; pos->_prev = newnode; } void Insert(Node* pos, const T& x) { Node* prev = pos->_prev; Node* newnode = new Node; newnode->_data = x; // 关键位置 // prev newnode pos prev->_next = newnode; newnode->_prev = prev; newnode->_next = pos; pos->_prev = newnode; } private: Node* _head; }; int main() { Listlc::string lt; lt.PushBack("1111"); lt.PushFront("2222"); return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 ♠️ 2. 新的类功能 默认成员函数 原来C++类中,有6个默认成员函数:
构造函数 析构函数 拷贝构造函数 拷贝赋值重载 取地址重载 const 取地址重载 最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类 型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内 置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造 完全类似) 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
♥️ 2.1 类成员变量初始化 C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化.
static 不能给缺省值,必须去类外面定义初始化 const static 可以给值初始化(在类内)。并且不是缺省值。 ♥️ 2.2 强制生成默认函数的关键字default: C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
class Person { public: Person(const char* name = "", int age = 0) :_name(name) , _age(age) {} Person(const Person& p) :_name(p._name) , _age(p._age) {} Person(Person&& p) = default; private: lc::string _name; int _age; }; int main() { Person s1; Person s2 = s1; Person s3 = std::move(s1); return 0; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ♥️ 2.3 禁止生成默认函数的关键字delete: 如果能想要限制某些默认函数的生成