23.C++11(三)
一.右值不能直接取地址(前情回顾)
#include <iostream>
#include <string.h>using namespace std;void x(int&& x1)
{printf("%p\n",&x1);
}int main()
{x(1);x(100);// printf("%p\n",&1);// printf("%p\n",&x100);return 0;
}
传参之后,x1本身是左值,可以取地址,要加上move(x1)才能变成右值
本节比较重要,建议看完上面的在开始继续学习:
https://blog.csdn.net/weixin_60668256/article/details/153720945?fromshare=blogdetail&sharetype=blogdetail&sharerId=153720945&sharerefer=PC&sharesource=weixin_60668256&sharefrom=from_link
二.完美转发
1.万能引用
上次我们讲到,右值被引用传参之后,会变成左值
但是难道每一次模板都要写两个吗?(一个左值,一个右值)
包括const && ---> const && const & -----> const &
测试如下:
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<typename T>
void PerfectForward(T&& t)
{// 模版实例化是左值引用,保持属性直接传参给Fun// 模版实例化是右值引用,右值引用属性会退化成左值,转换成右值属性再传参给FunFun(forward<T>(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;
}
所以我们通过Fun函数进行识别
这里本质上就是编译器帮我们识别出来了四个版本的函数,如下:
void PerfectForward(int& t)
{Fun(t);
}void PerfectForward(int&& t)
{Fun(move(t));
}void PerfectForward(const int& t)
{Fun(t);
}void PerfectForward(const int&& t)
{Fun(move(t));
}
这时候,就有了完美转发:
这样就实现了我们上面那四个函数的功能了
2.完美转发forward()
#include <iostream>
#include <utility> // 包含std::forward// 被转发的目标函数:重载左值和右值版本
void target(int& x) { std::cout << "左值引用版本\n"; }
void target(int&& x) { std::cout << "右值引用版本\n"; }// 完美转发的模板函数
template <typename T>
void forwarder(T&& t) {target(std::forward<T>(t)); // 用std::forward还原原始值类别
}int main() {int a = 10;forwarder(a); // 传入左值,调用左值版本的targetforwarder(20); // 传入右值,调用右值版本的targetreturn 0;
}
3.move()和forward()的差别
4.forward()和万能引用
三.新的类功能
1.默认成员函数
原来是6个默认的成员函数,这里又多了两个(移动构造函数和移动赋值运算符重载)
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}private:string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}// 自动生成拷贝构造和移动构造~Person(){}
private:string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}
s1的资源给了s3,s1自己就没资源了
默认的移动构造是给自身没有资源需要管理,但是自定义类型有资源需要进行管理的类型进行的
移动赋值也是同理:
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}//Person(Person&& p) = default;//Person& operator=(Person && p) = default;//Person(const Person& p) = default;//Person& operator=(const Person& p) = default;// 自动生成拷贝构造和移动构造// ~Person()// {}
private:string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}//Person(Person&& p) = default;//Person& operator=(Person && p) = default;//Person(const Person& p) = default;//Person& operator=(const Person& p) = default;// 自动生成拷贝构造和移动构造~Person(){}
private:string _name;int _age;
};int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);Person s4;s4 = std::move(s2);return 0;
}
2.类成员变量初始化
3.强制生成默认函数的关键字default:
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}Person(Person&& p) = default;Person& operator=(Person && p) = default;Person(const Person& p) = default;Person& operator=(const Person& p) = default;// 自动生成拷贝构造和移动构造~Person(){}
private:string _name;int _age;
};
这里显示让Person进行实现移动构造和赋值,但是这里必须是要求要四个一起写
4.delete关键字
和上面的default类似:
如果我们有一个类不想让别人进行拷贝:
class Person
{
public:Person(const char* name = "111111111111", int age = 0):_name(name), _age(age){}//C++11Person(const Person& p) = delete;Person& operator=(const Person & p) = delete;// 只声明不实现,声明为私有// C++98
//private:
// Person(const Person& p);
// Person& operator=(const Person & p);Person(const Person& p) = delete;Person& operator=(const Person& p) = delete;
private:string _name;int _age;
};
5.final关键字
6.override关键字
7.explicit关键字
修饰函数
8.#pragma pack()
四.可变参数模板(我们目前不常用,较麻烦)
// 可变模版参数
// 参数类型可变
// 参数个数可变
// 打印参数包内容
template <class ...Args>
void ShowList(Args... args)
{// 可变参数模版编译时解析// 下面是运行获取和解析,所以不支持这样用cout << sizeof...(args) << endl;for (size_t i = 0; i < sizeof...(args); i++){cout << args[i] << " ";}cout << endl;
}int main()
{ShowList();ShowList(1);ShowList(1, "xxxxx");ShowList(1, "xxxxx", 2.2);return 0;
}
void Print()
{cout << endl;
}template <class T, class ...Args>
void Print(T&& x, Args&&... args)
{cout << x << " ";Print(args...);
}// 编译时递归推导解析参数
template <class ...Args>
void ShowList(Args&&... args)
{Print(args...);
}int main()
{ShowList();ShowList(1);ShowList(1, "xxxxx");ShowList(1, "xxxxx", 2.2);return 0;
}
底层实现类似于下面:
void Print()
{cout << endl;
}template <class T, class ...Args>
void Print(T&& x, Args&&... args)
{cout << x << " ";Print(args...);
}// 编译器推导的
void Print(double x)
{cout << x << " ";Print();
}void Print(const char* x, double z)
{cout << x << " ";Print(z);
}void Print(int x, const char* y, double z)
{cout << x << " ";Print(y, z);
}// 编译时递归推导解析参数
template <class ...Args>
void ShowList(Args&&... args)
{Print(args...);
}
// 编译器实例化生成
void ShowList(int x, const char* y, double z)
{Print(x, y, z);
}int main()
{ShowList(1, "xxxxx", 2.2);return 0;
}
还要其他的遍历方式:
template <class T>
int PrintArg(T t)
{cout << t << " ";return 0;
}template <class ...Args>
void ShowList(Args... args)
{int arr[] = { PrintArg(args)... };cout << endl;
}// 编译推演生成下面的函数
// void ShowList(int x, char y, std::string z)
// {
// int arr[] = { PrintArg(x),PrintArg(y),PrintArg(z) };
// cout << endl;
// }int main()
{//ShowList(1);//ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}
还可以这么写:
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (cout<<(args)<<" ", 0)...};cout << endl;
}void ShowList(int x, char y, std::string z)
{int arr[] = { (cout<<(x)<<" ", 0), (cout << (y) << " ", 0), (cout << (z) << " ", 0) };cout << endl;
}int main()
{ShowList(1, 'A', std::string("sort"));return 0;
}
auto推导数组(退化成 int* ):
#include <iostream>using namespace std;int main() {int arr[3] = {1, 2, 3};auto x = arr; // x 推导为 int*(数组名退化)// for(auto e:x)// {// cout << e << " ";// }// cout << endl;std::cout << typeid(x).name() << std::endl; // 输出:int*(因编译器而异)x[0] = 10; // 可通过指针访问数组元素x[1] = 11;std::cout << arr[0] << std::endl; // 输出 10std::cout << arr[1] << std::endl; // 输出 11return 0;
}
五.emplace_back的实现
1.emplace_back()的使用
2.emplace_back()的实现
emplace_back()是一次传递多个值吗?
template<class... Args>ListNode(Args&&... args):_next(nullptr), _prev(nullptr), _data(move(args...)){}
list内部,我们要实现一下这个函数
template <class... Args>void emplace_back(Args&&... args){insert(end(),args...);}template <class... Args>iterator insert(iterator pos,Args&&... args){Node* cur = pos._node;Node* newnode = new Node(args...);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}
但是这里还是不太对,我们看到第二个还是我们的深拷贝
这里就涉及到了我们的,传参会导致参数变成左值,这里就涉及到我们的完美转发了
template<class... Args>ListNode(Args&&... args):_next(nullptr), _prev(nullptr), _data(forward<Args>(args)...){}
template <class... Args>void emplace_back(Args&&... args){insert(end(),forward<Args>(args)...);}template <class... Args>iterator insert(iterator pos,Args&&... args){Node* cur = pos._node;Node* newnode = new Node(forward<Args>(args)...);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}
这样就对了