接口在C++中是一种很好的做法吗?
Are interfaces a good practice in C++?
来自Java/python世界,几乎没有C++经验,为了Liskov替换原则和依赖注入,我习惯于使用接口将类的契约与其实现分开。
我不打算讨论 Java 中接口的所有好处,或者为什么引入它们(缺乏多重继承(并且C++不需要(例如,请参阅此处(。 我还发现了如何在C++中拥有等效的Java接口
我的问题更多的是关于这在C++环境中是否是一种好的做法。
据我了解,没有纯虚拟方法,就不可能有等效的接口。这意味着将接口引入C++会在代码中引入一些开销(因为虚拟方法引入了开销(。
因此,基于纯虚拟方法的接口是一件好事吗?有没有其他方法可以实现我不知道的Liskov替换原则和依赖注入?也许使用模板?
例如,谷歌测试很容易模拟虚拟方法,但提出了一种模拟非虚拟方法的方法。
我试图弄清楚我的编码习惯是否仍然适用于我的新C++环境,或者我是否应该适应和改变我的范式。
[根据答案和评论进行编辑]
我得到了我正在寻找的部分答案(即"是/否与参数"(,我想我应该澄清更多我仍在试图弄清楚的内容
- 是否有使用类似接口的设计进行依赖注入的替代方法?
- 反过来说:如果一个人决定采用基于接口的设计,除非速度绝对至关重要,什么时候不想做一个基于纯虚拟方法的界面?
笔记:
- 我想我试图弄清楚我在界面方面是否思维过于狭隘(因此我的编辑正在寻找替代方案(。
- 我在 C++ 11 环境中工作
我想说接口在C++仍然是一个很好的做法。虚拟方法引入的开销很小,正如您将一次又一次地听到的那样,过早优化是一个很大的错误。抽象基类在C++中是一个众所周知的、易于理解的概念,从长远来看,更喜欢可读的常见概念而不是复杂的模板元编程可以极大地帮助你。
话虽如此,我会尽量避免多重继承。它带来了一些棘手的问题,这就是为什么Java明确禁止它进行常规继承的原因。一个简单的谷歌搜索可以给你更多的解释。
如果你有多个不相关的类,并且你想在每个类上调用一个同名的方法(假设foo()
(,那么你可以创建一个模板化的函数来做到这一点,而不是一个接口。
class A {
void foo() {
// do something
}
};
class B {
void foo() {
// do something
}
};
template <typename T>
void callFoo(const T& object) {
object.foo();
}
int main() {
A a;
B b;
callFoo(a);
callFoo(b);
return 0;
}
即使callFoo()
中没有明确的"合约"声明类型必须支持.foo()
,但传递给它的任何对象都必须支持它,否则会出现编译错误。这是在编译时隐藏对象类型的常用方法,也是某些方案接口的替代方法。
在一天结束时,随着你学习更多C++,你将用自己的判断来决定你将如何完成你想要的多态行为。如何做到这一点没有单一的正确答案,就像也没有错误的答案一样。抽象基类和模板鸭子类型都是很好的工具,用途略有不同。
- 将错误返回给调用方而不是立即在 C++ 中抛出错误是否是一种好的做法
- 寻找一种更好的方法来表示无符号字符数组
- 将相同共享指针的副本存储在不同的向量中是否是一种好的做法?
- 使用移位的无符号数字作为数组的索引号是一种很好的做法
- C++|以一种很好的方式将树(不一定是二进制的)打印到stdout
- 接口在C++中是一种很好的做法吗?
- 为许多类可能需要的所有常量变量制作独立的头文件是否是一种很好的做法?
- 有没有一种很好的方法来实现具有默认失败情况的条件类型?
- 是否有一种很好的方法可以将STD :: Minmax(A,B)分配给STD :: TIE(A,B)
- 有没有一种很好的跨平台方法可以使用CMake包含外部C++库
- 创建对象的副本以进行多重比较是一种很好的做法
- 如何正确地对数据开始指针进行类型转换(这是一种很好的做法吗)
- 在析构函数中调用函数是一种很好的做法
- 矢量在C++对一种方法没有反应,但对其他方法很好
- 使用 std::正则表达式进行简单的 RX 是一种很好的做法
- C++内置的封闭就是一种很好的做法
- 指向类的C++指针,调用释放类实例的函数,就是一种很好的做法
- 这是一种很好的OO方法(c++)
- 如何以一种很好的方式禁用OpenMP指令
- 在 std::map 的值中使用非 ptr 是一种很好的做法吗