【问题标题】:Good practice for downcasting return type of inherited functions向下转换继承函数的返回类型的良好做法
【发布时间】:2011-06-27 12:41:30
【问题描述】:

我有一个Matrix-Class 派生自Vector-Class,为了获得额外的功能和更好的使用,我有一个派生自Vector 类的Vector3-Class。我现在的问题是Vector-class 实现了一个函数,例如+normalizeVector:,它返回一个新分配的Vector-instance。子类Vector3 应该继承这两个函数,但是继承会导致函数原型返回Vector-instance 而不是Vector3-instance。这就是继承的工作原理,但是有一个好的做法来解决这个问题吗?一个天真的解决方案是创建 Vector3 也是一个新类,它是 NSObject 的子类,但我希望 Vector- 和 Vector3-instances 可以轻松交互。

这里是一个代码示例:

@interface Vector : NSObject {
....
}
+(Vector*) normalizeVector:(Vector*)v; //returns a new allocated Vector-instance
-(Vector*) normalize; //normalizes itself and returns itself
-(Vector*) otherFunction;
@end

@interface Vector3 : Vector {
}
-(Vector3*) specialFunction;
@end

该代码的用法:

Vector3 *v3 = ...;
[[v3 normalize] specialFunction]; //Compiler gives me a warning because Vector has no specialFunction. Cast would help
[[Vector3 normalizeVector:v3] specialFunction]; //Compiler gives me a warning and during runtime it will crash because a `Vector` doesn't implement specialFunction

对Vector3 的强制转换会有所帮助,但使用起来并不好,而且静态函数+normalizeVector: 也会失败,因为在该静态函数中分配了Vector-instance,而没有指针强制转换帮助。

有什么想法吗?或其他方法/其他建模?

编辑: 由Vector3 继承的静态函数 normalizeVector 的代码:

@implementation Vector
...
+(Vector*) normalizeVector:(Vector *)v
{
    unsigned int dim = vector_max(v.cols, v.rows);
    Vector *res = [[[Vector alloc]initAsColumnVectorWithDim:dim] autorelease];
    [Vector normalizeVector:v destination:res]; // this does only the logic: calc length and divide each component by the len and store at the vector passed to destination
    return res;
}
@end

【问题讨论】:

  • 显示 +normalizeVector 的代码:
  • @fakeAccount22:看看我的编辑

标签: objective-c xcode inheritance


【解决方案1】:

您会注意到 -init 方法总是返回类型 id -(id)init {..} 正是因为这个原因。

另外,而不是 [Vector alloc] - 正如您注意到的那样,您实际上并不知道您在运行时所在的类(它可能是子类),因此只需使用 @987654322 @ 其中 self 是当前 Class 因为您在 Class 方法中。所以,如果你这样做 [Vector3 normalizeVector:v] self 是 Vector3 而如果你这样做 [Vector normalizeVector:v] self 是 Vector。

尝试将您的 +normailzeVector: 方法调整为

+ (id)normalizeVector:(Vector *)v {
    unsigned int dim = vector_max(v.cols, v.rows);
    id res = [[[self alloc] initAsColumnVectorWithDim:dim] autorelease];
    [self normalizeVector:v destination:res];
    return res;
}

请注意,+ (id)normalizeVector: 不是函数,也绝对不是静态函数。它是一个类方法,它只是有助于使术语正确。

【讨论】:

  • 是的,这会有所帮助,因为我直接覆盖了类方法,但使用我的代码也会导致强制转换,而且我知道在 Vector3-instance 上调用的 +normalizeVector: 返回 Vector3 (由于用 id-return-type 覆盖)。我认为您对 init-methods 中的 id-return-type 是正确的。这是同样的问题,只能通过返回最通用的类​​型来解决。
  • 感谢您的回答和编辑。这有点帮助,但我认为没有强制转换就没有解决方案。
  • 强制转换有时是一种必要的邪恶。如果你想要严格的类型检查,你必须强制转换。如果您不想要它,请返回类型 id。 IMOA,您没有通过使用 - (Vector *)normalize 之类的方法来帮助自己;修改目标并返回对自身的引用。 “就地修改”的方法不应返回值。我知道您可能喜欢将方法调用链接在一起,但这不是惯用的 Cocoa。
  • 是的,你是对的。我通常不这样做,但我想构建一个内存高效的矩阵、向量、......类,因为在相对较高的频率下会有很多计算(所以我需要数据覆盖方法)。我还需要计算长线性方程,我想以代码在最后可读的方式对它们进行编码:[[MyMatrix multiplyPost:[anotherMatrix invert]] sensorVector] ....(所以我需要自返回方法- nicetohave 功能)
  • 链接方法调用不会带来任何效率上的好处,但您是否认为它更具可读性是您的调用。
【解决方案2】:

在这种情况下,我会将 normaliseVector 设为实例方法。所以不是

向量 *newV = [向量​​ normalizeVector:v];

打电话

向量 *newV = [v normalizeVector];

然后你可以为 Vector 和 Vector3 生成不同的 normalizeVector

编辑: 对于 [[v3 normalize] specialFunction];存在一个问题,即 normalize 有时会返回一个 specialFunction 不起作用的对象 - 即它仅在 v3 是 Vector3 时才有效。所以在这种情况下,你有额外的信息,所以需要演员或者 Vector3 normailze 与 Vector 不同。在这种情况下,我将在 Vector3 上生成一个覆盖方法来调用 normalize] specialFunction],以便强制转换在 Vector3 特定代码中。

【讨论】:

  • 但这并不能解决[[v3 normalize] specialFunction];中的警告我认为
  • @thomas:不适用于-(Vector3*)normalize; in Vector3。
  • 感谢您的回答!与您的编辑有关:是的,我也想过相同的方向,但我的 specialFunction 可以是任意的,例如 crossProduct、dotProduct、multiplyScalar,... cast 但随后有两个函数仅在返回类型上有所不同,这使编译器不满意。我也可以将包装函数命名为略有不同,但我的代码的用户必须猜测要使用哪个函数。
  • 问题是如果在 Vector 上调用特殊函数会发生什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-13
相关资源
最近更新 更多