C++:对象的引用计数器

C++: reference counter for objects

本文关键字:引用 计数器 对象 C++      更新时间:2023-10-16

我需要一个未在堆上分配的对象的引用计数器。

我需要它来在无法轻易复制和销毁的对象上实现RAII机制:

class File
{
private:
    int fd;
public:
    File( const std::string &path ) ... // opening file
    destroy( );                         // actually closing file
    File( const File &f ) ...           // just copying the fd
    ~File( );                           // doing nothing
}

对于这样的场景,通常使用std::shared_ptr:共享指针的对象的构造函数和析构函数只调用一次。

然而,在我的情况下,我更希望避免在堆上分配对象。我需要一个做类似于std::shared_ptr的工作的shared_object类,这样我的类的非复制构造函数和destroy函数(在上面的例子中)只被调用一次。

像这样的东西存在吗?

如果您想在动态存储("堆上")中分配nothing时具有共享指针行为,您可以查看各种智能指针实现策略。在《现代C++设计》中,作者在"智能指针"一章中讨论了其中的许多策略,该章可以在网上免费(合法)获得。

您感兴趣的技术是引用链接。使用这种技术,智能指针对象在双向双链表中链接在一起,而不是指向动态分配的引用计数器。


话虽如此,使用std::shared_ptrstd::unique_ptr或它们的Boost变体可能会更快地编写,更容易维护。如果动态分配和引用计数曾经是一个瓶颈(我怀疑它会是,但我们不能过于草率地概括),那么您总是可以花时间使用自定义引用链接版本。

您可以向std::shared_ptr提供自己的deleter,该deleter将调用您的自定义destroy函数而不是delete。

class File
{
private:
    int fd;
public:
    static void destroyThis(File* f){f->destroy();}
    File( const std::string &path ) ... // opening file
    void destroy( );                         // actually closing file
    File( const File &f ) ...           // You probably don't need this anymore.
    ~File( );                           // doing nothing
};
File fileObj("path");
std::shared_ptr<File> pf(&fileObj,std::bind(&File::destroyThis,std::placeholders::_1));
std::shared_ptr<File> pf2(pf);

我相信以下架构能够满足您的需求:

// pseudo-code
class File
{
private:
    int fd;
    File* prev;
    File* next;
public:
    File(const std::string &path) :
        fd(open(path)),
        prev(0),
        next(0)
    {}
    void destroy()
    {
        close(fd);
    }
    File( const File &f )
         fd(f.fd),
         prev(&f),
         next(f.next)
    {
         if (next)
             next->prev = this;
         f.next = this;
    }

    ~File()
    {
         if (prev)
            prev->next = next;
         if (next)
            next->prev = prev;
         if ((!prev) && (!next))
            destroy();
    }
};

在重复的文件实例之间维护一个双链接列表。列表的最后一个成员,因此最后一个重复调用destroy。不需要堆分配。

(显然这不是线程安全的。你可以用互斥来保护,也可以用无锁的方法来维护列表。)