c++ - 隐式与显式接口(interface)

标签 c++ templates interface

<分区>

在以下示例中,使用隐式接口(interface)(案例 2 和 3;模板)与使用显式接口(interface)(案例 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:采用基类指针的非模板类,该指针提供显式接口(interface):

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:模板类型提供隐式接口(interface)的模板类:

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:一个模板类,其模板类型提供了一个隐式接口(interface)(这次,不是从 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 的子对象(并实现 worthless())。另一方面,情况 2 和 3 将采用具有 doSomethingCool() 函数的任何类。

如果代码的用户总是很好地子类化 CoolClass,那么情况 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 不是纯接口(interface),即部分实现也被继承(尽管您也可以为案例 2/3 提供它,例如以基类的形式);<
  • 如果有理由将 CoolClassUser 以二进制而不是 header 的形式实现(这不仅是为了保护,还可能是代码大小、资源控制、集中式错误处理等);
  • 如果您想存储指针并在以后使用它,那么情况 1 似乎也更好:(a) 将它们全部保存在同一个容器中更容易,并且 (b) 您需要将实际数据类型存储为好吧,对于案例 2/3,想到的解决方案是在模板包装器的帮助下将其转换为“显式”接口(interface)(即案例 1)。

案例 2/3 可能更可取的原因:

  • 如果您后来决定 worthless() 现在有值(value)并开始使用它,在情况 2 中您将遇到未实现的类的编译时错误。在案例 1 中,没有什么会提醒您真正实现这些功能,除非您不走运时可能会出现运行时错误。
  • Case2/3 的性能可能略好,但代价是代码量更大。

在某些情况下,这可能纯粹是您或您的用户的个人偏好问题。

关于c++ - 隐式与显式接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9232948/

相关文章:

使用 static_assert 的 C++ 模板在错误的位置显示编译错误

java - 在 Eclipse Java 中实现多个类的接口(interface)

Java:接口(interface)之前的 protected 方法

json - Golang Unmarshal slice 类型接口(interface)

c++ - 从派生类中的模板化基类使用 typedef/using

C++11 字符串赋值运算符

c++ - 使用指针而不是引用时出现段错误访问指向结构的指针数组

c++ - 为什么我不能向 CComSafeArray 添加 0?

c++ - 通过命令行在 OS X 上编译简单的 Hello World 程序

c++ - 有没有办法在编译时检查 std::initializer_list 参数的数量?