【施磊C++】智能指针


前言

本文介绍了C++中智能指针的原理与实现,基于施磊老师的C++课程。


一、基础知识

智能指针的基本原理:利用栈上的对象出作用域自动析构的特征,来做到资源的自动释放。

#include <iostream>
using namespace std;

template<typename T>
class CSmartPtr {
   
   
public:
    CSmartPtr(T *ptr = nullptr) : mptr(ptr) {
   
   }
    ~CSmartPtr() {
   
    delete mptr; }
    T& operator*() {
   
    return *mptr; }
    /*
    返回底层指针,让底层指针调用->运算符
    */
    T* operator->() {
   
    return mptr; }
private:
    T *mptr;
};

int main()
{
   
   
    CSmartPtr<int> p(new int);
    *p = 20;

    class Test {
   
   
    public:
        void func() {
   
    cout << "Test::func()" << endl; }
    };

    CSmartPtr<Test> ptr(new Test());
    // (ptr.operator->())->test();
    ptr->func();

    return 0;
}

注意不要将智能指针定义到堆上,没有意义,这种情况下智能指针会变成裸指针:

//看似使用智能指针,实际是裸指针
CSmartPtr<int> *p = new CSmartPtr<int>(new int);

二、不带引用计数的智能指针

在上一节的代码中,如果有以下语句:

CSmartPtr<int> p1(new int);
CSmartPtr<int> p2(p1);

那么运行程序时会发生coredump,容易发现发生了浅拷贝问题,即p2和p1指向了同一块内存资源,在出作用域析构时,p2析构释放了一次,p1析构时重复析构就出现了问题。如果按我们之前的处理方式,实现拷贝构造函数:

CSmart(const CSmart& src) {
   
   
	mptr = new T(*src.mptr);
}

依然会有问题,因为p1和p2指向了不同的资源,在用户看来,p1和p2应当指向同一个资源,这么处理对用户并不友好。

为了解决浅拷贝问题,C++提出了带引用计数的智能指针和不带引用计数的智能指针,我们先介绍带引用计数的智能指针:

1、auto_ptr

auto_ptr的处理是对拷贝构造函数做处理,其做法是在进行拷贝构造时,将原来的指针释放,并将原来的地址赋值给新的指针:

/**
       *  @brief  An %auto_ptr can be constructed from another %auto_ptr.
       *  @param  __a  Another %auto_ptr of the same type.
       *
       *  This object now @e owns the object previously owned by @a __a,
       *  which has given up ownership.
       */
      auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {
   
    }

	   /**
       *  @brief  Bypassing the smart pointer.
       *  @return  The raw pointer being managed.
       *
       *  You can get a copy of the pointer that this object owns, for
       *  situations such as passing to a function which only accepts
       *  a raw pointer.
       *
       *  @note  This %auto_ptr no longer owns the memory.  When this object
       *  goes out of scope, nothing will happen.
       */
      element_type*
      release() throw()
      {
   
   
	element_type* __tmp = _M_ptr;
	_M_ptr = 0;
	return __tmp;
      }
C++多线程编程中,施磊的代码示例提供了一个经典的“哲学家进餐问题”的实现[^1]。该问题的核心在于多个线程(代表哲学家)需要同时获取两个资源(左右两边的叉子),这可能导致死锁。施磊的实现中,通过使用`std::mutex`来模拟叉子,并在`wantsToEat`方法中使用`std::unique_lock`来确保线程安全。 以下是一个简化的C++多线程资源管理示例,适用于类似场景: ```cpp #include <iostream> #include <thread> #include <vector> #include <mutex> #include <functional> class DiningPhilosophers { public: const int n = 5; std::vector<std::unique_ptr<std::mutex>> mtxs; DiningPhilosophers() { for (int i = 0; i < n; i++) { mtxs.push_back(std::make_unique<std::mutex>()); } } void wantsToEat(int philosopher, std::function<void()> pickLeftFork, std::function<void()> pickRightFork, std::function<void()> eat, std::function<void()> putLeftFork, std::function<void()> putRightFork) { std::unique_lock<std::mutex> llock(*mtxs[philosopher]); std::unique_lock<std::mutex> rlock(*mtxs[(philosopher + n - 1) % n]); pickLeftFork(); pickRightFork(); eat(); putLeftFork(); putRightFork(); } }; int main() { DiningPhilosophers dp; auto pickLeft = [](int id) { std::cout << "Philosopher " << id << " picked up left fork.\n"; }; auto pickRight = [](int id) { std::cout << "Philosopher " << id << " picked up right fork.\n"; }; auto eat = [](int id) { std::cout << "Philosopher " << id << " is eating.\n"; }; auto putLeft = [](int id) { std::cout << "Philosopher " << id << " put down left fork.\n"; }; auto putRight = [](int id) { std::cout << "Philosopher " << id << " put down right fork.\n"; }; std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.emplace_back([&dp, i]() { dp.wantsToEat(i, [&]() { pickLeft(i); }, [&]() { pickRight(i); }, [&]() { eat(i); }, [&]() { putLeft(i); }, [&]() { putRight(i); }); }); } for (auto& t : threads) { t.join(); } return 0; } ``` ### C++资源推荐 1. **书籍** - 《C++ Primer》:适合初学者和有一定经验的开发者,内容详尽。 - 《Effective Modern C++》:Scott Meyers的经典之作,深入探讨C++11/14的新特性。 - 《C++ Concurrency in Action》:Anthony Williams撰写,专注于C++并发编程。 2. **在线教程与文档** - [CppReference](https://en.cppreference.com/):权威的C++参考文档,涵盖标准库和语言特性。 - [LearnCPP](https://www.learncpp.com/):适合初学者的C++教程网站。 - [GeeksforGeeks C++](https://www.geeksforgeeks.org/c-plus-plus/):提供大量C++示例和算法实现。 3. **视频课程** - **B站**:搜索“施磊 C++”,可以找到相关的教学视频,涵盖基础语法、面向对象、STL、多线程等。 - **Coursera**:提供由大学教授讲授的C++课程,如“C++程序设计”。 - **Udemy**:有大量实战项目导向的C++课程,如“C++17 Fundamentals”。 4. **开源项目与代码库** - **GitHub**:搜索关键词“C++ 项目”或“C++ 示例”,可以找到大量开源项目,如游戏引擎、算法库、图形渲染等。 - **Boost库**:C++的扩展库,包含大量实用的模板和工具类,适合进阶学习。 5. **社区与论坛** - **Stack Overflow**:遇到具体问题时,搜索相关标签(如`c++`)可以找到解决方案。 - **Reddit的r/cpp**:活跃的C++讨论区,适合交流经验和获取最新资讯。 - **C++论坛**:一些中文技术社区(如优快云、知乎)也有专门的C++讨论区。 ### 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值