代码之家  ›  专栏  ›  技术社区  ›  bitmask

arb的静态ctor/dtor观测器。C++类

  •  1
  • bitmask  · 技术社区  · 14 年前

    我有一系列的课程 A B , ... 其中有许多派生类,这些派生类是在模块内创建的,我不想更改。

    Z ,当类型为 一个 (或派生类)是 摧毁 Y , X 想观察不同物体的人。

    我在找一个方便的方法来解决这个问题。 乍一看,这个问题似乎微不足道,但我现在有点卡住了。

    SpawnObserver SpawnObservable 他们本该做这项工作,但我对他们很不满意有几个原因(见附件中这些类的简化)。

    1. 什么时候? Z轴 还没有 不再是了 Z轴 )这在创建时不起作用,在您具有多重继承时肯定不起作用。
    2. 如果你只想看一节课,就说 一个 一个 , ...).
    3. 必须在所有类中显式地if/else,因此必须 知道 可观察的产卵 ,这很糟糕。

    可观察的产卵 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
    

    我想强调的是,我非常清楚 为什么?

    编辑

    为什么你认为这是一个可怕的方法?

    另请注意:我要完成的是在每个创建的对象中安装一个普通的观察者。

    我将接受解决问题1的答案(上面列举的粗体) 描述为什么整件事是一个非常糟糕的主意。

    3 回复  |  直到 14 年前
        1
  •  2
  •   Puppy    14 年前

    使用奇怪的重复模板模式。

    template<typename T> class watcher {
        typename std::list<T>::iterator it;
        watcher();
        ~watcher();
        void ctord(T*);
        void dtord(T*);    
    };
    template<typename T> class Observer {
    public:
    
        typedef std::list<T*> ptr_list;
        static ptr_list ptrlist;
        typedef typename ptr_list::iterator it_type;
        it_type it;
    
        typedef std::list<watcher<T>*> watcher_list;
        static watcher_list watcherlist;
        typedef typename watcher_list::iterator watcher_it_type;
    
        Observer() {
           ptrlist.push_back(this);
           it_type end = ptrlist.end();
           end--;
           it = end;
           for(watcher_it_type w_it = watcherlist.begin(); w_it != watcherlist.end(); w_it++)
               w_it->ctord(this);
        }
        ~Observer() {
            ptrlist.erase(it);
           for(watcher_it_type w_it = watcherlist.begin(); w_it != watcherlist.end(); w_it++)
               w_it->ctord(this);
        }
    };
    class A : public Observer<A> {
    };
    class B : public Observer<B> {
    };
    class C : public A, public B, public Observer<C> {
        // No virtual inheritance required - all the Observers are a different type.
    };
    template<typename T> watcher<T>::watcher<T>() {
        Observer<T>::watcherlist.push_back(this);
        it = watcherlist.end();
        it--;         
    }
    template<typename T> watcher<T>::~watcher<T>() {
        Observer<T>::watcherlist.erase(it);
    }
    template<typename T> void watcher<T>::ctord(T* ptr) {
        // ptr points to an instance of T that just got constructed
    }
    template<typename T> void watcher<T>::dtord(T* ptr) {
        // ptr points to an instance of T that is just about to get destructed.
    }
    

    不仅如此,你还可以继承 Observer 多次使用这种技术,如两次 Observer<X> Observer<Y> 是不同的类型,因此不需要钻石继承或类似的东西。另外,如果您需要不同的功能 观察者<X> 观察员<Y> ,你可以专攻。

    编辑@评论:

    Observer<A> Observer<B>

    至于ctord和dtord,我实际上不知道它们执行什么功能。您可以使用Observer::ptrlist获取任何特定类型的列表。

    再次编辑:哦,我明白了。对不起,我再编辑一下。伙计,这是我写过的最可怕的代码。你应该认真考虑不需要它。为什么不让那些需要被告知的对象去做他们的创造呢?

        2
  •  1
  •   please delete me please delete me    14 年前

    如果您不介意执行您的实际操作(除了记帐,我的意思是)或者检查每个对象的构造函数或析构函数之外的列表,那么您可以让它(重新)只在操作即将执行时构建最小列表。这使您有机会使用完全构造的对象,并使问题2更容易解决。

    Observable 构造函数,可能在单个对象的构造过程中多次。另一个是 void * ,指向对象最派生的部分--使用 dynamic_cast<void *>

    可观察的 基,每一个都试图从列表中删除自己,当它到达完整的列表时,只有一个会成功——但这很好,因为每个基都和该对象的任意基一样好。

    你的全部物品清单,可以用简单明了的方式 std::map 空的* 每一个 Observable * 可观察的* 可观察的 析构函数)

    typedef std::map<Observable *, void *> AllObjects;
    AllObjects allObjects;
    

    allObjects

    std::set<Observable *> recentlyConstructedObjects;
    

    可观察的 构造函数,将新对象添加到挂起对象列表中:

    recentlyConstructedObjects.insert(this);
    

    可观察的 析构函数,移除对象:

    // 'this' may not be a valid key, if the object is in 'allObjects'.
    recentlyConstructedObjects.erase(this);
    
    // 'this' may not be a valid key, if the object is in 'recentlyConstructedObjects',
    // or this object has another Observable base object and that one got used instead.
    allObjects.erase(this);
    

    在你要做你的事情之前,更新 ,如果自上次更新以来构造了任何对象:

    if(!recentlyConstructedObjects.empty()) {
        std::map<void *, Observable *> newObjects;
        for(std::set<Observable *>::const_iterator it = recentlyConstructedObjects.begin(); it != recentlyConstructedObjects.end(); ++it)
            allObjectsRev[dynamic_cast<void *>(*it)] = *it;
    
        for(std::map<void *, Observable *>::const_iterator it = newObjects.begin(); it != newObjects.end(); ++it)
            allObjects[it->second] = it->first;
    
        recentlyConstructedObjects.clear();
    }
    

    现在您可以访问每个对象一次:

    for(std::map<Observable *,void *>::const_iterator it = allObjects.begin(); it != allObjects.end(); ++it) {
        // you can dynamic_cast<whatever *>(it->first) to see what type of thing it is
        //
        // it->second is good as a key, uniquely identifying the object
    }
    

    好。。。既然我写了这些,我不确定这是否能解决你的问题。尽管如此,考虑一下还是很有意思的。

    dynamic_cast ,当然,如果在对象的构造过程中调用它,就没有多大用处,这当然是奇怪地重复出现的东西的优点:在基的构造过程中知道派生类型。

    typeid ,可能是,或者是特征——当你构建更大的列表时,把它们合并在一起。这应该很简单,因为您将知道哪些基本对象对应于同一派生对象。这可能有助于解决第3个问题,具体取决于您尝试执行的操作

        3
  •  0
  •   doron    14 年前