突破编程_C++_面试(智能指针)

面试题 1 :什么是 RAII 编程方法

RAII(Resource Acquisition Is Initialization)是C++的发明者 Bjarne Stroustrup 提出的概念,也称为资源获取就是初始化,是一种管理资源、避免泄漏的编程方法。 它的基本思想是在对象的构造函数中获取资源,并在对象的析构函数中释放资源。通过这种方式,资源管理被封装在对象的生命周期中,从而简化了资源的获取和释放,避免了手动管理资源时可能出现的错误。
RAII 的核心思想是将资源的生命周期与对象的生命周期绑定在一起。当对象被创建时,它会自动获取所需的资源;当对象被销毁时,它会自动释放所拥有的资源。这种方式可以确保资源的正确获取和释放,避免了资源泄漏和内存泄漏等问题。
比如针对上面章节中在释放内存前有返回操作的场景,使用 RAII 编程方法可以作如下代码调整:

#include <iostream>

class SmartVal
{
public:
	SmartVal()
	{
		printf("automatically apply for memory\n");
		m_val = new int;
	}

	~SmartVal()
	{
		if (nullptr != m_val)
		{
			printf("automatically release for memory\n");
			delete m_val;
			m_val = nullptr;
		}
	}

public:
	int* getVal()
	{
		return m_val;
	}

private:
	int* m_val = nullptr;
};

void doSomething(int type)
{
	SmartVal val;
	if (0 != type)
	{
		return;			//OK:val 申请的内存会自动释放
	}
}

int main()
{
	doSomething(0);

	return 0;
}

上面代码的输出为:

automatically apply for memory
automatically release for memory

通过在构造函数自动申请内存,在析构函数中自动释放内存,从而避免了由于忘记正确释放内存导致内存泄漏。除了对于内存的自动管理,RAII 还可以应用于其他类型的资源管理,如文件句柄、网络连接等。通过将资源的获取和释放封装在相应的对象中,可以简化资源的管理,提高代码的可读性和可维护性。根据 RAII 编程方法, C++11 标准引入了能够自动管理动态内存的智能指针。

面试题 2 :如何解决智能指针的循环依赖问题

使用 weak_ptr 解决循环引用问题。循环引用是指两个或更多智能指针相互引用,形成一个闭环,导致它们的引用计数永远不会降到0,从而使得它们管理的内存无法得到释放。
使用 weak_ptr 可以打破这个循环,因为它不增加所指向对象的引用计数。当一个 shared_ptr 和一个 weak_ptr 相互引用时,只有当 shared_ptr 的引用计数变为 0 时,对象才会被销毁。而 weak_ptr 可以通过调用 lock() 方法来尝试获取一个临时的 shared_ptr,以安全地访问对象。如果对象已经被销毁,lock() 方法将返回一个空的 shared_ptr。如下为样例代码:

#include <iostream>  
#include <memory>  

using namespace std;

class A;
class B;

class A
{
public:
	A() {}
	~A() 
	{
		printf("destroy A\n");
	}

public:
	void setB(shared_ptr<B> b)
	{
		m_b = b;
	}

private:
	shared_ptr<B> m_b;
};

class B
{
public:
	B() {}
	~B()
	{
		printf("destroy B\n");
	}

public:
	void setA(shared_ptr<A> a)
	{
		m_a = a;
	}

private:
	weak_ptr<A> m_a;
};

int main() {

	shared_ptr<A> a(new A);
	shared_ptr<B> b(new B);

	a->setB(b);
	b->setA(a);

	return 0;
}

上面代码的输出为:

destroy A
destroy B

面试题 3 :unique_ptr 如何实现独占所有权

unique_ptr 在其生命周期内拥有它所指向的对象的唯一所有权,其他任何指针(包括其他 unique_ptr )都不能同时拥有该对象的所有权。当 unique_ptr 被销毁时(例如离开其作用域或被重新赋值),它所拥有的对象也会被自动删除。
unique_ptr 实现独占所有权的技术原理主要有以下 4 点:
(1)内部私有指针:unique_ptr 内部维护一个私有的原始指针,该指针指向它所拥有的对象。外部代码不能直接访问它。
(2)排他性:unique_ptr 不支持复制语义,从而可以保证了在任何时候都只有一个 unique_ptr 拥有指向对象的所有权。
(3)移动语义:unique_ptr 支持移动语义,允许将一个 unique_ptr 的所有权移动给另一个 unique_ptr ,但不能复制它。这是通过将 unique_ptr 的拷贝构造函数和拷贝赋值运算符设置为 delete (即禁用),同时提供移动构造函数和移动赋值运算符来实现的。
(4)资源释放:unique_ptr 通过RAII(资源获取即初始化)原则来实现在其生命周期结束时自动释放其拥有的资源(如动态分配的内存):在构造函数中获取资源,在析构函数中释放(析构函数中可以调用自定义删除器)。
样例代码如下:

unique_ptr<int> ptr1(new int); // 创建unique_ptr并初始化 
unique_ptr<int> ptr2 = ptr1; 	// 错误:不能复制 unique_ptr  
unique_ptr<int> ptr3 = move(ptr1); // 正确: 移动 unique_ptr 的所有权 

面试题 4 :shared_ptr 如何实现内部的引用计数机制

shared_ptr 的内部引用计数机制通常是通过内部使用一个控制块(control block)来实现的。当创建一个 shared_ptr 时,它会在堆上分配一个控制块,并将引用计数初始化为 1。然后,shared_ptr 内部的指针实际上是指向这个控制块的指针,而不是直接指向所管理的对象。控制块内部有一个指针指向实际的对象。
以下是 shared_ptr 实现引用计数的一些关键步骤:
(1)构造函数:当创建一个新的 shared_ptr 并指向某个对象时,如果这是第一个 shared_ptr 指向该对象,它会分配一个新的控制块,并将引用计数设置为 1。然后,shared_ptr 的内部指针指向这个控制块。如果已经有其他的 shared_ptr 指向该对象,它会增加现有控制块的引用计数。
(2)拷贝构造函数和拷贝赋值:当使用拷贝构造函数或拷贝赋值运算符创建一个新的 shared_ptr 时,它会增加现有控制块的引用计数,并使新的 shared_ptr 指向同一个控制块。
(3)移动构造函数和移动赋值:与 unique_ptr 类似,shared_ptr 也支持移动语义。当使用移动构造函数或移动赋值运算符时,它会将所有权从一个 shared_ptr 移动到另一个 shared_ptr,而不改变引用计数。
(4)析构函数:当 shared_ptr 的析构函数被调用时(例如,shared_ptr 离开其作用域),它会减少控制块的引用计数。如果引用计数变为 0,这表明没有其他 shared_ptr 再指向该对象,因此 shared_ptr 会删除控制块和所管理的对象,并释放内存。
(5)自定义删除器:shared_ptr 允许用户提供自定义的删除器,用于在删除对象时执行特定的操作。例如,可以使用自定义删除器来调用对象的自定义析构函数,或者执行其他清理任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值