文章目录
前言
本文记录C++一些面试难点问题剖析。
1. 左右值和右值引用的作用
- 左值:
可以在=左边,表达式结束后依然存在的持久对象,一般有名字,可以取地址。
提示: 前置自加/自减 可以做左值;
- 右值
在=右边,不能取地址,没有名字,临时对象。
提示: 后置自加自减 做右值
但是,右值分两种:
纯右值:字面值,后置自增自减,算术表达式,返回非引用的函数调用
将亡值:与右值引用相关的表达式,。例如将要被移动的对象
右值引用:
语法: 数据类型 && 变量名= 右值 。就是给右值取个名字,但是右值引用本身变成了左值。
作用:1. 实现移动语义 2. 实现完美转发
左值引用:
例如:
int a=0;
int &b = a; 这是普通左值引用
const int & c = 0; 这是常量左值引用,必须要有const,否则报错。
所以常量左值引用可以接收常量右值,也就是字面值。
移动语义:
C++ 11实现移动语义来减少拷贝:
对右值使用移动语义可以减少拷贝,对左值可以使用std::move将左值转义为右值。
- 万能引用: T && ,以此为形参,可以接收左值和右值。
提示: T&&必须这么写,也就是只能用在模板中。如果写成int &&做形参,那只能接收右值。
对右值引用的两个规则中的第一个也同样影响了旧式的左值引用。回想在pre-11 C++时,对一个引用取引用是是不被允许的,比如A& &会导致一个编译器错误。相比之下,在C++11中,引入了如下所示的引用折叠规则(reference collapsing rules):
1. A& &变成A&
2. A& &&变成A&
3. A&& &变成A&
4. A&& &&变成A&&
- 模板函数:std::forward<T>(参数),用于转发参数,如果参数是右值转发完是右值引用,如果是左值,则转发后是左值引用。
C++11 forward完美转发_Barry__的博客-CSDN博客_c forward
上述文章解释了完美转发的使用场景和更合理的解释。
2. 智能指针
- unique_ptr
独占式拥有,保证同一时间只有一个智能指针指向该对象。
#include<iostream>
#include<memory>
using namespace std;
int main()
{
unique_ptr<int> up_x(new int(10));
//unique_ptr<int> up_x2(up_x); //没有左值拷贝构造函数
//unique_ptr<int> up_x2=up_x; //没有左值拷贝构造函数
unique_ptr<int> up_x2;
//up_x2 = up_x; //没有左值拷贝赋值运算符,因为已被delete
unique_ptr<int> up_x3(unique_ptr<int>(new int(9))); //有右值拷贝构造
up_x2 = std::move(up_x);//可以借助move将up_x赋值给up_x2.此操作完成后up-x不可用,有右值拷贝复制
*up_x2 = 99;//
//*up_x = 22;
return 0;
}
shared_ptr
多个智能指针共享指向一个对象,所指对象在最后一个引用被销毁时释放,
可以使用make_shared函数通过构造函数传入普通指针,get函数获得普通指针。
- 默认情况下,初始化智能指针的普通指针必须指向一个动态分配的内存,因为智能指针默认调用delete释放内存。但也可以将智能指针绑定到一个类类型,但是必须正确定义它的析构函数。例如shared_ptr<T> p(q,d);d为自定义的析构函数对象。
注意事项:shared_ptr可以指向数组,但是需要自定义删除器
关于数组的使用和指派删除器:
我们经常看到的例子都是单个对象,那数组是不是也可以像这样shared_ptr<int> sp(new int[10]);使用shread_ptr?
这样是错误的。我们要使用shared_ptr管理数组的话,必须给其制定一个删除器(函数):
shared_ptr<int> sp(new int[10], [](int *p) {delete[] p; });
这里的匿名函数即是删除器。
如果没有提供删除器,这段代码就是未定义的。默认情况下,shared_ptr使用delete销毁它所指的对象。如果这个对象是个动态数组,对其使用delete所产生的问题和释放一个动态数组忘记加[]的后果相同。
- 构造函数是explict的,所以不存在从内置指针到智能指针的隐式类型转换。因此必须使用直接初始化(显式)。
share_ptr循环引用导致的内存泄漏:
#include<iostream>
#include<memory>
using namespace std;
namespace demo63 //演示智能指针循环引用的问题
{
class P2;
class P1
{
public:
std::shared_ptr<P2> P2Ptr; //如果换成weak_ptr的就可以打破僵局
P1() {
cout << "hello P1" << endl;
}
~P1() {
cout << "bye P1\n";
}
};
class P2 {
public:
std::shared_ptr<P1> P1Ptr; //如果换成weak_ptr的就可以打破僵局
P2() {
cout << "hello P2\n";
}
~P2() {
cout << "bye P2\n";
}
};
}
int main()
{
using namespace demo63;
//shared_ptr<int> sp();
//shared_ptr<int> sp(new int[10], [](int *p) {delete[] p; });
{
shared_ptr<P1> p1p(new P1());
cout << "p count :" << p1p.use_count() << endl;
shared_ptr<P2> p2p(new P2());
cout << "c count :" << p2p.use_count() << endl;
p1p->P2Ptr = p2p;
cout << "c count :" << p2p.use_count() << endl;
p2p->P1Ptr = p1p;
cout << "p count :" << p1p.use_count() << endl;
}
//退出循环后,p1p和p2p释放,他们只是指针而已,所以会调用一次智能指针的析构函数
//当p1p要析构的时候,发现本身还被p2p->P1Ptr指着,所以对p2p()的引用只是减一,p1p就析构了,不存在了
//当p2p要析构的时候,发现本身被先前p1p的P2Ptr指针引用这,所以,只是引用减一,然后p2p被销毁。
//而此时,对象P2和P1都没有被释放,内部的P1Ptr指针和P2Ptr指针还在相互指向着对方。导致内存泄露
return 0;
}
PS D:\MyCode> cd "d:\MyCode\" ; if ($?) { g++ main.cpp -o main } ; if ($?) { .\main }
hello P1
p count :1
hello P2
c count :1
c count :2
p count :2
weak_ptr:
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。