深入实践C++11智能指针_template <class> class,2024年最新2024最新物联网嵌入式开发大厂面试真题大全

74 阅读13分钟

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

{
}

};

int main() { auto deletor = [](Socket *pSocket) { //关闭句柄 pSocket->close(); //TODO: 你甚至可以在这里打印一行日志... delete pSocket; };

std::unique_ptr<Socket, void (\*)(Socket \* pSocket)> upSocket(new Socket(), deletor);

return 0;

}


自定义 std::unique\_ptr 的资源释放函数其规则是:



std::unique_ptr<T, DeletorFuncPtr>


其中 T 是你要释放的对象类型,DeletorPtr 是一个自定义函数指针。上述代码 28 行表示 DeletorPtr 有点复杂(是 C++11 中的 Lambda 函数),我们可以使用 decltype(deletor) 让编译器自己推导 deletor 的类型,因此可以将 28 行代码修改为:



std::unique_ptr<Socket, decltype(deletor)> upSocket(new Socket(), deletor);


### std::shared\_ptr


std::unique\_ptr 对其持有的资源具有独占性,而 std::shared\_ptr 持有的资源可以在多个 std::shared\_ptr 之间共享,每多一个 std::shared\_ptr 对资源的引用,资源引用计数将增加 1,每一个指向该资源的 std::shared\_ptr 对象析构时,资源引用计数减 1,最后一个 std::shared\_ptr 对象析构时,发现资源计数为 0,将释放其持有的资源。多个线程之间,递增和减少资源的引用计数是安全的。(注意:这不意味着多个线程同时操作 std::shared\_ptr 引用的对象是安全的)。std::shared\_ptr 提供了一个 use\_count() 方法来获取当前持有资源的引用计数。除了上面描述的,std::shared\_ptr 用法和 std::unique\_ptr 基本相同。


下面是一个初始化 std::shared\_ptr 的示例:



int main() { //初始化方式1 std::shared_ptr sp1(new int(123));

//初始化方式2
std::shared_ptr<int> sp2;
sp2.reset(new int(123));

//初始化方式3
std::shared_ptr<int> sp3;
sp3 = std::make_shared<int>(123);

return 0;

}

std::unique\_ptr 一样,你应该优先使用 std::make\_shared 去初始化一个 std::shared\_ptr 对象。


再来看另外一段代码:



#include #include

class A { public: A() { std::cout << "A constructor" << std::endl; }

~A()
{
    std::cout << "A destructor" << std::endl;
}

};

int main() { { //初始化方式1 std::shared_ptr sp1(new A());

    std::cout << "use count: " << sp1.use\_count() << std::endl;

    //初始化方式2
    std::shared_ptr<A> sp2(sp1);
    std::cout << "use count: " << sp1.use\_count() << std::endl;

    sp2.reset();
    std::cout << "use count: " << sp1.use\_count() << std::endl;

    {
        std::shared_ptr<A> sp3 = sp1;
        std::cout << "use count: " << sp1.use\_count() << std::endl;
    }

    std::cout << "use count: " << sp1.use\_count() << std::endl;
}

return 0;

}


所以整个程序的执行结果如下:



A constructor use count: 1 use count: 2 use count: 1 use count: 2 use count: 1 A destructor


上述代码 22 行 sp1 构造时,同时触发对象 A 的构造,因此 A 的构造函数会执行。


此时只有一个 sp1 对象引用 22new 出来的 A 对象(为了叙述方便,下文统一称之为资源对象 A),因此代码 24 行打印出来的引用计数值为 1。


代码 27 行,利用 sp1 拷贝一份 sp2,导致代码 28 行打印出来的引用计数为 2。


代码 30 行调用 sp2 的 reset() 方法,sp2 释放对资源对象 A 的引用,因此代码 31 行打印的引用计数值再次变为 1。


代码 34 行 利用 sp1 再次 创建 sp3,因此代码 35 行打印的引用计数变为 2。


程序执行到 36 行以后,sp3 出了其作用域被析构,资源 A 的引用计数递减 1,因此 代码 38 行打印的引用计数为 1。


程序执行到 39 行以后,sp1 出了其作用域被析构,在其析构时递减资源 A 的引用计数至 0,并析构资源 A 对象,因此类 A 的析构函数被调用。


另外,**std::shared\_ptr 有几个常用函数如下**:


`void swap (unique_ptr& x)`


将 shared\_ptr 对象的内容与对象 x 进行交换,在它们两者之间转移管理指针的所有权而不破坏或改变二者的引用计数。


`void reset()`


`void reset (ponit p)`


没有参数时,先将管理的计数器引用计数减一并将管理的指针和计数器置清零。有参数 p 时,先做面前没有参数的操作,再管理 p 的所有权和设置计数器。


`element_type* get()`


得到其管理的指针。


`long int use_count()`


返回与当前智能指针对象在同一指针上共享所有权的 shared\_ptr 对象的数量,如果这是一个空的 shared\_ptr,则该函数返回 0。如果要用来检查 use\_count 是否为 1,可以改用成员函数 unique 会更快。


`bool unique()`


返回当前 shared\_ptr 对象是否不和其他智能指针对象共享指针的所有权,如果这是一个空的 shared\_ptr,则该函数返回 false。


`element_type& operator\*()`


重载指针的 `*` 运算符,返回管理的指针指向的地址的引用。


`element_type* operator->()`


重载指针的 `->` 运算符,返回管理的指针,可以访问其成员。


`explicit operator bool()`


返回存储的指针是否已经是空指针,返回的结果与 get() != 0 相同。


#### std::enable\_shared\_from\_this


实际开发中,有时候需要在类中返回包裹当前对象(this)的一个 std::shared\_ptr 对象给外部使用,C++ 新标准也为我们考虑到了这一点,有如此需求的类只要继承自 std::enable\_shared\_from\_this 模板对象即可。用法如下:



#include #include

class A : public std::enable_shared_from_this { public: A() { std::cout << "A constructor" << std::endl; }

~A()
{
    std::cout << "A destructor" << std::endl;
}

std::shared_ptr<A> getSelf()
{
    return shared\_from\_this();
}

};

int main() { std::shared_ptr sp1(new A());

std::shared_ptr<A> sp2 = sp1->getSelf();

std::cout << "use count: " << sp1.use\_count() << std::endl;

return 0;

}


上述代码中,类 A 的继承 std::enable\_shared\_from\_this 并提供一个 getSelf() 方法返回自身的 std::shared\_ptr 对象,在 getSelf() 中调用 shared\_from\_this() 即可。


std::enable\_shared\_from\_this 用起来比较方便,但是也存在很多不易察觉的陷阱。


##### 陷阱一:不应该共享栈对象的 this 给智能指针对象


假设我们将上面代码 main 函数 25 行生成 A 对象的方式改成一个栈变量,即:



//其他相同代码省略...

int main() { A a;

std::shared_ptr<A> sp2 = a.getSelf();

std::cout << "use count: " << sp2.use\_count() << std::endl;

return 0;

}


运行修改后的代码会发现程序在 `std::shared_ptr sp2 = a.getSelf()` 产生崩溃。这是因为,智能指针管理的是堆对象,栈对象会在函数调用结束后自行销毁,因此不能通过 shared\_from\_this() 将该对象交由智能指针对象管理。


切记:**智能指针最初设计的目的就是为了管理堆对象的**(即那些不会自动释放的资源)。


##### 陷阱二:避免 std::enable\_shared\_from\_this 的循环引用问题


再来看另外一段代码:



#include #include

class A : public std::enable_shared_from_this { public: A() { m_i = 9; //注意: //比较好的做法是在构造函数里面调用shared_from_this()给m_SelfPtr赋值 //但是很遗憾不能这么做,如果写在构造函数里面程序会直接崩溃

    std::cout << "A constructor" << std::endl;
}

~A()
{
    m_i = 0;

    std::cout << "A destructor" << std::endl;
}

void func()
{
    m_SelfPtr = shared\_from\_this();
}

public: int m_i; std::shared_ptr m_SelfPtr; };

int main() { { std::shared_ptr spa(new A()); spa->func(); }

return 0;

}


乍一看上面的代码好像看不出什么问题,让我们来实际运行一下看看输出结果:



A constructor


我们发现在程序的整个生命周期内,只有 A 类构造函数的调用输出,没有 A 类析构函数的调用输出,这意味着 new 出来的 A 对象产生了内存泄漏!


我们来分析一下为什么 new 出来的 A 对象得不到释放。当程序执行到 39 行后,spa 出了其作用域准备析构,在析构时其发现仍然有另外的一个 std::shared\_ptr 对象即 A::m\_SelfPtr 引用了 A,因此 spa 只会将 A 的引用计数递减为 1,然后就销毁自身了。现在留下一个矛盾的处境:必须销毁 A 才能销毁其成员变量 m\_SelfPtr,而销毁 m\_SelfPtr 必须先销毁 A。这就是所谓的 std::enable\_shared\_from\_this 的**循环引用**问题。我们在实际开发中应该避免做出这样的逻辑设计,这种情形下即使使用了智能指针也会造成内存泄漏。也就是说**一个资源的生命周期可以交给一个智能指针对象,但是该智能指针的生命周期不可以再交给整个资源来管理**。


### std::weak\_ptr


std::weak\_ptr 是一个不控制资源生命周期的智能指针,是对对象的一种弱引用,只是提供了对其管理的资源的一个访问手段,引入它的目的为协助 std::shared\_ptr 工作。


std::weak\_ptr 可以从一个 std::shared\_ptr 或另一个 std::weak\_ptr 对象构造,std::shared\_ptr 可以直接赋值给 std::weak\_ptr ,也可以通过 std::weak\_ptr 的 lock() 函数来获得 std::shared\_ptr。它的构造和析构不会引起引用计数的增加或减少。std::weak\_ptr 可用来解决 std::shared\_ptr 相互引用时的死锁问题(即两个std::shared\_ptr 相互引用,那么这两个指针的引用计数永远不可能下降为 0, 资源永远不会释放)。


示例代码如下:



#include #include

int main() { //创建一个std::shared_ptr对象 std::shared_ptr sp1(new int(123)); std::cout << "use count: " << sp1.use_count() << std::endl;

//通过构造函数得到一个std::weak\_ptr对象
std::weak_ptr<int> sp2(sp1);
std::cout << "use count: " << sp1.use\_count() << std::endl;

//通过赋值运算符得到一个std::weak\_ptr对象
std::weak_ptr<int> sp3 = sp1;
std::cout << "use count: " << sp1.use\_count() << std::endl;

//通过一个std::weak\_ptr对象得到另外一个std::weak\_ptr对象
std::weak_ptr<int> sp4 = sp2;
std::cout << "use count: " << sp1.use\_count() << std::endl;

return 0;

}


运行结果:



use count: 1 use count: 1 use count: 1 use count: 1


无论通过何种方式创建 std::weak\_ptr 都不会增加资源的引用计数,因此每次输出引用计数的值都是 1。


既然,std::weak\_ptr 不管理对象的生命周期,那么其引用的对象可能在某个时刻被销毁了,如何得知呢?std::weak\_ptr 提供了一个 `expired()` 方法来做这一项检测,返回 true,说明其引用的资源已经不存在了;返回 false,说明该资源仍然存在,这个时候可以使用 std::weak\_ptr 的 `lock()` 方法得到一个 std::shared\_ptr 对象然后继续操作资源,以下代码演示了该用法:



// tmpConn_ 是一个 std::weak_ptr 对象 // tmpConn_ 引用的TcpConnection已经销毁,直接返回 if (tmpConn_.expired()) return;

std::shared_ptr conn = tmpConn_.lock(); if (conn) { //对conn进行操作,省略... }


有读者可能对上述代码产生疑问,既然使用了 std::weak\_ptr 的 expired() 方法判断了对象是否存在,为什么不直接使用 std::weak\_ptr 对象对引用资源进行操作呢?实际上这是行不通的,std::weak\_ptr 类没有重写 operator-> 和 operator\* 方法,因此不能像 std::shared\_ptr 或 std::unique\_ptr 一样直接操作对象,同时 std::weak\_ptr 类也没有重写 operator! 操作,因此也不能通过 std::weak\_ptr 对象直接判断其引用的资源是否存在:



#include

class A { public: void doSomething() { } };

int main() { std::shared_ptr sp1(new A());

std::weak_ptr<A> sp2(sp1);

//正确代码
if (sp1)
{
    //正确代码
    sp1->doSomething();
    (\*sp1).doSomething();
}

//正确代码
if (!sp1)
{
}

//错误代码,无法编译通过
//if (sp2)
//{
// //错误代码,无法编译通过
// sp2->doSomething();
// (\*sp2).doSomething();
//}

//错误代码,无法编译通过
//if (!sp2)
//{

//}

return 0;

}


之所以 std::weak\_ptr 不增加引用资源的引用计数不管理资源的生命周期,是因为,即使它实现了以上说的几个方法,调用它们也是不安全的,因为在调用期间,引用的资源可能恰好被销毁了,这会造成棘手的错误和麻烦。


因此,**std::weak\_ptr 的正确使用场景是那些资源如果可能就使用,如果不可使用则不用的场景,它不参与资源的生命周期管理**。例如,网络分层结构中,Session 对象(会话对象)利用 Connection 对象(连接对象)提供的服务工作,但是 Session 对象不管理 Connection 对象的生命周期,Session 管理 Connection 的生命周期是不合理的,因为网络底层出错会导致 Connection 对象被销毁,此时 Session 对象如果强行持有 Connection 对象与事实矛盾。


std::weak\_ptr 的应用场景,经典的例子是订阅者模式或者观察者模式中。这里以订阅者为例来说明,消息发布器只有在某个订阅者存在的情况下才会向其发布消息,而不能管理订阅者的生命周期。



class Subscriber { };

class SubscribeManager { public: void publish() { for (const auto &iter : m_subscribers) { if (!iter.expired()) { //TODO:给订阅者发送消息 } } }

private: std::vector<std::weak_ptr> m_subscribers; };


另外,**std::weak\_ptr 有几个常用函数如下**:


`void swap (weak_ptr& x)`


将当前 weak\_ptr 对象的内容与 x 的内容交换。


`void reset()`


将当前 weak\_ptr 对象管理的指针和计数器变成空的,就像默认构造的一样。


`long int use_count()`


返回与当前 weak\_ptr 对象在同一指针上共享所有权的 shared\_ptr 对象的数量。


`bool expired()`


检查是否过期,返回 weak\_ptr 对象管理的指针为空,或者和他所属共享的没有更多 shared\_ptr。lock 函数一般需要先调用 expired 判断,如果已经过期,就不能通过 weak\_ptr 恢复拥有的 shared\_ptr。此函数应返回与(use\_count() == 0)相同的值,但是它可能以更有效的方式执行此操作。


`shared_ptr<element_type> lock()`


如果它没有过期,则返回一个 shared\_ptr,其中包含由 weak\_ptr 对象保留的信息。如果 weak\_ptr 对象已经过期,则该函数返回一个空的 shared\_ptr(默认构造一样)。因为返回的 shared\_ptr 对象也算作一个所有者,所以这个函数锁定了拥有的指针,防止它被释放(至少在返回的对象没有释放它的情况下)。 此操作以原子方式执行。


### 智能指针的大小


一个 std::unique\_ptr 对象大小与裸指针大小相同(即 `sizeof(std::unique_ptr) == sizeof(void)`),而 std::shared\_ptr 的大小是 std::unique\_ptr 的一倍。以下是我分别在 Visual Studio 2019 和 gcc/g++ 4.8 上(二者都编译成 x64 程序)的测试结果:



#include #include #include

int main() { std::shared_ptr sp0; std::shared_ptrstd::string sp1; sp1.reset(new std::string()); std::unique_ptr sp2; std::weak_ptr sp3;

std::cout << "sp0 size: " << sizeof(sp0) << std::endl;
std::cout << "sp1 size: " << sizeof(sp1) << std::endl;
std::cout << "sp2 size: " << sizeof(sp2) << std::endl;
std::cout << "sp3 size: " << sizeof(sp3) << std::endl;

return 0;

}


Visual Studio 2019 (32bit) 运行结果:



sp0 size:8 sp1 size:8 sp2 size:4 sp3 size:8


gcc/g++ (64bit) 运行结果:



sp0 size:16 sp1 size:16 sp2 size:8 sp3 size:16

32 位机器上,std\_unique\_ptr 占 4 字节,std::shared\_ptr 和 std::weak\_ptr 占 8 字节。


在 64 位机器上,std\_unique\_ptr 占 8 字节,std::shared\_ptr 和 std::weak\_ptr 占 16 字节。


也就是说,std\_unique\_ptr 的大小总是和原始指针大小一样,std::shared\_ptr 和 std::weak\_ptr 大小是原始指针的一倍。


### 智能指针使用注意事项


C++ 新标准提倡的理念之一是不应该再手动调用 delete 或者 free 函数去释放内存了,而应该把它们交给新标准提供的各种智能指针对象。C++ 新标准中的各种智能指针是如此的实用与强大,在现代 C++ 项目开发中,我们应该尽量去使用它们。智能指针虽然好用,但稍不注意,也可能存在许多难以发现的 bug,这里我根据经验总结了几条:


* **一旦一个对象使用智能指针管理后,就不该再使用原始裸指针去操作**  
 看一段代码:



#include

class Subscriber { };

int main() { Subscriber *pSubscriber = new Subscriber();

std::unique_ptr<Subscriber> spSubscriber(pSubscriber);

delete pSubscriber;

return 0;

}


这段代码利用创建了一个堆对象 Subscriber,然后利用智能指针 spSubscriber 去管理之,可以却私下利用原始指针销毁了该对象,这让智能指针对象 spSubscriber 情何以堪啊?


记住,一旦智能指针对象接管了你的资源,所有对资源的操作都应该通过智能指针对象进行,不建议再通过原始指针进行操作了。


当然,除了 std::weak\_ptr 之外,std::unique\_ptr 和 std::shared\_ptr 都提供了获取原始指针的方法——get() 函数。



int main() { Subscriber *pSubscriber = new Subscriber();

std::unique_ptr<Subscriber> spSubscriber(pSubscriber);

//pTheSameSubscriber和pSubscriber指向同一个对象
Subscriber \*pTheSameSubscriber = spSubscriber.get();

return 0;

}


* **分清楚场合应该使用哪种类型的智能指针**


通常情况下,如果你的资源不需要在其他地方共享,那么应该优先使用 std::unique\_ptr,反之使用 std::shared\_ptr,当然这是在该智能指针需要管理资源的生命周期的情况下;如果不需要管理对象的生命周期,请使用 std::weak\_ptr。


* **认真考虑,避免操作某个引用资源已经释放的智能指针**


前面的例子,一定让你觉得非常容易知道一个智能指针的持有的资源是否还有效,但是还是建议在不同场景谨慎一点,有些场景是很容易造成误判。例如下面的代码:



#include #include

class T { public: void doSomething() { std::cout << "T do something..." << m_i << std::endl; }

private: int m_i; };

int main() { std::shared_ptr sp1(new T()); const auto &sp2 = sp1;

sp1.reset();

//由于sp2已经不再持有对象的引用,程序会在这里出现意外的行为
sp2->doSomething();

return 0;

}


上述代码中,sp2 是 sp1 的引用,sp1 被置空后,sp2 也一同为空。这时候调用 sp2->doSomething(),sp2->(即 operator->)在内部会调用 get() 方法获取原始指针对象,这时会得到一个空指针(地址为 0),继续调用 doSomething() 导致程序崩溃。


你一定仍然觉得这个例子也能很明显地看出问题,ok,让我们把这个例子放到实际开发中再来看一下:



//连接断开 void MonitorServer::OnClose(const std::shared_ptr &conn) { std::lock_guardstd::mutex guard(m_sessionMutex); for (auto iter = m_sessions.begin(); iter != m_sessions.end(); ++iter) { //通过比对connection对象找到对应的session if ((*iter)->GetConnectionPtr() == conn) { m_sessions.erase(iter); //注意这里:程序在此处崩溃 LOGI("monitor client disconnected: %s", conn->peerAddress().toIpPort().c_str()); break; } } }


该段程序会在代码 12 行处崩溃,崩溃原因是调用了 `conn->peerAddress()` 方法。为什么这个方法的调用可能会引起崩溃?现在可以一目了然地看出了吗?


崩溃原因是传入的 conn 对象和上一个例子中的 sp2 一样都是另外一个 std::shared\_ptr 的引用,当连接断开时,对应的 TcpConnection 对象可能早已被销毁,而 conn 引用就会变成空指针(严格来说是不再拥有一个 TcpConnection 对象),此时调用 TcpConnection 的 peerAddress() 方法就会产生和上一个示例一样的错误。


* **作为类成员变量时,应该优先使用前置声明(forward declarations)**


我们知道,为了减小编译依赖加快编译速度和生成二进制文件的大小,C/C++ 项目中一般在 \*.h 文件对于指针类型尽量使用前置声明,而不是直接包含对应类的头文件。例如:



//Test.h //在这里使用A的前置声明,而不是直接包含A.h文件 class A;

class Test { public: Test(); ~Test();

private: A *m_pA; };


同样的道理,在头文件中当使用智能指针对象作为类成员变量时,也应该优先使用前置声明去引用智能指针对象的包裹类,而不是直接包含包含类的头文件。



//Test.h #include

//智能指针包裹类A,这里优先使用A的前置声明,而不是直接包含A.h class A;

class Test { public: Test(); ~Test();

private: std::unique_ptr m_spA; };


Modern C/C++ 已经变为 C/C++ 开发的趋势,希望能善用和熟练这些智能指针对象。



### 智能指针的简单实现


最后,给出智能指针的简单实现,因为 weak\_ptr 作为弱引用指针,其实现依赖于 Counter 计数器类和 shared\_ptr 的赋值,所以先进行 Counter 计数器类和 share\_ptr 的简单实现。



#### Counter的简单实现



/* * 计数器 * Counter对象就是用来申请一块内存存储引用计数 * m_refCount是SharedPtr的引用计数 * m_weakCount是WeakPtr的引用计数 * 当m_weakCount为0时删除Counter对象 */ template class Counter { friend class SharedPtr; friend class WeakPtr;

public: Counter() : m_refCount(0), m_weakCount(0) {} virtual ~Counter() {}

private: Counter(const Counter &) = delete; Counter &operator=(const Counter &) = delete;

private: atomic_uint m_refCount; // #shared,原子操作 atomic_uint m_weakCount; // #weak,原子操作 };


#### shared\_ptr的简单实现



/* * SharedPtr的简单实现 */ template class SharedPtr {

friend class WeakPtr<T>;

public: /* * 构造函数,用原生指针构造 */ SharedPtr(T *ptr) : m_ptr(ptr), m_cnt(new Counter) { if (ptr) { m_cnt->m_refCount = 1; } cout << "Ptr Construct S." << endl; }

~SharedPtr()
{
    release();
}
/\*

* 拷贝构造函数,用另一个SharedPtr对象构造 */ SharedPtr(const SharedPtr &s) { m_ptr = s.m_ptr; s.m_cnt->m_refCount++; m_cnt = s.m_cnt; cout << "S Copy Construct S." << endl; }

/\*

* 拷贝构造函数,用另一个WeakPtr对象构造 * 为了WeakPtr对象调用自己的lock()方法将自己传进来构造一个SharedPtr返回 */ SharedPtr(const WeakPtr &w) { m_ptr = w.m_ptr; w.m_cnt->m_refCount++; m_cnt = w.m_cnt; cout << "W Copy Construct S." << endl; }

/\*

* 赋值构造函数,用另一个SharedPtr对象构造 */ SharedPtr &operator=(const SharedPtr &s) { if (this != s) { this->release(); m_ptr = s.m_ptr; s.m_cnt->m_refCount++; m_cnt = s.m_cnt; cout << "S Assign Construct S." << endl; } return *this; }

T &operator\*()
{
    return \*m_ptr;
}

T \*operator->()
{
    return m_ptr;
}

protected: void release() { m_cnt->m_refCount--; if (m_cnt->m_refCount < 1) { delete m_ptr; m_ptr = nullptr; cout << "SharedPtr Delete Ptr." << endl; if (m_cnt->m_weakCount < 1) { delete m_cnt; m_cnt = nullptr; cout << "SharedPtr Delete Cnt." << endl; } cout << "SharedPtr Release." << endl; } }

private: T *m_ptr; Counter *m_cnt; };


#### weak\_ptr的简单实现



template class WeakPtr { public: /* * 构造函数,用SharedPtr对象构造 */ WeakPtr(SharedPtr &s) : m_ptr(s.m_ptr), m_cnt(s.m_cnt) { m_cnt->m_weakCount++; cout << "S Construct W." << endl; } /* * 构造函数,用WeakPtr对象构造 */ WeakPtr(WeakPtr &w) : m_ptr(w.m_ptr), m_cnt(w.m_cnt) { m_cnt->m_weakCount++; cout << "W Construct W." << endl; }

~WeakPtr()
{
    release();
}
/\*

* 赋值构造函数,用另一个SharedPtr对象构造 */ WeakPtr &operator=(SharedPtr &s) { release(); m_cnt = s.m_cnt; m_cnt->m_weakCount++; m_ptr = s.m_ptr; cout << "S Assign Construct W." << endl; return *this; } /* * 赋值构造函数,用另一个WeakPtr对象构造 */ WeakPtr &operator=(WeakPtr &w) { if (this != &w) { release(); m_cnt = w.m_cnt; m_cnt->m_weakCount++; m_ptr = w->m_ptr; cout << "W Assign Construct W." << endl; } return *this; } /* * WeakPtr通过lock函数获得SharedPtr */ SharedPtr &lock() { return SharedPtr(*this); } /* * 检查SharedPtr是否已过期 */ bool expired() { if (m_cnt) { if (m_cnt->m_refCount > 0) return false; } return true; }

private: WeakPtr() = delete; WeakPtr禁止默认构造,只能从SharedPtr或者WeakPtr构造 T &operator*() = delete; //WeakPtr禁止* T *operator->() = delete; //WeakPtr禁止->

private: void release() { if (m_cnt) { m_cnt->m_weakCount--; if (m_cnt->m_weakCount < 1 && m_cnt->m_refCount < 1) { delete m_cnt; m_cnt = nullptr; cout << "Delete Cnt." << endl; } cout << "WeakPtr Release." << endl;

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。 img img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!