开发者

Options for granting access to class data members (C++)

This is a basic qu开发者_如何学Goestion about what options are available for defining the scope of some parameters. I'd particularly like to have a sense of the tradeoffs in speed, memory, code clarity, ease of implementation, and code safety. My programming experience is clearly modest, and I have little formal training. I suspect one or two options will be obvious 'right' answers.

I have a simulation encapsulated in its own class called Simulation. Some of the parameters used in the simulation are read in from a set of files when the Simulation is initialized, and these parameters are currently stored in a few two-dimensional arrays. These arrays, demPMFs and serotypePars, are currently both private members of 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]...
};

The simulations mostly involve modifying instances of class Host. Member functions of class Host frequently need access to parameter values stored in serotypePars. Previously, I had all the data now in serotypePars stored as const numerics interpreted at compile time, but now that these parameters are read from a file, things are a little more complicated (for me, anyway).

What is the fastest and safest way for me to grant all members of class Host 'read-only' access to serotypePars? Fastest deserves priority: There are no other classes in my program, so global is a (crude) potential solution; all simulations will run from identical parameter sets. I'm reluctant to pass the array to individual functions that act on Host members, since there are probably a dozen and some are quite nested. (For example, I have intermediate wrapper structs that cannot take two-dimensional arrays as arguments, and I'm unsure what syntactical work-arounds there might be. I would like to stick with arrays for speed purposes, and my non-uniform prng generators all expect arrays.)

I would greatly appreciate knowing what would require the least modification of the existing code while not introducing huge dangers.

Thanks for any insight you can offer.


A related challenge I have is not knowing exactly how to access Simulation members from the Host class, unless these members are passed to the Host function. Here's basically what happens:

// main.cpp

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

Function Simulation::runDemSim() and Simulation::runEpidSim() create, modify, and destroy many instances of class Host. Pointers to these instances are stored in a Boost MultiIndex container, which is where the intermediate wrapper functions come in. One of the many modifications involves ultimately calling Host::calcRecovery, which needs access to 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 );
}

(Apologies if TMI.) Simply declaring serotypePars public in the Simulation class definition resulted in a "serotypePars was not declared in this scope" error in Host.cpp.


Solution summary

GMan's suggestion that I package all the simulation parameters in a private class, e.g., SimulationPars, seems like the most elegant and extensible route. An instance of SimulationPars could belong in every Simulation, and a pointer to SimulationPars can be passed to the constructor of every Host within a given Simulation. Thanks to everyone for the thoughtful discussions.


This is normal:

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;
};


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


You could write inline accessor methods, such as:

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

As long as the compiler honors your inline usage, this should be just as fast as direct access, since it tells the compiler to embed the function at every occurrence rather than generating function calls.


Write a global function to read your configuration into two local arrays and construct your Simulation with those local arrays:

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

Simulation s(demPMFs, serotypePars);

Make the data members public and const and modify your constructor to take the values of these arrays as arguments and initialize them as part of the constructor's initialization list:

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

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

You end up with a simulation containing constant configuration. You can safely expose these constant arrays to the public without wrapping them in any accessors.


I'm not sure if there's a way to make the values read-only available to ONLY instances of the Host class, but in terms of making them generally read-only at the public access level, I would suggest using const references:

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 ];

Then add a pair of initializers to the Simulation constructor:

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

This way, you can access demPMFsReadOnly and serotypeParsReadOnly as regular read-only values, rather than pointers or functions, at the public level, but you can change the values of demPMFs and serotypePars at the private level.

It takes a bit more code in the Simulation class itself than some other methods, but it saves on time (both yours and the program's) on the long run.

P.S. It's important that the private arrays are declared before the public ones; they are initialized in that order, and the private arrays must be initialized first so that the public references can be initialized to refer to them.

P.P.S. The parentheses in the public reference declarations are important too. Without them, the compiler will try to interpret these statements as declaring arrays of references (which is ILLEGAL).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜