通过对比理解C++智能指针

理论

  • 概述

    • 智能指针:把管理资源的责任交给了对象,这样我们在使用结束时不需要显式地释放资源,而是由析构函数自动完成这一工作

    • 它是一个类模板,可以创建任意类型的指针对象

    • 智能指针使用时,资源对象不能被重复释放,否则会导致未定义的行为

  • C++中的智能指针关系

    智能指针简述
    auto_ptr存在管理权转移问题,不适合共享资源,已被C++11弃用
    unique_ptr暴力处理auto_ptr的管理权转移问题,独占资源所有权,不允许拷贝和赋值操作(开销小)
    shared_ptr使用引用计数优雅解决auto_ptr的问题,但带来了循环引用bug(开销大)
    weak_ptr专注给shared_ptr打补丁,不增加引用计数
  • auto_ptr

    • 不足

      管理权转移,原对象拷贝给新对象时,原对象被置为nullptr,此时只有新对象指向这块资源空间

      此时如果再使用原来的对象,那么会出现未定义的行为(资源不能被重复释放)

  • unique_ptr

    • 原理

      针对auto_ptr在拷贝、赋值操作中的管理权转移问题,unique_ptr直接禁用了拷贝和赋值操作

      这样自然就避免了重复释放资源

    • 不足

      不能进行拷贝和赋值操作

      可以使用move把左值转成右值进行赋值操作,此时效果和auto_ptr一样

      move是一种将对象转变为右值引用的方法:表示将放弃当前对象的所有权,将所有权转移到另一个对象,由新对象接管所有权

  • shared_ptr

    • 原理

      为了进行拷贝和赋值操作,shared_ptr采用了引用计数原理,记录有多少shared_ptr实例指向同一资源

      有几个实例对象计数值就是几,调用析构函数会将计数值减1,当计数为0时,才会释放资源

      引用计数指向的对象在堆上,保证了所有线程都能够访问该资源;

      shared_ptr引用计数通过原子操作实现,因此引用计数本身是线程安全的

      但是,如果多线程尝试修改引用计数时,需要用户手动进行同步、加锁等操作以确保对资源的访问是安全的

    • 不足

      存在循环引用问题,导致引用计数不能变成0

      两个类对象,二者通过shared_ptr智能指针互相引用,会增加引用计数

  • weak_ptr

    • 原理

      weak_ptr对象可以指向shared_ptr,但不会改变shared_ptr的引用计数

    • 不足

      仅用于解决shared_ptr的循环引用问题,它不能直接访问资源



实验

auto_ptr管理权转移

// auto_ptr管理权转移
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		std::cout << obj_name << "——构造函数.\n"; 
		}
    ~Test(){ 
		std::cout << obj_name << "——析构函数.\n"; 
		}
private:
	string obj_name;
};

int main() {
    auto_ptr<Test> obj1(new Test("obj1")); 
	auto_ptr<Test> obj2(new Test("obj2"));
	cout << "obj1 address = " << obj1.get() << endl;
	cout << "obj2 address = " << obj2.get() << endl;
	cout << "\n执行 obj2 = obj1\n" << endl;
	obj2 = obj1;
	cout << "obj1 address = " << obj1.get() << endl;
	cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915171327248

unique_ptr独占资源

// unique_ptr独占资源
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		cout << obj_name << "——构造函数.\n"; 
	}
    ~Test(){ 
		cout << obj_name << "——析构函数.\n"; 
	}
private:
	string obj_name;
};

int main() {
    unique_ptr<Test> obj1(new Test("obj1")); 
    unique_ptr<Test> obj2(new Test("obj2"));
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    cout << "\n执行 obj2 = move(obj1)\n" << endl;
    // obj2 = obj1;
	obj2 = move(obj1);
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915171846314

shared_ptr共享资源

// shared_ptr共享资源
#include <iostream>
#include <memory>
using namespace std;

class Test {
public:
    Test(const string &name):obj_name(name){ 
		cout << obj_name << "——构造函数.\n"; 
	}
    ~Test(){ 
		cout << obj_name << "——析构函数.\n"; 
	}
private:
	string obj_name;
};

int main() {
    shared_ptr<Test> obj1(new Test("obj1")); 
    shared_ptr<Test> obj2(new Test("obj2"));
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    cout << "\n执行 obj2 = obj1\n" << endl;
    obj2 = obj1;
    cout << "obj1 address = " << obj1.get() << endl;
    cout << "obj2 address = " << obj2.get() << endl;
    return 0;
}
image-20240915172140224

shared_ptr循环引用

// shared_ptr循环引用
#include <iostream>
#include <memory>
using namespace std;

class A;

class B {
public:
    shared_ptr<A> a_ptr;
    B() { cout << "B类 构造函数" << endl; }
    ~B() { cout << "B类 析构函数" << endl; }
};

class A {
public:
    shared_ptr<B> b_ptr;
    A() { cout << "A类 构造函数" << endl; }
    ~A() { cout << "A类 析构函数" << endl; }
};

int main() {
    // shared_ptr<A> a = make_shared<A>();
    // shared_ptr<B> b = make_shared<B>();
	shared_ptr<A> a(new A());
	shared_ptr<B> b(new B());

    a->b_ptr = b;  // A 指向 B
    b->a_ptr = a;  // B 指向 A

    // 循环引用导致对象不能被正确销毁

    cout << "a use_count: " << a.use_count() << endl;
    cout << "b use_count: " << b.use_count() << endl;

    return 0;
}
image-20240915173108183

weak_ptr解决循环引用

// weak_ptr解决循环引用
#include <iostream>
#include <memory>
using namespace std;

class A;  // 前向声明

class B {
public:
    weak_ptr<A> a_ptr;  // 使用 weak_ptr 来避免循环引用
    B() { cout << "B类 构造函数" << endl; }
    ~B() { cout << "B类 析构函数" << endl; }
};

class A {
public:
    // shared_ptr<B> b_ptr;
	weak_ptr<B> b_ptr;
    A() { cout << "A类 构造函数" << endl; }
    ~A() { cout << "A类 析构函数" << endl; }
};

int main() {
    // shared_ptr<A> a = make_shared<A>();
    // shared_ptr<B> b = make_shared<B>();
    shared_ptr<A> a(new A());
    shared_ptr<B> b(new B());

    a->b_ptr = b;
    b->a_ptr = a; 

    cout << "a use_count: " << a.use_count() << endl;
    cout << "b use_count: " << b.use_count() << endl;

    return 0;
}
image-20240915173621393
<think>好的,我现在需要回答用户关于C++智能指针的教学请求。首先,我要回忆智能指针的相关知识,确保自己理解正确。智能指针C++中用于管理动态内存的类模板,主要有几种类型:unique_ptr、shared_ptr、weak_ptr,还有已经被废弃的auto_ptr。 用户可能希望了解这些指针的用途、用法以及它们之间的区别。我应该从基本概念开始,逐个介绍每种智能指针的特点,然后给出示例代码说明如何使用。同时,要解释为什么需要智能指针,比如自动内存管理,避免内存泄漏等问题。 接下来,我需要考虑如何组织内容。首先介绍智能指针的整体概念,然后分点详细说明每种类型,包括它们的特性、适用场景和示例代码。例如,unique_ptr是独占所有权的,不能复制只能移动;shared_ptr是共享所有权,通过引用计数管理;weak_ptr是解决shared_ptr循环引用问题的辅助指针。 在示例代码部分,需要展示每种指针的基本用法,比如如何创建、如何转移所有权,以及循环引用的例子和如何用weak_ptr解决。代码要简洁明了,加上注释帮助理解。 之后,要总结智能指针的注意事项,比如不能混合使用原始指针和智能指针,避免循环引用,以及正确选择智能指针类型。最后,按照用户要求,提出相关问题,这些问题应该覆盖智能指针的基本概念、用法、区别和常见问题。 检查是否有遗漏的内容,比如make_shared和make_unique的使用,引用计数的机制,或者异常安全方面的优势。确保回答全面,但不过于冗长。最后确认问题部分是否符合格式要求,四个相关问题是否相关且有助于用户进一步学习。</think><< C++智能指针教学: 1. unique_ptr(独占指针) - 特性:唯一所有权,不可复制但可移动 - 适用场景:资源独占管理,简单内存管理 - 示例代码: ```cpp #include <memory> // 创建unique_ptr std::unique_ptr<int> uptr(new int(10)); // 转移所有权 std::unique_ptr<int> uptr2 = std::move(uptr); // 自动释放内存 ``` 2. shared_ptr(共享指针) - 特性:共享所有权,基于引用计数 - 适用场景:多对象共享资源 - 示例代码: ```cpp std::shared_ptr<int> sptr1 = std::make_shared<int>(20); { std::shared_ptr<int> sptr2 = sptr1; // 引用计数+1 } // 引用计数-1 // make_shared更高效(单次内存分配) ``` 3. weak_ptr(弱指针) - 特性:不增加引用计数,解决循环引用 - 适用场景:打破shared_ptr循环引用 - 示例代码: ```cpp class B; class A { std::shared_ptr<B> b_ptr; }; class B { std::weak_ptr<A> a_ptr; // 使用weak_ptr打破循环 }; ``` 4. 智能指针优势 - 自动内存管理 - 异常安全保证 - 明确的资源所有权语义 注意事项: 1. 不要混合使用原始指针和智能指针 2. 优先使用make_shared/make_unique 3. 避免循环引用(使用weak_ptr解决) 4. 不要用相同原始指针初始化多个智能指针 使用示例对比: ```cpp // 错误示例(双重释放) int* raw = new int(5); std::shared_ptr<int> p1(raw); std::shared_ptr<int> p2(raw); // 错误! // 正确用法 auto p3 = std::make_shared<int>(5); auto p4 = p3; // 正确共享 ``` >>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值