用于在C ++中的对象之间共享数据的模式

时间:2011-01-25 16:03:21

标签: c++ design-patterns

我已经开始将用FORTRAN编写的高能物理算法迁移到C ++中面向对象的方法。 FORTRAN代码在很多函数中使用了很多全局变量。

我已将全局变量简化为一组输入变量,以及一组不变量(变量在算法开始时计算一次,然后由所有函数使用)。

此外,我已将完整算法划分为三个逻辑步骤,由三个不同的类表示。所以,以一种非常简单的方式,我有这样的事情:

double calculateFactor(double x, double y, double z)
{
    InvariantsTypeA invA();
    InvariantsTypeB invB();

    // they need x, y and z
    invA.CalculateValues();
    invB.CalculateValues();

    Step1 s1();
    Step2 s2();
    Step3 s3();

    // they need x, y, z, invA and invB
    return s1.Eval() + s2.Eval() + s3.Eval();
}

我的问题是:

  • 进行计算时,所有InvariantsTypeXStepX个对象都需要输入参数(而这些参数不仅仅是三个)。
  • 三个对象s1s2s3需要invAinvB个对象的数据。
  • 所有类通过组合使用其他几个类来完成他们的工作,所有这些类也需要输入和不变量(例如,s1有一个成员对象{{需要thetaThetaMatrixx构建的类z的1}}。
  • 我无法重写算法来减少全局值,因为它遵循几个高能物理公式,而那些公式就是这样。

是否有一个很好的模式来分享输入参数和不变量到用于计算结果的所有对象?

我应该使用单身人士吗? (但invB函数估计大约一百万次)

或者我应该在创建对象时将所有必需的数据作为参数传递给它们?(但是如果我这样做,那么数据将在每个类的每个成员对象中的任何地方传递,造成混乱)

感谢。

7 个答案:

答案 0 :(得分:2)

为什么不将不变量作为函数参数传递给具有calculateFactor方法的类的构造函数?

如果单个函数的参数太多(例如,代替(x,y,z)传递3D点,那么也尝试将参数收集在一起),那么只有1个参数而不是3个参数。

答案 1 :(得分:2)

有一个非常简单的模板类可以在C ++中的对象之间共享数据,它被称为shared_ptr。它在新的STL和boost中。

如果两个对象都有一个shared_ptr到同一个对象,则它们可以共享访问它所拥有的任何数据。

在您的特定情况下,您可能不希望这样,但需要一个包含数据的简单类。

class FactorCalculator
{
   InvariantsType invA;
   InvariantsType invB;

public:
   FactorCalculator() // calculate the invariants once per calculator
   {
      invA.CalculateValues();
      invB.CalculateValues();
   }

   // call multiple times with different values of x, y, z
   double calculateFactor( double x, double y, double z ) /*const*/ 
   {
       // calculate using pre-calculated values in invA and invB
   }
};

答案 2 :(得分:2)

  

三个逻辑步骤,由三个不同的类

表示

这可能不是最好的方法。

单个类可以包含大量“全局”变量,由类的所有方法共享。

将旧代码(C或Fortran)转换为新的OO结构时,我所做的就是尝试创建一个代表更完整“事物”的类。

在某些情况下,结构良好的FORTRAN会使用“命名的COMMON块”将事物聚类成有意义的组。这是关于“事物”究竟是什么的暗示。

此外,FORTRAN将有许多并行数组,这些数组并不是真正独立的事物,它们是常见事物的独立属性。

DOUBLE X(200)
DOUBLE Y(200)

实际上是一个包含两个属性的小类,您可以将它们放入集合中。

最后,您可以轻松地创建只包含数据的大型类,与包含执行工作的函数的类分开。这有点令人毛骨悚然,但它允许您通过将COMMON块转换为类并简单地将该类的实例传递给使用COMMON的每个函数来解决常见问题。

答案 3 :(得分:2)

嗯,在C ++中,最合适的解决方案,给定您的约束和条件,由指针表示。许多开发人员告诉你使用boost :: shared_ptr。嗯,没有必要,虽然它提供了更好的性能,特别是在考虑可移植性和系统故障的稳健性时。

没有必要绑定到boost。确实它们没有被编译,现在标准化过程将导致c ++与boost直接集成为标准库,但如果你不想使用外部库,你显然可以。

让我们继续尝试使用C ++及其实际提供的内容来解决您的问题。

你可能有一个主方法,你之前告诉过,初始化所有不变量元素...所以你基本上有常量,它们可以是每种可能的类型。如果你愿意,不需要使它们保持不变,但是,在main中你实例化你的不变元素并将它们指向需要它们的所有那些组件。首先在一个名为“common_components.hpp”的单独文件中考虑以下内容(我假设您的不变量需要一些类型):

typedef struct {
   Type1 invariant_var1;
   Type2 invariant_var2;
   ...
   TypeN invariant_varN;
} InvariantType; // Contains the variables I need, it is a type, instantiating it will generate a set of global variables.
typedef InvariantType* InvariantPtr; // Will point to a set of invariants

在“main.cpp”文件中,您将拥有:

#include "common_components.hpp"
// Functions declaration
int main(int, char**);
MyType1 CalculateValues1(InvariantPtr); /* Your functions have as imput param the pointer to globals */
MyType2 CalculateValues2(InvariantPtr); /* Your functions have as imput param the pointer to globals */
...
MyType3 CalculateValuesN(InvariantPtr); /* Your functions have as imput param the pointer to globals */
// Main implementation
int main(int argc, char** argv) {
   InvariantType invariants = {
      value1,
      value2,
      ...
      valueN
   }; // Instantiating all invariants I need.
   InvariantPtr global = &invariants;
   // Now I have my variable global being a pointer to global.
   // Here I have to call the functions
   CalculateValue1(global);
   CalculateValue2(global);
   ...
   CalculateValueN(global);
}

如果有函数返回或使用全局变量,请使用指向struct的指针来修改方法的接口。通过这样做,所有变化都将使用thoss变量充斥所有变化。

答案 4 :(得分:0)

不是单独传递每个参数,而是创建另一个类来存储它们并传递该类的实例:

// Before
void f1(int a, int b, int c) {
    cout << a << b << c << endl;
}

// After
void f2(const HighEnergyParams& x) {
    cout << x.a << x.b << x.c << endl;
}

答案 5 :(得分:0)

第一点:全局变量几乎与许多(大多数?)程序员声称的差不多(本身)。事实上,他们自己并不是真的很糟糕。它们主要是其他问题的症状,主要是1)在逻辑上分离不必要的混合代码片段,2)具有不必要的数据依赖性的代码。

在你的情况下,声音已经消除(或至少最小化)真正的问题(不变量,而不是真正的变量本身就消除了一个主要的问题来源)。您已经声明无法消除数据依赖性,并且您显然没有将代码混合到至少有两组不同的不变量。在没有看到代码的情况下,这可能比实际需要的更粗糙,并且可能在仔细检查后,可以完全消除这些依赖性中的一些。

如果你可以减少或消除依赖关系,这是一个值得追求的 - 但是消除全局变量本身并不值得或有用。事实上,我会说在过去十年左右的时间里,我发现全局变量引起的问题要少于那些没有真正理解他们试图消除那些(或者应该是)完全正确的全局变量问题的人。

鉴于它们是不变的,你可能应该做的是明确强制执行。例如,有一个工厂类(或函数)创建一个不变的类。不变类使工厂成为它的朋友,但这是唯一的方式,不变类的成员可以改变。反过来,工厂类具有(例如)静态bool,并且如果您尝试多次运行它,则执行assert。这给了(一个合理的水平)保证不变量确实是不变的(是的,reinterpret_cast将允许你修改数据,但不是偶然的。)

我真正面临的一个问题是,如果所有的计算都依赖于两者,那么将不变量分成两个“块”是否真的有意义。如果两者之间存在清晰,合理的分离,那就太好了(即使它们一起使用)。但是,如果您在逻辑上拥有单个数据块,那么尝试将其分解为碎片可能会适得其反。

底线:全局(最坏)是一种症状,而非疾病。坚持你 将患者的体温降至98.6度可能会适得其反 - 特别是如果患者是正常体温实际为102度的动物。

答案 6 :(得分:0)

嗯Cpp不一定是面向对象的。这是编程的GTA!您可以自由成为对象痴迷的怪胎,放松的C程序员,函数式程序员,无论如何。混合武术家。

我的意思是,如果全局变量在您的fortran编译中起作用,则只需将其复制并粘贴到Cpp中即可。无需避免全局变量。它遵循的原则是,不要碰遗留代码。

让我们理解为什么全局变量可能导致问题。众所周知,变量是程序的状态,而状态是程序的灵魂。错误或无效状态会导致运行时和逻辑错误。全局变量/全局状态的问题在于,我们代码的任何部分都可以访问它;因此,在无效状态的情况下,他们是要考虑的许多坏人或罪魁祸首,意味着功能和运算符。但是,这仅在您确实在全局变量上使用了许多函数的情况下才适用。我的意思是,您是唯一一个从事孤独项目的人。如果您正在执行团队项目,则全局变量只是一个实际的问题。在这种情况下,许多人都可以访问它,编写可能访问或不访问该变量的不同函数。