我想在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;
};
我的观点是,由于我有一个密集而复杂的对象图,我想避免将所拥有对象的地址存储在通知程序中。有没有办法改变我的通知程序类,以便它可以从自己的地址推导出拥有对象的地址,以及在编译时计算的偏移量?
另请注意,任何对象都可能需要通知几个“所有者”,可能来自同一个类。
感谢。
答案 0 :(得分:6)
答案 1 :(得分:3)
这将是一个令人讨厌的黑客,可能无法保证工作,但这是一个想法我不推荐这个。
假设您的布局如下所示:
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 :(得分:3)
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)
};
然而,实施此解决方案会比前一个解决方案稍长一些。
答案 3 :(得分:2)
或类似的东西:
从通知程序继承并添加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 )
{}
};
答案 4 :(得分:1)
部分解决方案是从Notifier继承Owned。这样,被破坏对象的地址就是'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;
}
谢谢!
答案 5 :(得分:0)
我非常怀疑。通知程序无法知道它已用于合成中。怎么办
class Foo
{
private:
Notifier _a, _b, _c;
}
我很乐意被证明是错的,但我真的怀疑,如果没有明确地向通知者提供更多信息,这是可行的。