我正在开发一个C ++应用程序,它内部有一些控制器对象,这些对象是经常创建和销毁的(使用new)。这些控制器必须将自己注册到另一个对象(让我们称之为controllerSupervisor),并在它们被破坏时取消注册。
当我退出应用程序时,我现在遇到的问题是:由于破坏的顺序不是确定性的,所以恰好单个controllerSupervisor实例在(某些)控制器本身之前被破坏,并且当它们调用时在析构函数中取消注册方法,它们在已经被破坏的对象上执行。
到目前为止我提出的唯一想法(有一个很大的冷,所以这可能并不重要)并没有将controllerSupervisor作为堆栈上的全局变量,而是在堆上(即使用new)。但是在那种情况下我没有地方可以删除它(这都是第三方类型的库)。
有关可能的选项的任何提示/建议将不胜感激。
答案 0 :(得分:5)
自动变量的破坏顺序(包括您在函数中使用的“普通”局部变量)的顺序与它们的创建顺序相反。所以将controllerSupervisor置于顶部。
全局变形的破坏顺序也与它们的创建相反,而这又取决于它们的定义顺序:后来定义的对象是在以后创建的。但要注意:不保证以任何定义的顺序创建在不同.cpp文件(翻译单元)中定义的对象。
我认为你应该考虑使用Mike推荐的方式:
!= 0
的指针)。如果是,那么什么也没做。否则通知主管。因为我想可能有一个没有控制器连接的主管(如果只是临时的),智能指针不能用于自动销毁主管。
答案 1 :(得分:5)
在Alexandrescu的现代C ++设计(Chaper 6,Singletons)中,基本上有一章关于这个主题。他定义了一个单例类,它可以管理依赖关系,甚至可以管理单例本身。
全书强烈推荐BTW。
答案 2 :(得分:2)
您可以使用观察者模式。控制器向其主管传达它正在被销毁的信息。并且主管在销毁时将其传达给它的孩子。
答案 3 :(得分:1)
一些建议:
使controllerSupervisor成为一个单例(或将其包装在为此目的创建的单个对象中),通过返回指针的静态方法访问,然后注册对象的dtors可以调用静态访问器(在应用程序关闭的情况下,controllerSupervisor已被销毁将返回NULL)并且那些对象可以避免在这种情况下调用取消注册方法。
使用new在堆上创建controllerSupervisor,并使用boost::shared_ptr<>
之类的东西来管理它的生命周期。在单例的静态访问器方法中分发shared_ptr<>
。
答案 4 :(得分:1)
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.
答案 5 :(得分:0)
您可以根据具体情况做以下任何事情。
答案 6 :(得分:0)
您可以查看使用已注册控制器的数量作为实际删除的哨兵。
删除调用只是一个请求,您必须等到控制器取消注册。
如上所述,这是观察者模式的一种用途。
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();
答案 7 :(得分:0)
这不是很优雅,但你可以这样做:
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.supervisor的全局引用来避免control.
前缀。
协调器的主管成员在析构函数运行之前不会被销毁,因此您可以保证主管将比控制器更长。
答案 8 :(得分:0)
让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);
}
};
答案 9 :(得分:0)
让主管负责破坏控制器?
答案 10 :(得分:0)
好的,正如其他地方所建议的那样,使主管成为单身人士(或类似的受控对象,即作用于会话)。
如果需要,在单身人士周围使用适当的警卫(领衔等)。
// -- 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)
{}
}
答案 11 :(得分:0)
虽然丑陋,但这可能是最简单的方法:
只是试着抓住取消注册的电话。您不必更改大量代码,因为应用程序已经关闭它并不是什么大问题。 (或者还有其他批准关闭的命令吗?)
其他人指出了更好的设计,但这个很简单。 (又丑陋)
在这种情况下,我更喜欢观察者模式。
答案 12 :(得分:0)
您可以使用事件来表示控制器的销毁
在Supervisor的析构函数中添加WaitForMultipleObjects,它将一直等到所有控制器都被销毁。
在控制器的析构函数中,您可以引发控制器退出事件。
您需要为每个控制器维护全局的Exit事件句柄数组。
答案 13 :(得分:0)
当所讨论的变量都适合一个文件(“翻译单元”)时,C ++标准规定了初始化/销毁的顺序。跨越多个文件的任何内容都变得不可移植。
我会提出让主管销毁每个控制器的建议。这是线程安全的,只有主管告诉任何人自己摧毁(没有人自己摧毁自己),所以没有竞争条件。你还必须避免任何死锁的可能性(提示:确保控制器一旦被告知就可以自行销毁,而不需要来自主管的其他)。
即使控制器需要在程序结束之前销毁(也就是控制器可能是短暂的),然后他们(或其他人),也可以使这个线程安全。
首先,如果我决定摧毁自己并且微秒后主管决定摧毁我并告诉我,那可能不是一种竞争条件。
其次,如果您担心这种竞争条件,您可以通过要求所有销毁请求通过主管进行修复。我想破坏自己,我要么告诉主管告诉我,要么我向主管注册这个意图。如果其他人 - 包括主管 - 希望我被摧毁,他们会通过主管这样做。
答案 14 :(得分:0)
当我读到这个问题的标题时,我立刻问自己“如果有一种方法可以确保任何物体最终被破坏(破坏?),那么如果两个物体采用这种方法会发生什么?”