假设我有一个类MyClass,我想向它添加一些“观察者”行为。然后我可以这样定义类:
class MyClass : public IObserver
{
...
};
现在假设这个“观察者”功能不是直接与类相关的,而是与存储在类中的数据成员相关的。例如,一个数据成员指向另一个类OtherClass,如果它引用的实例被删除,则需要将其设置为空:
class PointerToOtherClass : public IObserver
{
...
};
class MyClass
{
private:
PointerToOtherClass m_ptr;
};
在这种情况下,我们甚至可以使用智能指针编写更简单的代码。
现在假设,如果删除了OtherClass实例,我们也希望删除MyClass,而不只是将指针放为空。所以,让PointerToOtherClass作为观察者已经不够了,MyClass也应该是一个观察者。
但是,这意味着数据成员m_ptr不能单独实现完整的功能(更改其值),但还需要在父类中放置一些功能。
解决方案可以是将指向MyClass的指针传递给PointerToOtherClass成员。如果MyClass随后实现了Observer,那么PointerToOtherClass实例可以轻松地执行“注册”和“注销”MyClass Observer的操作。
template <typename ParentType>
class PointerToOtherClass
{
public:
PointerToOtherClass(ParentType *parent) : m_parent(parent) {}
void setValue (OtherClass *c) { /* unregister/register m_parent */ }
private:
ParentType *m_parent;
};
class MyClass : public IObserver
{
public:
MyClass() : m_ptr(this) {}
private:
PointerToOtherClass m_ptr;
};
尽管这可以正确地工作,并且可以被概括为一种智能指针,但是我们牺牲了4个字节(32位环境),因为数据成员需要指向其父级。如果应用程序中有数百万个MyClass实例,并且MyClass有十个或更多这些数据成员,那么这看起来不算多,但可能是实质性的。
因为m浐ptr是myClass的成员,所以看起来应该可以从m浐ptr的指针开始获取指向myClass的指针。或者,换句话说:PointerToOtherClass中的方法应该能够通过减去MyClass中m_ptr的偏移量,将其“this”指针转换为指向MyClass的指针。
这让我想到用模板来写这个。在以下代码中,模板化hostawarefield模板类通过减去作为模板参数传递的偏移量来访问其父级:
#include <iostream>
typedef unsigned char Byte;
template <typename ParentType,size_t offset>
class HostAwareField
{
public:
ParentType *getParent() const {return (ParentType *)(((Byte *)this)-offset);}
void printParent() {std::cout << "Parent=" << getParent()->m_name << std::endl;}
};
class X
{
public:
X (char *name) : m_name(name) {}
char *m_name;
HostAwareField<X,offsetof(X,m_one)> m_one;
};
void main()
{
std::cout << "X::m_one: offset=" << offsetof(X,m_one) << std::endl;
X x1("Ross");
X x2("Chandler");
X x3("Joey");
x1.m_one.printParent();
x2.m_one.printParent();
x3.m_one.printParent();
}
但是,这并不能编译。它报告以下错误:
test.cpp(18) : error C2027: use of undefined type 'X'
test.cpp(14) : see declaration of 'X'
test.cpp(18) : error C2227: left of '->m_one' must point to class/struct/union/generic type
test.cpp(16) : error C2512: 'HostAwareField' : no appropriate default constructor available
test.cpp(26) : error C2039: 'm_two' : is not a member of 'X'
test.cpp(14) : see declaration of 'X'
test.cpp(32) : error C2662: 'HostAwareField<ParentType,offset>::printParent' : cannot convert 'this' pointer from 'HostAwareField' to 'HostAwareField<ParentType,offset> &'
Reason: cannot convert from 'HostAwareField' to 'HostAwareField<ParentType,offset>'
Conversion requires a second user-defined-conversion operator or constructor
test.cpp(33) : error C2662: 'HostAwareField<ParentType,offset>::printParent' : cannot convert 'this' pointer from 'HostAwareField' to 'HostAwareField<ParentType,offset> &'
Reason: cannot convert from 'HostAwareField' to 'HostAwareField<ParentType,offset>'
Conversion requires a second user-defined-conversion operator or constructor
test.cpp(34) : error C2662: 'HostAwareField<ParentType,offset>::printParent' : cannot convert 'this' pointer from 'HostAwareField' to 'HostAwareField<ParentType,offset> &'
Reason: cannot convert from 'HostAwareField' to 'HostAwareField<ParentType,offset>'
Conversion requires a second user-defined-conversion operator or constructor
test.cpp(36) : error C2039: 'm_two' : is not a member of 'X'
test.cpp(14) : see declaration of 'X'
test.cpp(36) : error C2228: left of '.printParent' must have class/struct/union
test.cpp(37) : error C2039: 'm_two' : is not a member of 'X'
test.cpp(14) : see declaration of 'X'
test.cpp(37) : error C2228: left of '.printParent' must have class/struct/union
test.cpp(38) : error C2039: 'm_two' : is not a member of 'X'
test.cpp(14) : see declaration of 'X'
test.cpp(38) : error C2228: left of '.printParent' must have class/struct/union
如果我更改以下行:
HostAwareField<X,offsetof(X,m_one)> m_one;
到此行:
HostAwareField<X,4> m_one;
然后,这段代码工作正常,但需要我手动“计算”偏移量,如果添加、删除或重新组织数据成员,可能会导致错误。
这意味着尽管我不能自动完成这项工作,但我可以在之后硬编码偏移量(如上面的值4),并执行检查(查看4是否真的是类中m_one的偏移量),但这需要额外的手动检查,使整个系统不防水。
有没有一种方法可以正确地编译上面的源代码?或者还有其他的技巧来实现我想要做的事情?