在最后一个类实例被破坏/覆盖时调用函数

时间:2014-09-25 17:26:55

标签: c++ reference-counting

我正在尝试创建一个包装fopen()/ fclose()/ f *方法的类。我想将此方法用于其他目的,这就是我不想使用智能指针的原因。

问题是我不知道何时调用fclose()或其他'生命终结'功能。可以调用析构函数,但在此期间,FILE *被复制到另一个对象,例如通过复制构造函数。

我尝试编写'Reference Counter'类(它将是所有类的基类)但不幸的是我无法从构造函数/析构函数中调用纯虚方法。

这是我尝试过的:

class ReferenceCounter
{
    public:
        ReferenceCounter()
        {
            ReferenceCount = new unsigned int(0);
            AddRef();
        }
        ReferenceCounter(const ReferenceCounter & CopyFrom)
        {
            ReferenceCount = CopyFrom.ReferenceCount;
            AddRef();
        }
        ReferenceCounter & operator = (const ReferenceCounter & AssignFrom)
        {
            RemoveRef();
            ReferenceCount = AssignFrom.ReferenceCount;
            AddRef();

            return *this;
        }
        ~ReferenceCounter()
        {
            RemoveRef();
        }

        virtual void OnInit() = 0;
        virtual void OnDestruct() = 0;
    private:
        unsigned int * ReferenceCount;

        void AddRef()
        {
            if(++*ReferenceCount == 1)
                OnInit();
        }
        void RemoveRef()
        {
            if(--*ReferenceCount == 0)
            {
                OnDestruct();
                delete ReferenceCount;
            }
        }
};

也许有一种方法可以“覆盖”或“覆盖”一个类而不是另一个类?

示例:

class File
{
    public:
        File(std::string FileName)
        {
            F = fopen(FileName.c_str(), ...);
        }
        ~File()
        {
            fclose(F);
        }
    private:
        FILE * F;
};

int main()
{
    File File1("a.txt");
    auto File2 = File1;

    //SegFault = fclose called twice for File1 and File2
}

2 个答案:

答案 0 :(得分:2)

这里有两个解决方案,它们协同工作。

首先,不允许分配或复制您的"文件句柄"类。 1

class File
{
    // C++11 solution: use =delete
    public:
        File(File & const) = delete;
        File & operator=(File & const) = delete;

    // C++ < 11 solution: make them private and *don't implement them*:
    private:
        File(File & const);
        File & operator=(File & const);
};

其次,请考虑仅传递对单个File对象的引用。 (编译器不再允许你复制File个对象,所以如果你这样做是偶然的,你会得到一个编译错误 - 这很好,因为它可以帮助你识别你需要修复的区域。)

如果建立单一所有权点太困难,那么请考虑使用std::shared_ptr<File>来传递实例,而File正是您尝试实现的那种引用计数 - {{1}当最后std::shared_ptr本身被破坏时,将被删除(因此它的析构函数被调用)。

auto file = std::make_shared(new File{"a.txt"});
auto file2 = file;
// file.use_count() and file2.use_count() are now both 2.
//
// When file2 is destructed this will drop to 1; when file is destructed this will
// drop to 0, and the File object will be deleted.

1 请注意,您可以使用dup()实现复制,尽管分配的语义可能有点棘手 - 应该分配关闭现有句柄和dup()句柄被分配?如果你实现了dup()功能,我会更倾向于使它成为一个成员函数,以便它的用法是显式的,而不是在你不打算的时候自动发生。

答案 1 :(得分:1)

您可以使用带fclose的共享指针作为删除器:

#include <cstdio>
#include <memory>
#include <stdexcept>

class File
{
    private:
    typedef std::shared_ptr<FILE> shared_file;

    public:
    // You might consider const char*
    File(const std::string& FileName, const std::string& Mode)
    :   F(std::fopen(FileName.c_str(),  Mode.c_str()), fclose)
    {
        if( ! F.get()) {
            throw std::runtime_error("File Open Failure");
        }
    }

    private:
    shared_file F;
};

int main()
{
    File File1("/tmp/test.txt", "r");
    auto File2 = File1;
}