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

Calling function when last instance of class is destructed / overwrited

本文关键字:覆盖 调用 函数 最后一个 实例      更新时间:2023-10-16

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

问题是我不知道什么时候调用 fclose(( 或其他"生命周期结束"函数。可以调用析构函数,但与此同时,FILE * 被复制到另一个对象,例如通过复制构造函数。

我尝试编写"参考计数器"类(这将是所有类的基类(,但不幸的是我无法从构造函数/析构函数调用纯虚拟方法。

这是我尝试过的:

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
}

这里有两种解决方案,可以协同工作。

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

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>传递实例,该实例执行您尝试实现的引用计数类型 - 当最后一个std::shared_ptr本身被破坏时,File将被删除(因此调用其析构函数(。

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()功能,我更倾向于将其作为成员函数,以便其用法是明确的,而不是在您不打算这样做时自动发生。

您可以使用

共享指针作为删除器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;
}