Objective-C的类别(category)特性允许程序员添加原始类定义中未定义的新方法。
我能在C++上实现类似的功能(语言结构或某些技术)吗?
主要问题是统一的方法调用语法(.
或 ->
运算符)。
Objective-C的类别(category)特性允许程序员添加原始类定义中未定义的新方法。
我能在C++上实现类似的功能(语言结构或某些技术)吗?
主要问题是统一的方法调用语法(.
或 ->
运算符)。
让我们考虑以下需要扩展的类:
struct A {
int x, y;
A(int x, int y) : x(x), y(y) {}
};
class B : public A {
public:
B (A &&a) : A(a), someOtherMember(a.x + a.y) {}
// added public stuff:
int someOtherFunction() const { return someOtherMember; }
private:
// added private stuff:
int someOtherMember;
};
A(A &&a)
是A
的移动构造函数。这将把旧的A
移动到新的A
中。从语义上讲,它是一个新对象,但由于移动语义,它可以“窃取”旧A
的内容。我的方法是将旧的A
移动到B
中。由于B
继承自A
,因此必须初始化一个A
。我在B
的某个构造函数中使用A
的移动构造函数,我称之为提升构造函数(我创造了这个词,因为它有效地将现有的A
提升为B
,只要它的移动构造函数编写得很好,就不会触及A
的内容)。 - leemesclass C : public A
的情况,其中有一个从A
移动构造函数。在这种情况下,你拥有的对象可以是A
、B
或C
中的任何一个,但不能同时是B
和C
。 - al45tair另一种选项,虽然某些人可能不认为它是“干净”的选择(但在我看来却是),但仍然可以实现同样的功能,即使用静态类。需要记住的重要一点是,当我们创建成员函数时,实际上发生的是编译器生成一个函数,其中对象(也称为“this”)是第一个参数。因此,我们可以通过相同的方式来扩展我们类的功能,而无需从中导出。
class Something
{
public:
Something()
~Something()
}
// In objective-c you may call this category Something+Utils
class SomethingUtils
{
// You can use a pointer, or a reference here, your call.
static int GetSomethingElse(Something *something, int parameter);
}
这将实现与类别相同的意图:扩展类对象的功能,而不必创建新的派生类。您将无法访问私有或受保护的成员函数和变量,但是在Objective-C中也无法这样做,因此在这方面没有任何损失(如果您正在尝试使用私有或受保护的成员状态,则已完全错过了类别的要点)。 您将无法使用.和->运算符,但在我看来,这比派生新类型仅添加一些实用方法要好得多。
C++有继承来实现这个功能。此外,我已经多次使用以下技巧来扩展由#import "progid:..."指令生成的类:
// This one is part of external framework, or auto-generated, or dllimport, or #import, etc..
class A
{
protected double m_x;
};
// This one is the extension class. Make sure you only add non-virtual methods here.
// Static methods and static data members are OK as well.
class __declspec( novtable ) B: public A
{
public:
double getSquare(){ return m_x * m_x; }
__declspec( property( get = getSquare ) ) double square;
};
// Usage example
double someFunc( A& arg )
{
B& b = static_cast<B&>( arg ); // Note we've not constructed any instance of B, just casted.
return b.square;
}
__declspec
е’Ң property
дёҚжҳҜ ISO ж ҮеҮҶ C++ зҡ„дёҖйғЁеҲҶгҖӮ - Bo Perssonproperty
看起来只是一个方便的包装器,用于封装 getter 和 setter。实际上,当读取 b.square
时,b.square
只是 b.getSquare()
的语法糖。另一件事情(novtable
)似乎是断言不应该使用 vtable;如果你写了虚函数,我猜你会得到编译器错误,因为它们需要一个 vtable。这种方法的问题在于,B
的内存布局必须以 A
开头,如果添加了 vtables 或成员(技术上,vtable 指针就像是一个成员),那么这种情况就不成立了。 - leemes我使用了一种几乎一致的调用约定,与我大约一年前在闪电演讲中提到的一个想法相似:
(如果没有评论,介绍就没那么有意义 - 等到C++部分再看)。
请注意,这些材料并不是要认真对待的 - 尽管不可避免地有些人会这样做;-)