C++【内存管理】| 【智能指针】动态内存管理

文章目录

  • 一、简介
  • 二、四种智能指针
    • 1、auto_ptr/scored_ptr
    • 2、unique_ptr
      • 2.1 测试案例
      • 2.2 使用场景
    • 3、weak_ptr
      • 3.1 提供的成员函数
      • 3.2 应用场景
    • 4、shared_ptr
      • 4.1 常用成员函数
      • 4.2 引用计数
      • 4.3 share_ptr与new结合使用
      • 4.4 enable_shared_from_this
      • 4.6 make_shared的使用
      • 4.5 使用场景
    • 5、注意事项
      • 5.1 案例

========》智能指针的实现《========

一、简介

是一种抽象类型,由类模板来实现,借由类别的析构函数来达到自动释放指针所指向的存储器或对象;

  • 支持自动、异常安全的对象生命周期管理;

二、四种智能指针

1、auto_ptr/scored_ptr

该智能指针管理通过`new`创建auto_ptr获得的对象,并在其自身被`销毁时删除该对象`;

【注意】
> - 不可用多个指向`同一个对象`,当使用`拷贝构造`或`赋值运算符`时,右值会变为`nullptr`;
> - 不可用于容器中,若对象被赋值或拷贝,则会导致为空;
> - 在C++11被弃用,C++17被删除;

而scored_ptr的拷贝构造和赋值操作被delete,相比于auto_ptr更安全;

2、unique_ptr

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

通过指针拥有和管理对象,且对该对象具有唯一权,没有其他操作能删除该对象;

  • 该智能指针禁止拷贝赋值操作,但可通过std::move移动赋值;
  • 可通过reset进行更改;
  • 对于数组对象提供[]运算符;

2.1 测试案例

#include 
#include 
#include 

using namespace std;


class A{
public:
	A(int a) : m_a(a) {
		cout << "A..." << endl;
	}

	~A() {
		cout << "Bye..." << endl;
	}

	int getA() const {
		return m_a;
	}

private:
	int m_a;
};

创建

void test_unique_ptr() {

	// 方式1 
	std::unique_ptr<A> u_a(new A(1));
	// 方式2
	std::unique_ptr<A> u_b;
	u_b.reset(new A(2));
	// 方式3
	std::unique_ptr<A> u_c = std::make_unique<A>(3);
	cout << "u_a: " << u_a->getA() << endl;
	cout << "u_b: " << u_b->getA() << endl;
	cout << "u_c: " << u_c->getA() << endl;

	// std::move
	std::unique_ptr<A> u_d = std::move(u_c);
	cout << "u_d : " << u_d ->getA() << endl;	
}

C++【内存管理】| 【智能指针】动态内存管理_第1张图片
reset

void test_unique_ptr() {

	std::unique_ptr<A> u_b(new A(2));
	u_b.reset(new A(4));	// 会先释放内存

	cout << "u_b: " << u_b->getA() << endl;
}

在这里插入图片描述

2.2 使用场景

- 忘记delete;
- 代码异常安全;
- 传参和返回;

3、weak_ptr

template <class T> class weak_ptr;

弱管理对象的类型,提供了对其管理的资源的一个访问手段,为协助shared_ptr

  • 析构和构造不会影响引用计数
  • 可以解决两个shared_ptr互相引用造成的死锁问题(导致资源不会释放)
  • 该智能指针没有重载*以及->,故不能直接操作对象,避免引发错误(由于已被释放);

3.1 提供的成员函数

operator=

  • 不会获得所有权,且不会增加计数;
  • shared_ptr可以直接分配给weak_ptr
  • 当一个weak_ptr分配给shared_ptr需要使用lock来完成;
void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2;
	std::weak_ptr<A> wptr;

	// 将shared_ptr转换为weak_ptr
	wptr = ptr1;

	// 将weak_ptr转换为shared_ptr
	ptr2 = wptr.lock();

	cout << "ptr1:" << &(*ptr1) << endl;
	cout << "ptr2:" << &(*ptr2) << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第2张图片

swap

交换对象;

void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2(new A(2));
	std::weak_ptr<A> wptr1 = ptr1;
	std::weak_ptr<A> wptr2 = ptr2;

	wptr1.swap(wptr2);
	cout << "ptr1: " << &(*ptr1) << endl;
	cout << "ptr2: " << &(*ptr2) << endl;
	cout << "wptr1: " << &(*wptr1.lock()) << endl;
	cout << "wptr2: " << &(*wptr2.lock()) << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第3张图片

reset

重置变为空;

void test_weak_ptr(){
	std::shared_ptr<A> ptr1(new A(1));
	std::weak_ptr<A> wptr1 = ptr1;
	wptr1.reset();

	std::cout << "wptr1 " << (wptr1.expired() ? "" : "没有") << " 过期\n";
}

C++【内存管理】| 【智能指针】动态内存管理_第4张图片

use_count

共享所有权的shared_ptr对象的数量

  • weak_ptr不计算在内;

expired

  • 检查是否过期, weak_ptr对象是否为空;

lock

若未过期,返回一个shared_ptr,包含由weak_ptr对象保留的信息;

3.2 应用场景

  • 辅助shared_ptr的使用;
  • 弱回调;

4、shared_ptr

对象具有获得指针所有权共享该所有权的能力,持有的资源能在多个shared_ptr之间共享;

  • 当多一次对该对象的引用,则引用计数加一,若发生析构,则减一
  • 当引用计数为0时,则释放该资源;

4.1 常用成员函数

operator=

  • 复制:使该对象的引用计数加1;
  • 移动:则原对象将变为空;
void test_shared_ptr() {
	std::shared_ptr<A> ptr1(new A(1));
	std::shared_ptr<A> ptr2;

	// copy
	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;
	ptr2 = ptr1;

	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;

	// move
	ptr2 = std::make_shared<A>(2);
	cout << "ptr1:" << ptr1.use_count() << endl;
	cout << "ptr2:" << ptr2.use_count() << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第5张图片

swap

交换托管对象内容,不会破坏或更改引用计数;

void test_shared_ptr() {
    std::shared_ptr<A> ptr1(new A(1));
    std::shared_ptr<A> ptr2(new A(2));

    cout << "ptr1 count: " << ptr1.use_count() << " ptr1: " << ptr1 << endl;
    cout << "ptr2 count: " << ptr2.use_count() << " ptr2: " << ptr2 << endl;

    ptr1.swap(ptr2);
    cout << "ptr1 count: " << ptr1.use_count() << " ptr1: " << ptr1 << endl;
    cout << "ptr2 count: " << ptr2.use_count() << " ptr2: " << ptr2 << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第6张图片

reset

重置指针,若原先由托管,则会将原先的删除;

  • 不传入参数,被置为0;
  • 传入参数,会被置为1;
void test_shared_ptr() {
	std::shared_ptr<A> ptr1(new A(1));

	ptr1.reset();
	cout << "ptr1引用计数: " << ptr1.use_count() << endl;
	ptr1.reset(new A(2));
	cout << "ptr1引用计数: " << ptr1.use_count() << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第7张图片

get

获取指针;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << "ptr1:" << ptr1.use_count() << endl;

	if (a == ptr1.get()) {
		cout << "equal..." << endl;
	}
}

在这里插入图片描述

operator*

解引用;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << "ptr1:" << ptr1.use_count() << endl;
	
	if (a == ptr1.get()) {
		cout << "equal..." << endl;
	}

	if (&(*ptr1) == a){
		cout << "equal..." << endl;
	}
}

C++【内存管理】| 【智能指针】动态内存管理_第8张图片

operator->

取消引用对象成员,即直接调用对象成员;

void test_shared_ptr() {
	A *a = new A(2);
	std::shared_ptr<A> ptr1(a);

	cout << a->getA() << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第9张图片

use_count

获取引用计数;

unique

检查是否唯一;

operator bool

检查是否不为空;

operators ==, !=, <, <=, >, >=

重载了运算符;

operator<<

make_shared

制作shared_ptr;

4.2 引用计数

当计数器为0时,智能指针将会自动释放

何时计数器会递增

  • 当使用拷贝时shared_ptr p(q)p时q的拷贝,计数器会递增;
  • 当两个智能指针相互转换时,左边的指针对象会增加
  • 当它作为参数传递时;
  • 当它作为函数返回值时;

何时计数器会递减

  • 当两个智能指针相互转换时,右边的指针对象会递减
  • 当智能指针被销毁时;

使用new返回的指针来初始化智能指针;

  • 一般初始化智能指针的指针必须指向动态内存;
  • 由于之智能指针内部的构造函数是explicit,不能进行隐式转换,必须使用直接初始化
  • 由于不清楚对象何时销毁,则最好不使用内置指针来访问一个智能指针且不使用get()初始化或赋值;
shared_ptr<T> p(new T(1));

4.3 share_ptr与new结合使用

当new与share_ptr作为参数时

// 函数声明
int func();
void test(std::shared_ptr<int> p, int func);

由于智能指针内部的构造函数是explicit,故不能直接传入new创建的指针;

// 可考虑使用以下:
test(std::shared_ptr<int> (new int), func());

第一个参数被分为俩部分:std::shared_ptrnew int
由于编译器考虑高效的操作,故传入参数的执行顺序:

  • 执行new int
  • 调用func
  • 调用智能指针构造

但需要考虑到,万一func函数内部出现异常,则此时将会引起内存泄漏,由于new int返回的指针遗失;


故将实参一先单独出来:

std::shared_ptr<int> p(new int);
test(p, func());

4.4 enable_shared_from_this

在子类中继承该类,能够让子类的对象创建指向自身的shared_ptr实例;

  • 获取返回会增加引用计数;
class A : public enable_shared_from_this<A>{
public:
    A(int a) : m_a(a) {
        cout << "A..." << endl;
    }

    ~A() {
        cout << "Bye..." << endl;
    }

    shared_ptr<A> GetThis() {
        return shared_from_this();
    }

    int getA() const {
        return m_a;
    }

private:
    int m_a;
};


void test_shared_ptr() {
    std::shared_ptr<A> ptr(new A(1));

    ptr->GetThis();
    cout << ptr.use_count() << endl;
}

C++【内存管理】| 【智能指针】动态内存管理_第10张图片

注意事项

  • 不能将栈对象,来获取shared_from_this,会导致程序奔溃;
void test_shared_ptr() {
	A a(1);
	auto p = a.GetThis();
}

4.6 make_shared的使用

shared_ptr内部有两个指针,其中一个是我们new传入的指针,另一个是指向包含uses以及weaks的指针;
若通过构造函数去创建一个shared_ptr,则在shared_ptr的构造函数会在次调用new去初始化引用计数的指针,可能会导致第
二次new失败,出现错误;
- 当shared_ptr引用计数为0时,则可将第一块内存释放调用尽管weaks不为0;

而make_shared是直接将两个内存一起分配的不会出现问题;
	-防止资源泄漏的风险;
	-内存使用效率高;
	-但该方法无法自定义删除器;
	-导致托管的资源延迟释放,由于是同一块内存,当weaks不为0时,他是不会被释放的;

在这里插入图片描述

4.5 使用场景

  • shared_ptr通常使用在共享权不明的场景。有可能多个对象同时管理同一个内存时。
  • 对象的延迟销毁。陈硕在《Linux多线程服务器端编程》中提到,当一个对象的析构非常耗时,甚至影响到了关键线程的速度。可以使用BlockingQueue将对象转移到另外一个线程中释放,从而解放关键线程。

5、注意事项

  • 分清楚什么情况下使用哪一种指针:
    • 当该资源只用在当前且不共享的情况下,使用unique_ptr
    • 当智能指针不需要管理对象的生命周期下,使用weak_ptr
    • 定义的地方使用强智能指针引用对象的地址使用弱智能指针
    • 若要其他地方共享使用,使用shared_ptr
  • 注意使用时,该资源是否有效
  • 作为类成员的时,一般优先考虑向前声明,而不是作为头文件导入;

5.1 案例

class B;

class A{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb;
};
class B{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra;
};

int main() {
	shared_ptr<A> pa(new A());
	shared_ptr<B> pb(new B());
	
	// 此处互相引用,而类内使用weak_ptr阔以解决无法释放的问题;
	pa->_ptrb = pb;	
	pb->_ptra = pa;
}

你可能感兴趣的:(C++,内存管理,c++,智能指针,shared_Ptr)