繁体   English   中英

在C ++中强制要破坏某些东西

[英]Forcing something to be destructed last in C++

我正在开发一个C ++应用程序,它内部有一些控制器对象,这些控制器对象是定期创建和销毁的(使用new)。 这些控制器必须将自己注册到另一个对象(让我们称之为controllerSupervisor),并在它们被销毁时取消注册。

当我退出应用程序时,我现在遇到的问题是:由于破坏的顺序不是确定性的,因此在控制器本身(某些)之前,单个controllerSupervisor实例被破坏,并且当它们调用unregister方法时他们的破坏者,他们是在一个已被破坏的物体上这样做的。

到目前为止我提出的唯一想法(有一个很大的冷,所以这可能并不重要)并没有将controllerSupervisor作为堆栈上的全局变量,而是在堆上(即使用new)。 但是在那种情况下我没有地方可以删除它(这都是第三方类型的库)。

任何可能的选项的提示/建议将不胜感激。

自动变量的破坏顺序(包括您在函数中使用的“普通”局部变量)的顺序与它们的创建顺序相反。 所以将controllerSupervisor置于顶部。

全局变量的破坏顺序也与它们的创建相反,而后者又取决于它们的定义顺序:后来定义的对象将在以后创建。 但要注意:不保证以任何定义的顺序创建在不同的.cpp文件(翻译单元)中定义的对象。

我认为你应该考虑使用迈克推荐的方式:

  1. 通过返回指向函数静态管理程序对象的指针,在第一次使用时使用单例模式(因为未定义不同翻译单元中的对象的初始化顺序)来完成创建。
  2. 主管通常被破坏(使用关于破坏功能中的静力学的规则)。 控制器使用管理程序的静态功能取消注册。 那个检查主管是否已经被破坏(检查指针!= 0 )。 如果是,那么什么也没做。 否则通知主管。

因为我想可能有一个没有控制器连接的主管(如果只是临时的),智能指针不能用于自动销毁主管。

在Alexandrescu的Modern C ++ Design(Chaper 6,Singletons)中,基本上有一章关于这个主题。 他定义了一个单例类,它可以管理依赖关系,甚至可以管理单例本身。

BTW强烈推荐全书。

您可以使用Observer模式。 控制器向其主管传达它正在被销毁的信息。 并且主管在破坏时将其传达给它的孩子。

请查看http://en.wikipedia.org/wiki/Observer_pattern

一些建议:

  • 使controllerSupervisor成为一个单例(或将它包装在你为此目的创建的单个对象中),通过返回指针的静态方法访问,然后注册对象的dtors可以调用静态访问器(在应用程序的情况下) shutdown和controllerSupervisor已被销毁将返回NULL)并且那些对象可以避免在这种情况下调用de-register方法。

  • 使用new在堆上创建controllerSupervisor,并使用诸如boost::shared_ptr<>类的东西来管理它的生命周期。 在singleton的静态访问器方法中shared_ptr<>

GNU gcc / g ++为非常有用的类型提供了非可移植属性。 其中一个属性是init_priority ,它定义了构造全局对象的顺序,并因此定义了它们被破坏的相反顺序。 来自男人:

init_priority(PRIORITY)

  In Standard C++, objects defined at namespace scope are guaranteed to be initialized in an order in strict accordance with that of their definitions _in a given translation unit_. No guarantee is made for initializations across translation units. However, GNU C++ allows users to control the order of initialization of objects defined at namespace scope with the init_priority attribute by specifying a relative PRIORITY, a constant integral expression currently bounded between 101 and 65535 inclusive. Lower numbers indicate a higher priority. In the following example, `A' would normally be created before `B', but the `init_priority' attribute has reversed that order: Some_Class A __attribute__ ((init_priority (2000))); Some_Class B __attribute__ ((init_priority (543))); Note that the particular values of PRIORITY do not matter; only their relative ordering. 

您可以根据具体情况执行以下任何操作。

  1. 使用gurin建议的Observer模式。 基本上,主管告知控制器它正在下降......
  2. 让主管“拥有”控制器并在其停机时对其进行破坏负责。
  3. 将控制器保持在shared_pointers中,以便最后一次下载的人将进行真正的破坏。
  4. 管理(控制器的智能指针)控制器和堆栈上的管理程序,这将允许您确定销毁的顺序
  5. 其他 ...

您可以查看使用已注册控制器的数量作为实际删除的哨兵。

删除调用只是一个请求,您必须等到控制器取消注册。

如上所述,这是观察者模式的一种用途。

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
        inDeleteMode_ = true;
        if( controllers_.empty()){
            delete this;
        }
    }

    void deregister(Controller* controller) {
        controllers_.erase(
            remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
        if( inDeleteMode_ && controllers_.empty()){
            delete this;
        }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();

它不是很优雅,但你可以这样做:

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

新的全球:

ControllerCoordinator control;

在构建控制器的任何地方,添加control.supervisor.insert(controller) 你破坏一个地方,添加control.erase(controller) 您可能可以避免control. 通过向control.supervisor添加全局引用来添加前缀。

协调器的主管成员在析构函数运行之后才会被销毁,因此您可以保证主管将比控制器更长久。

让cotrol主管成为一名singelton。 确保控件构造函数在构造期间(而不是后面)获得管理程序。 这保证了控制主管在控制之前完全构建。 在控制管理器析构函数之前,将调用析构函数。

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };

让主管负责破坏控制器怎么样?

好吧,正如其他地方所建议的那样,使主管成为一个单独的(或类似的受控对象,即作用于会话)。

如果需要,在单身人士周围使用适当的防护装置(theading等)。

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
        controller_(NULL)
        {
            controller_ = Controller::create();
        }

    ~ControllerClient() {
        delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {        
        if (!theSupervisor ) {
            theSupervisor = Supervisor();
        }
        return *theSupervisor;
    } 

    void deregister(Controller& controller) {
        remove( controller );
        if( controllers_.empty() ) {
            theSupervisor = NULL;
            delete this;
        }       
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
        return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
        supervisor_->deregister(*this);
        supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
        supervisor_(&supervisor)
        {}
}

虽然丑陋,这可能是最简单的方法:

只是试着抓住取消注册的电话。 您不必更改大量代码,因为应用程序已经关闭它并不是什么大问题。 (或者是否有其他批准关闭的命令?)

其他人指出了更好的设计,但这个很简单。 (又丑)

在这种情况下,我也更喜欢观察者模式。

您可以使用事件来指示控制器的销毁

在Supervisor的析构函数中添加WaitForMultipleObjects,它将等待所有控制器被销毁。

在控制器的析构函数中,您可以引发控制器Exit事件。

您需要为每个控制器维护全局的Exit事件句柄数组。

当有问题的变量都适合一个文件(“翻译单元”)时,C ++标准规定了初始化/销毁的顺序。 任何跨越多个文件的东西都变得不可移植。

我会提出让主管销毁每个控制器的建议。 这是线程安全的,只有主管告诉任何人自己摧毁(没有人自己摧毁自己),所以没有竞争条件。 你还必须避免任何死锁的可能性(提示:确保控制器一旦被告知无需主管的任何其他事情就可以自行销毁)。


即使控制器需要在程序结束之前(即控制器可能是短暂的)然后它们(或其他人)被销毁,也可以使这个线程安全。

首先,如果我决定摧毁自己并且微秒后主管决定摧毁我并告诉我,那可能不是一种竞争条件。

其次,如果您担心这种竞争条件,您可以通过例如要求所有销毁请求通过主管来解决。 我想破坏自己,我要么告诉主管告诉我,要么我向主管注册这个意图。 如果其他人 - 包括主管 - 希望我被摧毁,他们会通过主管这样做。

当我读到这个问题的标题时,我立即问自己“如果有一种方法可以确保任何物体最终被破坏(破坏?),那么如果两个物体采用这种方法会发生什么?”

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM