【发布时间】:2010-11-06 23:13:28
【问题描述】:
我有一系列的类A、B,...有很多派生类是在我不想更改的模块中创建的。
此外,我至少有一个类Z,每当A(或派生类)类型的对象创建或销毁时都必须通知它>。未来可能会有更多的类,Y,X,想要观察不同的物体。
我正在寻找一种方便的方法来解决这个问题。 乍一看,这个问题似乎微不足道,但我现在有点卡住了。
我想出的是两个基类 SpawnObserver 和 SpawnObservable,它们应该可以完成这项工作,但出于几个原因,我对它们非常不满意(请参阅附件对这些类的简化)。
-
当
Z收到通知时,由于创建基类的顺序,实际对象尚未或不再存在/被摧毁。尽管可以在销毁对象时比较指针(以将它们从Z中的某些数据结构中删除),但这在创建时不起作用,并且在具有多重继承时肯定不起作用。李> - 如果您只想观察一堂课,比如
A,您总是会收到全部通知(A、B、...)。 - 您必须通过所有类显式地 if/else,因此您必须知道所有从
SpawnObservable继承的类,这非常糟糕。
这里是类,我试图将它们精简为最基本的功能,你需要知道这些才能理解我的问题。简而言之:您只需从 SpawnObservable 继承,然后 ctor/dtor 负责通知观察者(嗯,至少,这是我想要拥有的)。
#include <list>
#include <iostream>
class SpawnObservable;
class SpawnObserver {
public:
virtual void ctord(SpawnObservable*) = 0;
virtual void dtord(SpawnObservable*) = 0;
};
class SpawnObservable {
public:
static std::list<SpawnObserver*> obs;
SpawnObservable() {
for (std::list<SpawnObserver*>::iterator it = obs.begin(), end = obs.end(); it != end; ++it) {
(*it)->ctord(this);
}
}
~SpawnObservable() {
for (std::list<SpawnObserver*>::iterator it = obs.begin(), end = obs.end(); it != end; ++it) {
(*it)->dtord(this);
}
}
virtual void foo() {} // XXX: very nasty dummy virtual function
};
std::list<SpawnObserver*> SpawnObservable::obs;
struct Dummy {
int i;
Dummy() : i(13) {}
};
class A : public SpawnObservable {
public:
Dummy d;
A() : SpawnObservable() {
d.i = 23;
}
A(int i) : SpawnObservable() {
d.i = i;
}
};
class B : public SpawnObservable {
public:
B() { std::cout << "making B" << std::endl;}
~B() { std::cout << "killing B" << std::endl;}
};
class PrintSO : public SpawnObserver { // <-- Z
void print(std::string prefix, SpawnObservable* so) {
if (dynamic_cast<A*>(so)) {
std::cout << prefix << so << " " << "A: " << (dynamic_cast<A*>(so))->d.i << std::endl;
} else if (dynamic_cast<B*>(so)) {
std::cout << prefix << so << " " << "B: " << std::endl;
} else {
std::cout << prefix << so << " " << "unknown" << std::endl;
}
}
virtual void ctord(SpawnObservable* so) {
print(std::string("[ctord] "),so);
}
virtual void dtord(SpawnObservable* so) {
print(std::string("[dtord] "),so);
}
};
int main(int argc, char** argv) {
PrintSO pso;
A::obs.push_back(&pso);
B* pb;
{
std::cout << "entering scope 1" << std::endl;
A a(33);
A a2(34);
B b;
std::cout << "adresses: " << &a << ", " << &a2 << ", " << &b << std::endl;
std::cout << "leaving scope 1" << std::endl;
}
{
std::cout << "entering scope 1" << std::endl;
A a;
A a2(35);
std::cout << "adresses: " << &a << ", " << &a2 << std::endl;
std::cout << "leaving scope 1" << std::endl;
}
return 1;
}
输出是:
entering scope 1
[ctord] 0x7fff1113c640 unknown
[ctord] 0x7fff1113c650 unknown
[ctord] 0x7fff1113c660 unknown
making B
adresses: 0x7fff1113c640, 0x7fff1113c650, 0x7fff1113c660
leaving scope 1
killing B
[dtord] 0x7fff1113c660 unknown
[dtord] 0x7fff1113c650 unknown
[dtord] 0x7fff1113c640 unknown
entering scope 1
[ctord] 0x7fff1113c650 unknown
[ctord] 0x7fff1113c640 unknown
adresses: 0x7fff1113c650, 0x7fff1113c640
leaving scope 1
[dtord] 0x7fff1113c640 unknown
[dtord] 0x7fff1113c650 unknown
我想强调一下,我完全清楚为什么我的解决方案的行为方式如此。我的问题是您是否有更好的方法来做到这一点。
编辑
作为这个问题的延伸(并受到下面 cmets 的启发),我想知道: 为什么你认为这是一种糟糕的方法?
作为补充说明:我试图通过这个来完成的是在每个创建的对象中安装一个普通的观察者。
编辑 2
我会接受解决问题 1 的答案(上面列举的粗体)或描述了为什么整个事情是一个非常糟糕的主意。
【问题讨论】:
-
您可以对观察到的对象使用智能引用,还是需要继续使用现有类型
A、B等而不彻底重新定义它们? -
修改
A,B, ... 是可能的,只要我不必更改它们的派生类。
标签: c++ constructor destructor observer-pattern