C++高级篇学习笔记

文章目录


前言

本文记录C++一些面试难点问题剖析。


1. 左右值和右值引用的作用

  • 左值

可以在=左边,表达式结束后依然存在的持久对象,一般有名字,可以取地址。
 提示: 前置自加/自减 可以做左值;

左值引用:

例如:

        int a=0;

        int &b = a;  这是普通左值引用

        const int & c = 0; 这是常量左值引用,必须要有const,否则报错。

        所以常量左值引用可以接收常量右值,也就是字面值。

  • 右值

在=右边,不能取地址,没有名字,临时对象。

提示: 后置自加自减 做右值

但是,右值分两种: 

纯右值:字面值,后置自增自减,算术表达式,返回非引用的函数调用

将亡值:与右值引用相关的表达式,。例如将要被移动的对象

右值引用:

语法:  数据类型 && 变量名= 右值  。就是给右值取个名字,但是右值引用本身变成了左值。

作用:1. 接受右值形参   2. 实现移动语义    3. 实现完美转发

根据右值引用的作用来介绍其实际用法和含义:

1. 接受右值形参:

#include 
using namespace std;
class Test {
public:
    Test(){};
    ~Test(){};
}

void fun(Test &test){
    cout << "lvalue call" << endl;
}

void fun(Test &&test){
    cout << "rvalue call" << endl;
}

int main (){
    Test t1;
    fun(t1);
    fun(Test());
    return 0;
}

上述代码会输出不同的结果,这是右值引用的第一种用法,也是基本用法。

2. 移动语义:

C++ 11实现移动语义来减少拷贝: 

C++高级篇学习笔记_第1张图片

对右值使用移动语义可以减少拷贝,对左值可以使用std::move将左值转义为右值。

需要注意的是,移动语义只是在对象构造的时候生效。什么场景会有对象构造呢,就是创建对象的时候嘛,比如 Test t1(); Test t2(std::move(t1)),但是Test对象还必须写移动拷贝构造函数,并且上述场景会调用移动拷贝构造函数。再比如 vector vT; vT.push_back(std::move(t1)),也会调用移动拷贝构造函数。

3. 完美转发:

C++高级篇学习笔记_第2张图片

  •  万能引用: 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(参数),用于转发参数,如果参数是右值转发完是右值引用,如果是左值,则转发后是左值引用。

C++高级篇学习笔记_第3张图片

 C++11 forward完美转发_Barry__的博客-CSDN博客_c forward

 上述文章解释了完美转发的使用场景和更合理的解释。

2. 智能指针

  • unique_ptr

独占式拥有,保证同一时间只有一个智能指针指向该对象。

#include
#include
using namespace std;
int main()

{
    unique_ptr up_x(new int(10));
    //unique_ptr up_x2(up_x); //没有左值拷贝构造函数
    //unique_ptr up_x2=up_x;  //没有左值拷贝构造函数
    unique_ptr up_x2;
    //up_x2 = up_x;                //没有左值拷贝赋值运算符,因为已被delete
    unique_ptr up_x3(unique_ptr(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 p(q,d);d为自定义的析构函数对象。

注意事项:shared_ptr可以指向数组,但是需要自定义删除器

关于数组的使用和指派删除器:

        我们经常看到的例子都是单个对象,那数组是不是也可以像这样shared_ptr sp(new int[10]);使用shread_ptr?

        这样是错误的。我们要使用shared_ptr管理数组的话,必须给其制定一个删除器(函数):

shared_ptr<int> sp(new int[10], [](int *p) {delete[] p; });

         这里的匿名函数即是删除器。
        如果没有提供删除器,这段代码就是未定义的。默认情况下,shared_ptr使用delete销毁它所指的对象。如果这个对象是个动态数组,对其使用delete所产生的问题和释放一个动态数组忘记加[]的后果相同。

  • 构造函数是explict的,所以不存在从内置指针到智能指针的隐式类型转换。因此必须使用直接初始化(显式)。

C++高级篇学习笔记_第4张图片

 share_ptr循环引用导致的内存泄漏:

#include
#include
using namespace std;
namespace demo63  //演示智能指针循环引用的问题
{
	class P2;
	
	class P1
	{
	public:
		std::shared_ptr P2Ptr; //如果换成weak_ptr的就可以打破僵局
		P1() {
			cout << "hello P1" << endl;
		}
		~P1() {
			cout << "bye P1\n";
		}
	};

	class P2 {
	public:
		std::shared_ptr P1Ptr; //如果换成weak_ptr的就可以打破僵局
		P2() {
			cout << "hello P2\n";
		}
		~P2() {
			cout << "bye P2\n";
		}
	};
}
int main()
{
	using namespace demo63;
	//shared_ptr sp();
	//shared_ptr sp(new int[10], [](int *p) {delete[] p; });
	{
		shared_ptr p1p(new P1());
		cout << "p count :" << p1p.use_count() << endl;
		shared_ptr 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:

C++高级篇学习笔记_第5张图片


3. namespace

先占个坑位,研究全面再更新

4. 容器适配器

先占个坑位,研究全面再更新

5. 分配器allocator

总结

你可能感兴趣的:(C/C++,c++)