代码之家  ›  专栏  ›  技术社区  ›  Xavier Nodet

如何知道C++中的所有者对象的地址?

  •  7
  • Xavier Nodet  · 技术社区  · 17 年前

    我想在C++中创建一个通知类,当对象被破坏时,我将用它在其他对象中通知不同的持有者。

    template <class Owner>
    class Notifier<Owner> {
    public:
      Notifier(Owner* owner);
      ~Notifier(); // Notifies the owner that an object is destroyed
    };
    
    class Owner;
    
    class Owned {
    public:
      Owned(Owner* owner);
    private:
      Notifier<Owner> _notifier;
    };
    

    还请注意,任何对象都可能必须通知多个“所有者”,可能来自同一类。

    谢谢

    6 回复  |  直到 17 年前
        1
  •  6
  •   Mykola Golubyev    17 年前
        2
  •  3
  •   Evan Teran    17 年前

    这将是一场灾难 肮脏的 黑客,可能不能保证工作,但这里有一个想法 .

    假设您有如下描述的布局:

    template <class Owner>
    class Notifier<Owner> {
    public:
      Notifier(Owner* owner);
      ~Notifier(); // Notifies the owner that an object is destroyed
    };
    
    class Owner;
    
    class Owned {
    public:
      Owned(Owner* owner);
    private:
      Notifier<Owner> _notifier;
    };
    

    如果 _notifier 知道它的名字,它可以计算 Owned 的地址(在 Notifier

    Owned *p = reinterpret_cast<Owned *>(reinterpret_cast<char *>(this) - offsetof(Owned, _notifier));
    

    基本上,假设_通知程序位于所属类中的某个固定偏移量。因此,拥有的地址等于 _通知者 的地址减去相同的偏移量。

    再一次,这是我不推荐的未定义行为,但可能有效。

        3
  •  3
  •   Community Mohan Dere    9 年前

    fa.'s answer 这是一个好的开始。但是,它不能解决具有相同类型的多个所有者的问题。一种解决方案是让通知程序存储所有者列表,而不是单个所有者列表。下面是一个快速实现,以展示这一想法:

    template <typename Owner, typename Owned>
    class Notifier
    {
      protected:
        Notifier()
        {}
    
        // Constructor taking a single owner
        Notifier(Owner & o) 
        { 
            owners.push_back(&o); 
        }
    
        // Constructor taking a range of owners
        template <typename InputIterator>
        Notifier(InputIterator firstOwner, InputIterator lastOwner)
            : owners(firstOwner, lastOwner) {}
    
        ~Notifier()
        {
            OwnerList::const_iterator it = owners.begin();
            OwnerList::const_iterator end = owners.end();
            for ( ; it != end ; ++it)
            {
                (*it)->notify(static_cast<Owned*>(this));
            }
        }
    
        // Method for adding a new owner
        void addOwner(Owner & o) 
        { 
            owners.push_back(&o); 
        }
    
    private:
        typedef std::vector<Owner *> OwnerList;
        OwnerList owners;
    };
    

    class Owner;
    
    class Owned : public Notifier<Owner, Owned>
    {
        typedef Notifier<Owner, Owned> base;
    
        //Some possible constructors:
        Owned(Owner & o) : base(o) { }
    
        Owned(Owner & o1, Owner & o2)
        {
            base::addOwner(o1); //qualified call of base::addOwner
            base::addOwner(o2); //in case there are other bases
        }
    
        Owned(std::list<Owner*> lo) : base(lo.begin(), lo.end()) { }
    };
    

    如果您有许多不同类型的所有者,则此解决方案可能会变得非常难以使用。在本例中,您可能需要查看boost元编程库( MPL , Fusion

    class Owned : public Notifier<Owned, OwnerType1, OwnerType1, OwnerType2>
    {
        Owned(OwnerType1 & o1, OwnerType1 & o2, OwnerType2 & o3) 
            : base(o1,o2,o3)
    };
    

    但是,实现此解决方案将比上一个解决方案稍微长一点。

        4
  •  2
  •   fa.    17 年前

    或者像这样:

    从通知程序继承并添加Owned as template参数。然后,您可以在通知程序中使用自己的方法:

    template < class Owner , class Owned >
    class Notifier
    {
    public:
        Notifier(Owner* owner)
        {}
    
        Owned * owned()
        { return static_cast< Owned * >( this ); }
    
        ~Notifier()
        {
            // notify owner with owned()
        }
    };
    
    class Owner
    {};
    
    class Owned : public Notifier< Owner , Owned >
    {
    public:
        Owned( Owner * owner ) : Notifier< Owner , Owned >( owner )
        {}
    };
    
        5
  •  1
  •   Community Mohan Dere    9 年前

    部分解决方案是从通知程序继承。这样,被销毁对象的地址就是“This”。。。

    class Owned : public Notifier<Owner> {
    public:
      Owned(Owner* owner) 
        : Notifier<Owner>(owner)
      {}
    };
    

    但是如何处理来自同一类的多个“所有者”?如何从“同一类”继承多次?

    fa's answer ,以下是我一直在寻找的解决方案:

    #include <iostream>
    
    template <class Owner, class Owned, int = 0>
    class Notifier {
    public:
      Notifier(Owner* owner)
        : _owner(owner)
      {}
      ~Notifier() {
        _owner->remove(owned());
      }
      Owned * owned(){ 
        return static_cast< Owned * >( this ); 
      }
    
    private:
      Owner* _owner;
    };
    
    class Owner {
    public:
      void remove(void* any) {
        std::cout << any << std::endl;
      }
    };
    
    class Owned : public Notifier<Owner,Owned,1>, Notifier<Owner,Owned,2> {
    public:
      Owned(Owner* owner1, Owner* owner2)
        : Notifier<Owner,Owned,1>(owner1)
        , Notifier<Owner,Owned,2>(owner2)
      {}
    };
    
    int main() {
      std::cout << sizeof(Owned) << std::endl;
      Owner owner1;
      Owner owner2;
      Owned owned(&owner1, &owner2);
      std::cout << "Owned:" << (void*)&owned << std::endl << std::endl;
    }
    

    谢谢

        6
  •  0
  •   unwind    17 年前

    我对此深表怀疑。通知程序无法知道它已在合成中使用。如果我这样做呢

    class Foo
    {
    private:
      Notifier _a, _b, _c;
    }