C++ 使用静态std::set来管理一个类的所有实例的资源有什么目的吗?

C++ 使用静态std::set来管理一个类的所有实例的资源有什么目的吗?,c++,memory-management,std,C++,Memory Management,Std,我继承了一些我正在重新分解的代码,我遇到了一些我不确定意图的东西。许多(或多或少全部)类都默认为下面的模式,在这种模式下,内存管理似乎是要用静态std::set来处理的 class A { public: A() { B_ = new B(); All_A.insert(this); } ~A(){ delete B_; } static void DestructAll_A() { for (

我继承了一些我正在重新分解的代码,我遇到了一些我不确定意图的东西。许多(或多或少全部)类都默认为下面的模式,在这种模式下,内存管理似乎是要用静态std::set来处理的

class A
{
public:
    A()
    { 
        B_ = new B();
        All_A.insert(this);
    }

    ~A(){ delete B_; }

    static void DestructAll_A()
    {
        for (std::set<A*>::iterator Itr = All_A.begin(); Itr != All_A.end(); ++Itr) 
        {
            A* Obj = *Itr;
            delete Obj;
        }

        All_A.clear();
    };

    static std::set<A*> All_A;

    B* B_;
}
A类
{
公众:
()
{ 
B=新的B();
全部插入(本);
}
~A(){delete B_;}
静态void DestructAll_A()
{
for(std::set::iterator Itr=All_A.begin();Itr!=All_A.end();++Itr)
{
A*Obj=*Itr;
删除Obj;
}
一切都清楚了;
};
静态标准::设置所有_A;
B*B_;
}
我最初的想法是,这是一种不好的做法。就我所知,使用这些类来实现这种方式并没有明显的优势。此外,DestructAll_A()不调用A的析构函数,因此除非事先调用A的析构函数,否则B永远不会被删除,这造成了对操作顺序的依赖,这让我感到困惑(尽管我认为这可以在DestructAll_A()中完成)。最后,我不清楚如何区分静态构造的和动态构造的(使用new)实例

我错过什么了吗?使用此模式的一般目的或优点是什么?提前谢谢

DestructAll_A()
不调用A的析构函数

是的
Obj
具有类型
A*
,因此
delete Obj
将调用
A::~A()
析构函数。对于未使用标量
new A()
分配的任何实例,这是未定义的行为

最后,我不清楚如何区分静态构造的和动态构造的(使用new)实例

它不试图区分。。。除非与标量动态分配一起使用,否则不得使用此类

DestructAll_A()
不调用A的析构函数

是的
Obj
具有类型
A*
,因此
delete Obj
将调用
A::~A()
析构函数。对于未使用标量
new A()
分配的任何实例,这是未定义的行为

最后,我不清楚如何区分静态构造的和动态构造的(使用new)实例

它不试图区分。。。除非与标量动态分配一起使用,否则决不能使用此类。

在注释OP中询问“是否有可能以某种方式隐式调用DestructAll_a()?”

不是以隐含的方式,但几乎可以这样做:

class A_Cleaner
{
    ~A_Cleaner(){
         A::DestructAll_A();
    }
}

void Main()
{
     A_Cleaner cleaner;

     .... //your program

     // just before reaching the end of main, cleaner dtor is called, so A::DestructAll_A() is called, even in case of exception;
}
在评论中,我们会问“是否有可能以某种方式隐式调用DestructAll_a()?”

不是以隐含的方式,但几乎可以这样做:

class A_Cleaner
{
    ~A_Cleaner(){
         A::DestructAll_A();
    }
}

void Main()
{
     A_Cleaner cleaner;

     .... //your program

     // just before reaching the end of main, cleaner dtor is called, so A::DestructAll_A() is called, even in case of exception;
}

我认为这是一种试图实现“穷人的垃圾收集”的尝试。以下是如何在批处理情况下工作:

  • 您将获得一批新的工作,并开始处理
  • 您可以一边分配内存,一边忽略取消分配内存的需要
  • 批处理结束后,您可以调用
    DestructAll_XYZ
    来清理混乱
  • 您的系统已准备好下一批

这种方法既快又脏,所以我绝对建议在某个时候把它清理干净。至少我会去掉静态集合,用更可控的东西来代替它们——比如说,
BatchMemoryContext
,其中包含所有相关集合。通过这种方式,您可以严格控制对象的生命周期,并避免可能出现的并发问题。

我认为这是一种实现“穷人垃圾收集”的尝试。以下是如何在批处理情况下工作:

  • 您将获得一批新的工作,并开始处理
  • 您可以一边分配内存,一边忽略取消分配内存的需要
  • 批处理结束后,您可以调用
    DestructAll_XYZ
    来清理混乱
  • 您的系统已准备好下一批

这种方法既快又脏,所以我绝对建议在某个时候把它清理干净。至少我会去掉静态集合,用更可控的东西来代替它们——比如说,
BatchMemoryContext
,其中包含所有相关集合。通过这种方式,您可以严格控制对象的生命周期,还可以避免并发性可能出现的问题。

@BenVoigt是的,您是对的,我的错。那么,这种模式或多或少是为了方便不需要调用每个实例的析构函数吗?这看起来像穷人的垃圾收集,在处理批处理执行时可能很有用:运行批处理时不必担心释放内存,完成后调用
DestructAll_A
。现在,内存被释放,您可以进行下一批处理了。另一个问题是,这是否用于多线程程序,并且
std::set
不受数据争用的保护。正如@Ben Voigt在他的回答中指出的,使用这种模式,您无法在堆栈上创建
a
的实例:您将得到一个错误(或UB)稍后调用
析构函数A
时。请参阅我对他的回答的评论,让编译器强制执行此操作requirement@mSours不,那是不可能的,你需要一个明确的电话。不过,它可能来自某个析构函数,所以可能不容易看到。@BenVoigt是的,你是对的,我的错。那么,这种模式或多或少是为了方便不需要调用每个实例的析构函数吗?这看起来像穷人的垃圾收集,在处理批处理执行时可能很有用:运行批处理时不必担心释放内存,完成后调用
DestructAll_A
。现在,记忆又恢复了