C++ 习惯上把算法放到课堂上可以吗?
我有一个复杂的算法。这将使用许多变量,在初始化时计算帮助器数组,并同时计算数组。由于算法很复杂,我将其分解为几个函数 现在,我实际上不明白从惯用的方式来看这是一个怎样的类;我的意思是,我只是习惯于将算法作为函数。其用法是:C++ 习惯上把算法放到课堂上可以吗?,c++,algorithm,class,C++,Algorithm,Class,我有一个复杂的算法。这将使用许多变量,在初始化时计算帮助器数组,并同时计算数组。由于算法很复杂,我将其分解为几个函数 现在,我实际上不明白从惯用的方式来看这是一个怎样的类;我的意思是,我只是习惯于将算法作为函数。其用法是: Calculation calc(/* several parameters */); calc.calculate(); // get the heterogenous results via getters 另一方面,将其放入类中具有以下优点: 我不必将所有变量传递给
Calculation calc(/* several parameters */);
calc.calculate();
// get the heterogenous results via getters
另一方面,将其放入类中具有以下优点:
- 我不必将所有变量传递给其他函数/方法 在算法开始时初始化的数组可以在整个类中的每个函数中访问 我的代码更短(imo)更清晰
提前非常感谢 这实际上归结为:算法是否需要访问不应该是公共的类的私有区域?如果答案是肯定的(除非你愿意根据具体情况重构你的类接口),你应该使用一个成员函数,如果不是,那么一个自由函数就足够了
以标准库为例。大多数算法都是作为自由函数提供的,因为它们只访问类的公共接口(例如,使用标准容器的迭代器)。我想说,将算法(或者更好的,计算)表示为类是非常惯用的。OOP中对象类的定义之一是“数据和对该数据进行操作的函数”。compex算法及其输入、输出和中间数据与该定义完美匹配
我自己做过几次,它大大简化了(人工)代码流分析,使整个过程更易于推理、调试和测试。您是否需要每次以完全相同的顺序调用完全相同的函数?那么,您不应该要求调用代码来执行此操作。将算法拆分为多个函数是可以的,但我仍然需要一次调用下一次,然后再调用下一次,以此类推,并在过程中传递结果/参数的结构。类不适合一次性调用某个过程
我对一个类执行此操作的唯一方法是,如果该类本身封装了所有输入数据,然后您对其调用
myClass.nameofMyGorithm()
,以及其他可能的操作。然后是数据+操纵器。但仅仅是操纵者?是的,我不太确定。这取决于你想封装什么样的算法。一般来说,我同意约翰·卡马克的观点:“我的观点。”
我有一个复杂的算法。这将使用许多变量,在初始化时计算帮助器数组,并同时计算数组。[…]
现在,我实际上不明白从惯用的方式来看这是一个怎样的类
事实并非如此,但很多人都做和你一样的事情(我也做过几次)
<>而不是为算法创建类,考虑将输入和输出转换为类/结构。
也就是说,而不是:
Calculation calc(a, b, c, d, e, f, g);
calc.calculate();
// use getters on calc from here on
你可以写:
CalcInputs inputs(a, b, c, d, e, f, g);
CalcResult output = calculate(inputs); // calculate is now free function
// use getters on output from here on
这不会产生任何问题,并且执行相同(实际上更好)的数据分组。如果客户机代码的抽象是一种算法,那么 可能希望保留一个纯功能接口,而不是 在那里介绍其他类型。这是很常见的 另一方面,在源代码中实现这样的函数 文件,该文件为其定义公共数据结构或类 内部使用,因此您可能有:
double calculation( /* input parameters */ )
{
SupportClass calc( /* input parameters */ );
calc.part1();
calc.part2();
// etc...
return calc.results();
}
根据代码的组织方式,SupportClass
将
在源文件中的未命名命名空间中(可能是最
常见情况),或在“专用”标题中,仅由
在现代C++中涉及到的资源的来源,的区别已经被侵蚀了不少。即使通过ANSI之前语言的运算符重载,您也可以创建一个实例在语法上类似于函数的类:
struct Multiplier
{
int factor_;
Multiplier(int f) : factor_(f) { }
int operator()(int v) const
{
return v * _factor;
}
};
Multipler doubler(2);
std::cout << doubler(3) << std::endl; // prints 6
这里,doubler
是一个lambda,它本质上是一种声明编译器生成的类的实例的好方法,该类实现了()
操作符
更准确地再现原始示例,我们需要一个名为乘数
的类似函数的东西,它接受因子
,并返回另一个类似函数的东西,它接受值v
,并返回v*因子
auto multiplier = [] (int factor)
{
return [=] (int v) { return v * factor; };
};
auto doubler = multiplier(2);
std::cout << doubler(3) << std::endl; // prints 6
自动乘法器=[](整数因子)
{
return[=](int v){return v*factor;};
};
自动倍频器=倍频器(2);
std::cout如果你有持久状态,那么一定要创建一个类。我发现有一条规则很有用,那就是总是将参数作为函数参数传递(因为99.9%的bug来自于在共享状态下操作数据)——但是类确实有它们的用途,但我不知道它是否是惯用的,出于完全相同的原因,我一直都喜欢这样。这是一个从带有全局变量的算法的初始粗略变量的方便转换:
的随机数生成引擎是将状态封装在类中的算法的一个很好的例子。“算法”是一个名词,因此是一个对象。回答得好。事实上,我可能会强调,数据和算法应该成为一个类的实现,该类公开了更高级的逻辑。例如,与其使用“二进制搜索树的算法”,不如制作一个高级“地图”,等等@KerrekSB Yup,如果这是一个可以用来表达高级概念的算法,我100%同意。OTOH,我认为这更像是在一个复杂的模拟框架内“计算ODE集成”。在这里,我会说计算是更高层次的逻辑。我肯定会说,在我的例子中,计算本身就是高层次的
auto multiplier = [] (int factor)
{
return [=] (int v) { return v * factor; };
};
auto doubler = multiplier(2);
std::cout << doubler(3) << std::endl; // prints 6