shared_from_this
是 C++11 中引入的功能,允许对象在继承了 std::enable_shared_from_this
的情况下,安全地生成自身的 std::shared_ptr
实例,而不会创建新的控制块(reference counting block)。这样可以避免悬垂指针的问题,特别是在对象的成员函数中使用时,可以确保对象在使用期间不被销毁。
下面是一个简单的例子:
c++
#include <iostream>
#include <memory>
class MyClass : public std::enable_shared_from_this<MyClass> {
public:
void show() {
std::cout << "MyClass instance" << std::endl;
}
std::shared_ptr<MyClass> getShared() {
return shared_from_this();
}
};
int main() {
std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();
ptr->show();
std::shared_ptr<MyClass> anotherPtr = ptr->getShared();
// 'ptr' and 'anotherPtr' now share ownership of the same object
}
**如何正确继承和使用 **std::enable_shared_from_this
在 C++11 及其之后的版本中,为了在类的内部安全地使用 shared_from_this()
方法,类必须继承自 std::enable_shared_from_this<T>
。这是因为 shared_from_this()
是 std::enable_shared_from_this<T>
的成员函数,只有继承了这个基类的对象才具备调用它的能力。
这种设计的原因是std::enable_shared_from_this<T>
内部维护了一个 std::weak_ptr<T>
。当第一个 std::shared_ptr<T>
开始管理该对象时,这个 weak_ptr
被初始化。之后,当 shared_from_this()
被调用时,它将基于这个已经存在的 weak_ptr
返回一个新的 std::shared_ptr<T>
,这个新的 shared_ptr
与原有的 shared_ptr
共享对对象的所有权。
这样就避免了在对象内部直接创建新的 std::shared_ptr
实例,这种直接创建可能导致独立的所有权块的形成,增加了资源释放错误的风险。
使用注意事项
- 构造函数中禁用 :在对象的构造函数中使用
shared_from_this
是错误的,因为此时还没有std::shared_ptr
实例管理该对象。 - 安全调用条件 :只有当至少有一个
std::shared_ptr
实例正在管理该对象时,调用shared_from_this
才是安全的。在任何std::shared_ptr
管理该对象之前,shared_from_this
将无法正确工作并可能抛出异常。
继承与构造行为
继承 std::enable_shared_from_this
并不改变如何构造对象。你仍需提供适当的构造函数 ,特别是当默认构造函数不适用的情况下。此外,虽然 std::enable_shared_from_this
是一个基类,但继承它并不意味着基类和派生类之间共享对象所有权。相反,这种继承关系允许派生类在必要时通过 shared_from_this()
安全地生成一个新的 std::shared_ptr
实例,这个新实例将与已经存在的管理同一对象的 shared_ptr
共享所有权。
C++ 17 之前获取weak_ptr 的做法
在 C++17 之前,当你从一个类内部需要引用自己时,你可以使用 enable_shared_from_this
来安全地获取一个 shared_ptr
,但没有直接的方式来获取一个 weak_ptr
。
在 C++17 之前的用法中,如果你需要在类的内部获取一个指向自己的 weak_ptr
,你必须先调用 shared_from_this()
来获得一个 shared_ptr
,然后从这个 shared_ptr
创建一个 weak_ptr
。这样的操作是安全的,但它稍显间接和不便。
例如,在 C++17 之前,你可能会这样写:
c++
class Listener : public std::enable_shared_from_this<Listener> {
public:
std::weak_ptr<Listener> getWeakPtr() {
return shared_from_this();
}
// ...
};
在这个例子中,getWeakPtr
方法首先调用 shared_from_this()
来获取一个 shared_ptr
,然后自动将其转换为 weak_ptr
。
而在 C++17 中,enable_shared_from_this
类模板被增强,包括了一个 weak_from_this
方法,直接返回一个 weak_ptr
,这使得代码更直接和简洁。这个改进减少了需要创建临时 shared_ptr
的场景,使得代码更加高效和易于理解。
C++17中的 **weak_from_this
**功能介绍
该函数自C++17起提供了两个版本:
c++
std::weak_ptr<T> weak_from_this() noexcept; // (1)
std::weak_ptr<T const> weak_from_this() const noexcept; // (2)
这两个函数返回一个std::weak_ptr<T>
,该智能指针追踪所有指向*this
的std::shared_ptr
实例。
返回值
函数返回一个与*this
相关联的std::weak_ptr<T>
,共享所有权但不增加引用计数。
**weak_from_this
**的重要性
在C++17更新之前,由于std::enable_shared_from_this
缺少直接获取std::weak_ptr
的方法。weak_from_this
填补了这一空缺,并具有以下优势:
- 避免循环引用 :通过
weak_ptr
,可以解决因对象间相互持有shared_ptr
而导致的循环引用和潜在内存泄漏问题。 - 增强安全性 :
weak_from_this
允许安全地检查一个对象是否仍被shared_ptr
管理,通过尝试从weak_ptr
升级到shared_ptr
来访问对象之前,确认其未被销毁。 - 提供灵活性 :在复杂的设计模式或对象关系中,
weak_ptr
提供了根据对象生命周期动态管理资源的灵活性,同时不扰乱对象的生命周期管理。
通过引入weak_from_this
,C++17使得std::enable_shared_from_this
在处理复杂对象关系和资源管理时变得更为灵活和安全。
weak_from_this 使用示例
下面是一个使用 std::enable_shared_from_this
和 weak_from_this
的 C++ 示例。这个例子模拟了一个简单的事件监听器系统,其中监听器可以注册到事件发生器上。使用 std::weak_ptr
可以防止循环引用,同时确保在尝试通知监听器时,监听器仍然存在。
c++
#include <iostream>
#include <vector>
#include <memory>
/**
* @class Listener
* @brief A listener class that can listen to events.
*
* This class is derived from std::enable_shared_from_this to allow
* listeners to provide a shared_ptr to themselves when registering
* for events, without creating a new shared_ptr manually.
*/
class Listener : public std::enable_shared_from_this<Listener> {
public:
void onEvent() {
std::cout << "Event received!" << std::endl;
}
};
class EventGenerator {
public:
/**
* @brief Register a listener for events.
*
* @param listener A shared_ptr to the Listener to register.
*/
void registerListener(std::shared_ptr<Listener> listener) {
listeners.push_back(listener->weak_from_this());
}
/**
* @brief Notify all registered listeners of an event.
*/
void notifyListeners() {
// 遍历所有注册的监听器
for (auto weakListener : listeners) {
// 尝试从 weak_ptr 获取 shared_ptr
auto listener = weakListener.lock();
// 检查返回的 shared_ptr 是否为空,确保监听器仍存在
if (listener) {
listener->onEvent(); // 安全调用监听器的事件处理函数
} else {
// 可以在这里处理监听器已销毁的情况,例如从列表中移除
std::cout << "Listener has been destroyed and removed." << std::endl;
// 移除逻辑代码(此处略)
}
}
}
private:
std::vector<std::weak_ptr<Listener>> listeners; ///< List of weak pointers to registered listeners.
};
int main() {
auto eventGenerator = std::make_shared<EventGenerator>();
auto listener1 = std::make_shared<Listener>();
auto listener2 = std::make_shared<Listener>();
eventGenerator->registerListener(listener1);
eventGenerator->registerListener(listener2);
// Simulate an event
eventGenerator->notifyListeners();
// Output will be:
// Event received!
// Event received!
return 0;
}
在这个示例中,EventGenerator
类有一个方法 registerListener
,它接受一个指向 Listener
的 std::shared_ptr
并将其存储为 std::weak_ptr
。这样做的好处是,EventGenerator
不会增加 Listener
实例的引用计数,从而防止循环引用的问题。当 EventGenerator
需要通知监听器时,它会尝试通过调用 std::weak_ptr::lock
来获取一个 std::shared_ptr
,如果相关 Listener
已经被销毁,则 lock
会失败,这样就避免了访问悬空指针的风险。
这个示例展示了 std::enable_shared_from_this
和 weak_from_this
在复杂的对象关系和生命周期管理中的应用,特别是在事件监听系统这类场景下的有效性。
常用的使用情况:你需要引用一个对象,但又不想拥有它(即不想增加引用计数)
在复杂的系统设计和某些特定的编程模式中。这种需求通常出现在以下几种情况:
- 避免循环引用 :在使用智能指针管理对象生命周期的系统中,循环引用是一个常见问题。如果两个对象互相持有对方的
std::shared_ptr
,它们的引用计数永远不会降到零,导致内存泄漏。在这种情况下,一个对象可以持有对另一个对象的std::weak_ptr
,以保持对该对象的访问能力,但不形成强引用循环。 - 观察者模式 :在观察者模式中,被观察的对象(如事件发生器)通常不需要"拥有"其观察者。观察者可能由其他部分的系统管理。在这种情况下,使用
std::weak_ptr
来引用观察者是合适的,因为它允许观察者独立于被观察对象被创建和销毁。 - 临时引用:在某些情况下,你可能需要临时引用一个对象,但不想因此影响其生命周期。例如,在一个复杂的图或网络结构中,节点可能需要知道与之相连的其他节点,但不需要对它们拥有所有权。
- 资源的安全管理 :使用
std::weak_ptr
可以避免悬挂指针的问题。当从std::weak_ptr
转换为std::shared_ptr
时,如果原始对象已经不存在,你会得到一个空的shared_ptr
,从而安全地表明对象不再可用。
这些情况下,std::weak_ptr
提供了一种既可以访问对象又不会延长其生命周期的方式,这对于管理复杂的对象关系和避免内存泄漏非常有用。
总结
shared_from_this
和 weak_from_this
的使用场景和目的有所不同。
shared_from_this
:- 用途 :
shared_from_this
用于在类的成员函数内部安全地获取一个指向当前对象的std::shared_ptr
。这适用于您需要确保当前对象在函数执行期间保持存活的场景。 - C++11 及以后 :这个方法自 C++11 引入,适用于所有继承自
std::enable_shared_from_this
的类。 - 场景 :例如,当一个类的成员函数需要将
this
对象作为shared_ptr
传递给其他函数或存储它时,使用shared_from_this
。
- 用途 :
weak_from_this
:- 用途 :C++17 新增的
weak_from_this
方法返回一个std::weak_ptr
,它被用于创建一个不增加引用计数的指针,这对于避免循环引用特别有用。 - C++17 新增 :这是 C++17 新增的功能,用于获取一个
weak_ptr
,从而可以在不创建额外shared_ptr
(和不增加引用计数)的情况下观察对象。 - 场景 :当你需要引用一个对象,但又不想拥有它(即不想增加引用计数),以避免循环引用或其他所有权问题时,使用
weak_from_this
。
- 用途 :C++17 新增的
综上所述,shared_from_this
和 weak_from_this
都是在特定场景下的解决方案。在 C++17 之后,你拥有了更多的选择:如果需要共享所有权并确保对象在使用期间保持存活,使用 shared_from_this
;如果需要引用对象但不取得所有权,以避免循环引用或其他问题,使用 weak_from_this
。