【发布时间】:2018-08-06 13:56:51
【问题描述】:
我想编写一个使用许多参数的函数,我将其称为a、b 和c。我有四种选择在 C++14 中实现它。
对于 2018 年的一个新的现代 C++ 项目,其中哪一种风格最符合 ISO C++ 的理念?其他风格指南推荐哪些风格?
面向对象的风格
class Computer {
int a, b, c;
public:
Computer(int a, int b, int c) : a(a), b(b), c(c) {}
int compute(int) const {
// do something with a, b, c
}
};
...
const Computer computer(a, b, c);
int result = computer.compute(123);
优点:
- C++ 程序员易于掌握
缺点:
- 要在 map 或 fold 操作中计算事物,我们必须使用笨重的
[computer](int input){ return computer.compute(input); }
C 风格
struct ComputeParams {
int a, b, c;
};
int compute(const ComputeParams ¶ms, int input) {
// do something with params.a, params.b, params.c
}
...
const ComputeParams params{a, b, c};
int result = compute(params, 123);
优点:
- C 程序员易于掌握
缺点:
-
compute的详细实现涉及调用params.a而不是a。 - 冗长调用,每次都必须传入一个结构体。
函子风格
struct Computor {
int a, b, c;
int operator()(int input) const {
// do something with a, b, c
}
};
...
const Computor compute{a, b, c};
int result = compute(123);
优点:
- 面向对象风格的所有优点,加上它看起来像一个函数
- 可用于 map、fold 和 for_each 等函数式操作
缺点:
- “函子”这个词看起来很时髦。
功能风格
auto genCompute(int a, int b, int c) {
return [a, b, c](int input) -> int {
// do something with a, b, c
}
}
...
auto compute = genCompute(a, b, c);
int result = compute(123);
优点:
- OCaml 程序员易于掌握
- 可用于 map、fold 和 for_each 等函数式操作
- 技术上与仿函数相同
缺点:
- C++ 和 C 程序员很难掌握
- 由于 lambda 函数是由编译器生成的唯一类型,因此可能需要使用
auto或模板魔术来内联 lambda 函数,或使用std::function来增加性能开销 - 不能接受 vtable 的强大功能和多态性继承
【问题讨论】:
-
注意with uniform function call (pdf),上面的C风格和面向对象的风格变成了同一个东西。
-
“面向对象”和“函子”是一样的。当需要动态多态可调用对象时,只需添加 operator() 并将“doIt”或“compute”方法设置为从基类的(非虚拟)operator() 调用的“私有虚拟”。 OTOH 为什么函数必须在全局命名空间中?最好不要将 any 名称放入全局命名空间中。
-
关于命名空间你是对的;一切都应该在他们自己的命名空间中。我的观点是,班级成员可以免费获得某种“命名空间”。
标签: c++ oop functional-programming c++14 encapsulation