c – 隐式vs显式接口

在下面的示例中,使用隐式接口(案例2和3;模板)与使用显式接口(案例1;指向抽象类的指针)的优缺点是什么?

代码不会改变:

class CoolClass
{
public:
  virtual void doSomethingCool() = 0;
  virtual void worthless() = 0;
};

class CoolA : public CoolClass
{
public:
  virtual void doSomethingCool()
  { /* Do cool stuff that an A would do */ }

  virtual void worthless()
  { /* Worthless, but must be implemented */ }
};

class CoolB : public CoolClass
{
public:
  virtual void doSomethingCool()
  { /* Do cool stuff that a B would do */ }

  virtual void worthless()
  { /* Worthless, but must be implemented */ }
};

情况1:一个非模板化的类,它接受一个提供显式接口的基类指针:

class CoolClassUser
{
public:  
  void useCoolClass(CoolClass * coolClass)
  { coolClass.doSomethingCool(); }
};

int main()
{
  CoolClass * c1 = new CoolA;
  CoolClass * c2 = new CoolB;

  CoolClassUser user;
  user.useCoolClass(c1);
  user.useCoolClass(c2);

  return 0;
}

情况2:模板类型提供隐式接口的模板化类:

template <typename T>
class CoolClassUser
{
public:  
  void useCoolClass(T * coolClass)
  { coolClass->doSomethingCool(); }
};

int main()
{
  CoolClass * c1 = new CoolA;
  CoolClass * c2 = new CoolB;

  CoolClassUser<CoolClass> user;
  user.useCoolClass(c1);
  user.useCoolClass(c2);

  return 0;
}

情况3:一个模板类,其模板类型提供了一个隐式接口(这次,不是从CoolClass派生的:

class RandomClass
{
public:
  void doSomethingCool()
  { /* Do cool stuff that a RandomClass would do */ }

  // I don't have to implement worthless()! Na na na na na!
};

template <typename T>
class CoolClassUser
{
public:  
  void useCoolClass(T * coolClass)
  { coolClass->doSomethingCool(); }
};

int main()
{
  RandomClass * c1 = new RandomClass;
  RandomClass * c2 = new RandomClass;

  CoolClassUser<RandomClass> user;
  user.useCoolClass(c1);
  user.useCoolClass(c2);

  return 0;
}

情况1要求传入useCoolClass()的对象是CoolClass的子对象(并实现无价值()).另一方面,情况2和3将采用具有doSomethingCool()函数的任何类.

如果代码的用户总是很好地继承CoolClass,那么Case 1就具有直观意义,因为CoolClassUser总是希望实现CoolClass.但是假设这个代码将成为API框架的一部分,所以我无法预测用户是否想要子类化CoolClass或者推送他们自己的具有doSomethingCool()函数的类.

一些相关的帖子:

https://stackoverflow.com/a/7264550/635125

https://stackoverflow.com/a/7264689/635125

https://stackoverflow.com/a/8009872/635125

最佳答案
我想到了为什么你更喜欢案例1的一些注意事项:

>如果CoolClass不是纯接口,那么实现的一部分也是继承的(尽管你也可以为Case 2/3提供它,例如以基类的形式);
>如果有理由让CoolClassUser在二进制而不是标题中实现(这不仅是保护,还可以是代码大小,资源控制,集中错误处理等);
>如果你想存储指针并稍后使用它,那么案例1似乎也更好:(a)将它们全部保存在同一个容器中更容易,(b)你还需要存储实际的数据类型,对于案例2/3,我想到的解决方案是在模板包装器的帮助下将其转换为“显式”接口(即案例1).

Case 2/3可能更适合的原因:

>如果你后来认为无价值()现在有价值,并开始使用它,在案例2中,你将得到未实现的类的编译时错误.在案例1中,没有任何内容会提醒您实现这些功能,除非您(幸运)幸运,否则可能会出现运行时错误.
> Case2 / 3可能会有更好的性能,但代价是更大的代码.

在某些情况下,它可能纯粹是个人偏好的问题,无论是您的还是您的用户.

转载注明原文:c – 隐式vs显式接口 - 代码日志