授予对类数据成员的访问权限的选项(C ++)

时间:2010-06-22 19:30:48

标签: c++ class scope

这是一个基本问题,关于哪些选项可用于定义某些参数的范围。我特别想了解速度,内存,代码清晰度,易于实现和代码安全性之间的权衡。我的编程经验显然不大,我几乎没有接受过正式的培训。我怀疑一两个选项将是明显的“正确”答案。

我有一个封装在自己的类Simulation中的模拟。初始化Simulation时,模拟中使用的一些参数将从一组文件中读取,并且这些参数当前存储在一些二维数组中。这些数组demPMFsserotypePars目前都是Simulation的私有成员:

// Simulation.h

class Simulation
{
 Simulation( int simID );
 ~Simulation();

 public:
  // ...[code snipped]...

 private:
  double demPMFs[ NUM_SOCIODEM_FILES ][ INIT_NUM_AGE_CATS ];
  double serotypePars[ NUM_EPID_FILES ][ INIT_NUM_STYPES ];
  // ...[code snipped]...
};

模拟主要涉及修改类Host的实例。类Host的成员函数经常需要访问存储在serotypePars中的参数值。以前,我将serotypePars中的所有数据存储为编译时解释的const数字,但现在从文件中读取这些参数,事情有点复杂(对我来说,无论如何)

我最快最安全的方式是授予Host serotypePars课程的所有成员“只读”权限?最快值得优先:没有我的程序中的其他类,因此global是一个(粗略的)潜在解决方案;所有模拟都将从相同的参数集运行。我不愿意将数组传递给作用于Host成员的各个函数,因为可能有十几个而且有些是完全嵌套的。 (例如,我有中间包装器结构,不能将二维数组作为参数,我不确定那里可能有什么语法处理。我想坚持使用数组来达到速度目的,我的非均匀prng生成器都期望数组。)

我非常感谢知道在不引入巨大危险的情况下需要对现有代码进行最少修改的内容。

感谢您提供的任何见解。


我遇到的一个相关挑战是不知道如何从Host类中访问Simulation成员,除非这些成员被传递给Host函数。这基本上是发生了什么:

// main.cpp

int main() {
  for ( int s = 1; s < NUM_SIMS+1; s++ ) {
   Simulation thisSim(s);
   thisSim.runDemSim();
   thisSim.runEpidSim();
   // ... code snipped ...
 }
}

函数Simulation::runDemSim()Simulation::runEpidSim()创建,修改和销毁类Host的许多实例。指向这些实例的指针存储在Boost MultiIndex容器中,该容器是中间包装函数的来源。许多修改之一涉及最终调用Host::calcRecovery,需要访问serotypePars

// Host.cpp

// ... code snipped ...

double Host::calcRecovery( int s, double currentTime, double infectionTime, boost::mt19937& rng ) {
  // ...code snipped...
  effectiveMean = serotypePars[ MEAN_DURATION_INDEX ][ s ] * currentInfections * pastInfections;
  double rt = rgamma( effectiveMean, serotypePars[ VAR_DURATION_INDEX ][ s ], rng );
}

(如果是TMI,请道歉。)只需在serotypePars类定义中声明Simulation public,导致在Host.cpp中未在此范围内声明“serotypePars”错误。


解决方案摘要

GMan建议我将所有模拟参数打包在私有类中,例如SimulationPars,这似乎是最优雅和可扩展的路径。 SimulationPars的实例可能属于每个Simulation,指向SimulationPars的指针可以传递给给定Host内每个Simulation的构造函数。感谢大家的深思熟虑的讨论。

5 个答案:

答案 0 :(得分:2)

这是正常的:

class Simulation
{ 
public:
    // typedef's should be used liberally, it's easier to read
    typedef double pmf_type[NUM_SOCIODEM_FILES][INIT_NUM_AGE_CATS];
    typedef double sero_type[NUM_EPID_FILES][INIT_NUM_STYPES];

    // accessors
    const pmf_type& get_pmf(void) const
    {
        return demPMFs;
    }

    const sero_type& get_sero(void) const
    {
        return serotypePars;
    }

private:
    pmf_type demPMFs;
    sero_type serotypePars;
};

答案 1 :(得分:1)

public:    
const double** GetSerotypes() { return serotypePars; }

答案 2 :(得分:1)

您可以编写内联访问器方法,例如:

public:
    inline double readDemPMFs(int x, int y) {
        return demPMFs[x][y];
    }
    inline double readSerotypePars(int x, int y) {
        return serotypePars[x][y];
    }

只要编译器尊重您的inline用法,这应该与直接访问一样快,因为它告诉编译器在每次出现时嵌入函数而不是生成函数调用。

答案 3 :(得分:1)

编写一个全局函数,将您的配置读入两个本地数组,并使用这些本地数组构建Simulation:

double demPMFs[ NUM_SOCIODEM_FILES ][ INIT_NUM_AGE_CATS ];
double serotypePars[ NUM_EPID_FILES ][ INIT_NUM_STYPES ];
loadConfig(&demPMFs, &serotypePars);

Simulation s(demPMFs, serotypePars);

使数据成员为public和const并修改构造函数以将这些数组的值作为参数并将它们初始化为构造函数初始化列表的一部分:

public:
    Simulation(double const** demPMFs, double const** serotypePars)
        : demPMFs(demPMFs), serotypePars(serotypePars)
    {}

    double const** demPMFs;
    double const** serotypePars;

您最终会得到一个包含常量配置的模拟。您可以安全地将这些常量数组公开给公众,而不将它们包装在任何访问器中。

答案 4 :(得分:0)

我不确定是否有办法让值只读只用于Host类的实例,但是在公共访问级别通常只读它们时,我建议使用const引用:

private:
double demPMFs[ NUM_SOCIODEM_FILES ][ INIT_NUM_AGE_CATS ];
double serotypePars[ NUM_EPID_FILES ][ INIT_NUM_STYPES ];

public:
const double (& demPMFsReadOnly)[ NUM_SOCIODEM_FILES ][ INIT_NUM_AGE_CATS ];
const double (& serotypeParsReadOnly)[ NUM_EPID_FILES ][ INIT_NUM_STYPES ];

然后将一对初始值设定项添加到Simulation构造函数中:

Simulation(int simId) : demPMFsReadOnly(demPMFs), serotypeParsReadOnly(serotypePars){
    ...
}

这样,您可以在公共级别访问demPMFsReadOnly和serotypeParsReadOnly作为常规只读值,而不是指针或函数,但您可以在私有级别更改demPMF和serotypePars的值。

在Simulation类本身中需要比其他一些方法更多的代码,但从长远来看,它可以节省时间(包括你的程序和程序)。

P.S。私有数组在公共数组之前声明是很重要的;它们按此顺序初始化,并且必须首先初始化私有数组,以便可以初始化公共引用以引用它们。

P.P.S。公共引用声明中的括号也很重要。如果没有它们,编译器将尝试将这些语句解释为声明引用数组(即ILLEGAL)。