Как я могу узнать адрес объекта владельца в C ++? - PullRequest
7 голосов
/ 02 апреля 2009

Я хотел бы создать в C ++ класс Notifier, который я буду использовать в других объектах для уведомления различных владельцев, когда объект будет уничтожен.

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 ]

6 голосов
/ 02 апреля 2009
3 голосов
/ 02 апреля 2009

Ответ ФА. - хорошее начало. Однако это не решает проблему наличия нескольких владельцев одного типа. Одно из решений заключается в том, чтобы уведомитель сохранял список владельцев вместо одного. Вот быстрая реализация, чтобы показать идею:

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()) { }
};

В случае, когда у вас много разных типов владельцев, это решение может стать довольно сложным в использовании. В этом случае вам, возможно, захочется взглянуть на библиотеки повышения метапрограммирования ( MPL , Fusion ), с помощью которых вы можете получить код, позволяющий вам делать такие вещи:

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

Однако реализация этого решения будет немного дольше, чем предыдущая.

3 голосов
/ 02 апреля 2009

Это был бы мерзкий взлом и, вероятно, не гарантированно сработает, но вот мысль Я не рекомендую это .

Предположим, у вас есть макет, подобный описанному ниже:

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));

В основном предполагается, что _notifier имеет некоторое фиксированное смещение в классе Owned. Следовательно, адрес Owned равен адресу _notifier минус это же смещение.

Еще раз, это неопределенное поведение, которое я бы не рекомендовал, но могло бы сработать.

2 голосов
/ 02 апреля 2009

Или как то так:

Наследуйте от вашего уведомителя и добавьте параметр 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 )
    {}
};
1 голос
/ 02 апреля 2009

Частью решения было бы иметь Owned наследование от Notifier. Таким образом, адрес уничтоженного объекта просто "this" ...

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

Но как обращаться с несколькими «владельцами» из одного класса? Как можно наследовать несколько раз от «одного и того же класса»?

Благодаря ответу fa , вот решение, которое я искал:

#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;
}

Спасибо!

0 голосов
/ 02 апреля 2009

Я очень сомневаюсь в этом. Уведомитель не может знать, что он использовался в композиции. Что если я сделаю

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

Хотелось бы, чтобы меня ошиблись, но я действительно сомневаюсь, что это выполнимо без явного предоставления уведомителю дополнительной информации.

...