我有一些小类,包括对外部资源的引用。因为它们很小但经常传递并经常堆栈分配,所以我不传递指针而是实例本身。因此通常使用复制构造函数,有时使用赋值。
然而,这会导致资源管理不平衡,一旦第一个实例离开某个范围,外部资源就会被释放,而仍有副本。对于指针,有一些聪明但通常很昂贵的东西,如std::shared_ptr
。
但是,如何处理实例传递情况?似乎几乎不可能通过一个实例知道它是否是最后一种被破坏的?
答案 0 :(得分:0)
以前我曾推荐过一个单身人士,但对使用这种模式有相当多的批评 - one example。
如果你想要更具异国情调的东西(也许你需要在你的课堂上存储某种本地状态),你可以在对象本身中嵌入你自己的引用计数:
// Psuedo code
class Resource
{
static unsigned _refCount;
public:
Resource() {++_refCount;}
~Resource() {--_refCount; if(_refCount == 0) // cleanup!;}
};
您会注意到这与shared_ptr基本上是相同的方案,但是它允许通过实例化而不是传递指针来使用堆栈上的项目(您还必须将资源存储在静态成员中,对于所有实例有权访问它)。正如其中一条评论建议的那样,您应该更喜欢shared_ptr,如果您可以通过个人资料验证它对您的需求来说太慢,则只能拒绝它。
答案 1 :(得分:0)
这个问题的答案取决于你想要达到的终身政策。
如果你想要引用计数,那么当最后一个句柄超出范围时会释放资源,那么shared_ptr和intrusive_ptr是我所知道的唯一选项。你可以手动输入一些东西,但在做这件事之前我会想很久很久。如果您的程序是单线程的,那么最简单的方法是使用Boost共享指针禁用原子计数:
#define BOOST_SP_DISABLE_THREADS
#include <boost/shared_ptr.hpp>
这应该会显着降低成本。当然上面的评论是正确的,在走这条路线之前,你通常会对代码进行分析,以确保它确实是一个瓶颈,但是这个讨论与你提出的问题有点偏离。
如果您可以使用静态缓存而不是引用计数,那么您可以使用静态缓存:
// In the header.
#include <memory>
#include <functional>
namespace detail {
using deleter_t = std::function<void (void *)>;
using handle_t = std::unique_ptr<void, deleter_t>;
void *search_persistent_cache(std::string const &id);
void add_to_persistent_cache(std::string const &id, void *);
}
template<typename T, typename... Args>
T &get_persistent_resource(std::string const &id, Args&&... args) {
auto resource = search_persistent_cache(id);
if(resource) {
return *static_cast<T*>(resource);
}
auto uniq_instance = std::unique_ptr<T>(std::forward<Args>(args)...);
auto raw_instance = uniq_instance.get();
detail::deleter_t deleter = [](void *resource) -> void {
delete static_cast<T*>(resource);
};
try {
add_to_persistent_cache(id,
detail::handle_t { uniq_instance.release(), std::move(deleter); });
} catch(...) {
delete instance;
return nullptr;
}
}
这对于一般的想法应该足够了。我将由您来实现源文件中的静态存储和未定义函数。您可以进行一些调整,例如,如果您不需要由标识符索引的资源,或者您希望避免使用void *,那么调试版本可以使用boost :: polymorphic_down_cast。
我无法想到任何其他生命周期管理策略并不比上面显示的复杂得多。