再探 智能指针

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再探 智能指针相关的知识,希望对你有一定的参考价值。

使用任何一项工具,都需要承担代价与风险的。

1、一个裸的指针不要用两个shared_ptr管理,unique_ptr也是,它们都会认为自己独占这个资源,你等释放的时候试试看。

2、用weak_ptr打破循环引用。

3、当需要在类的内部接口中,如果需要将this作为智能指针来使用的话,需要用该类派生自enable_shared_from_this。
enable_shared_from_this和shared_from_this在构造和析构中是不能使用的,在某些情况下也不能使用,因为构造的时候类还未生成好,析构的时候类快完蛋了都没有意义。

#include <cassert>
#include <memory>
#include <iostream>

class Parent;

typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr;

class Child : public std::enable_shared_from_this<Child> {
public:
	WeakParentPtr father;
	~Child();
	Child();
	void checkRelation();
};

typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr;

class Parent : public std::enable_shared_from_this<Parent> {
public:
	WeakChildPtr son;
	~Parent();
	Parent();
	void checkRelation();
};

void handleChildAndParentRef(const Parent& p, const Child& c) {
	auto cp = c.father.lock();
	auto pc = p.son.lock();
	if (cp.get() == &p && pc.get() == &c) {
		std::cout << "right relation\\n";
	}
	else {
		std::cout << "oop!!!!!\\n";
	}
}

void handleChildAndParent(const ParentPtr& p, const ChildPtr& c) {
	auto cp = c->father.lock();
	auto pc = p->son.lock();
	if (cp == p && pc == c) {
		std::cout << "right relation\\n";
	}
	else {
		std::cout << "oop!!!!!\\n";
	}
}

Child::Child() { std::cout << "hello child\\n"; }
Parent::Parent() { std::cout << "hello parent\\n"; }
Child::~Child() { std::cout << "bye child\\n"; }
Parent::~Parent() { std::cout << "bye parent\\n"; }

void Parent::checkRelation() {
	auto ps = son.lock();
	if (ps) {
		// this
		handleChildAndParent(shared_from_this(), ps);
	}
	std::cout << "after call checkRelation\\n";
}

void Child::checkRelation() {
	// we call handleChildAndParent
}

void testParentAndChild() {
	Parent pp;
	ParentPtr p(new Parent());
	ChildPtr c(new Child());
	p->son = c; // c.use_count() == 2 and p.use_count() == 1
	c->father = p; // c.use_count() == 2 p.use_count() == 2
	p->checkRelation();
}

int main() {
	testParentAndChild();
}

4、shared_ptr,weak_ptr和裸指针相比会大很多,并且效率上会有影响,尤其是在多线程模式下。

关于这点自己实现一个智能指针就知道了。

一个shared_ptr在空间上至少是三个裸指针的大小(24个字节),本身有引用还要配合weak_ptr使用所以要保存多少该指针的引用。如下生成既有对象的new还有本身的new:

ObjectPtr obj3(new Object(2));

ObjectPtr obj4 = obj3进行拷贝时时间效率会慢很多
可以用以下方式减少空间

ObjectPtr obj5 = std::make_shared<Object>(3);
//ObjectPtr obj5(new Object(3));

5、如果有可能,优先使用类的实例,其次万不得已使用std::unique_ptr,再万不得已使用std:shared_ptr。

用unique_ptr和用shared_ptr一样为了防止处理某些异常时无法调用delete释放资源的情况,在同一个特定的时刻只会有一个unique_ptr来管理一份资源没有共享模式,所以拷贝构造函数,=符号拷贝操作等是不存在的。

由于unique_ptr的唯一性所以要转移资源可以transfer传值,只能调用obj的右值引用而不能用左值,transfer函数执行完就释放了,调用了transfer后原有的会变成空指针不再管理。

#include <cassert>
#include <memory>
#include <iostream>

typedef int Object;

typedef std::unique_ptr<Object> UniqueObjectPtr;
typedef std::shared_ptr<Object> SharedObjectPtr;

void print(const UniqueObjectPtr& obj) {}

void transfer(UniqueObjectPtr obj) {
	std::cout << obj << std::endl;
}

void uniquePtr() {
	UniqueObjectPtr obj(new Object(1));
	auto p = obj.get(); // operator bool
	if (p) {
		std::cout << p << std::endl;
	}
	// better
	if (obj) {
		std::cout << obj << std::endl;
	}

	// operator -> *
	std::cout << p << obj << std::endl;

	p = obj.release();
	delete p;

	obj.reset();
	obj.reset(new Object(2)); // obj.reset();
	// UniqueObjectPtr(const UniqueObjectPtr&) = delete
	// UniqueObjectPtr(UniqueObjectPtr&&) = default
	transfer(std::move(obj));

	assert(obj == nullptr);
	//std::cout << obj->id() << std::endl;

	obj.reset(new Object(4));
	SharedObjectPtr sharedObj(std::move(obj));
	assert(obj == nullptr);
	// shared_ptr weak_ptr enable_shared_from_this
	// unique_ptr
}

6、智能指针只能表示所有权,如果遇到某些复杂的数据结构,或者所有权不明确的场景,还是得裸指针来。
也很好举例,用智能指针写个二叉树试试。

好了就说到这儿吧,再说多了怕是以后都不敢用了。
今天之所以要写这么一篇,是最近看智能指针的好处被吹的天花乱坠的让我都有点想把以前裸指针的代码全改成智能指针了。
但是呢,不要忘了我们开头的那句话哦。

以上是关于再探 智能指针的主要内容,如果未能解决你的问题,请参考以下文章

指针辨析:悬垂指针哑指针野指针智能指针

再探pytorch的Dataset和DataLoader

如何设置 vscode 的代码片段,以便在自动完成后自动触发 vscode 的智能感知?

更新:C++ 指针片段

片段中的 EditText 上的空指针异常 [重复]

PHP代码审计之再探 TP3 漏洞-上