Варианты предоставления доступа к данным класса (C ++) - PullRequest
2 голосов
/ 22 июня 2010

Это основной вопрос о том, какие параметры доступны для определения области действия некоторых параметров.Мне бы особенно хотелось ощутить компромисс в скорости, памяти, четкости кода, простоте реализации и безопасности кода.Мой опыт программирования явно скромный, и у меня мало формального обучения.Я подозреваю, что один или два варианта будут очевидными «правильными» ответами.

У меня есть симуляция, инкапсулированная в свой собственный класс под названием Simulation.Некоторые параметры, используемые в симуляции, считываются из набора файлов при инициализации симуляции, и эти параметры в настоящее время хранятся в нескольких двумерных массивах.Эти массивы demPMFs и serotypePars в настоящее время являются частными членами 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все генераторы ожидают массивы.)

Я был бы очень признателен, зная, что потребовало бы наименьшего изменения существующего кода, не создавая при этом больших опасностей.

Спасибо за любую информацию, которую вы можете предложить.


У меня есть проблема, связанная с тем, что я не знаю точно, как получить доступ к членам Simulation из класса Host, если только эти участники не переданы в 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 public в определении класса Simulation привело к ошибке «serotypePars не был объявлен в этой области» в Host.cpp.


Краткое изложение решения

Предложение GMan о том, чтобы я упаковал все параметры моделирования в закрытом классе, например, SimulationPars, представляется наиболее элегантным и расширяемым маршрутом.Экземпляр SimulationPars может принадлежать каждому Simulation, и указатель на SimulationPars может быть передан конструктору каждого Host в пределах данного Simulation.Спасибо всем за вдумчивые обсуждения.

Ответы [ 5 ]

2 голосов
/ 22 июня 2010

Это нормально:

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 голос
/ 22 июня 2010

Напишите глобальную функцию для считывания вашей конфигурации в два локальных массива и построения вашего моделирования с этими локальными массивами:

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

Simulation s(demPMFs, serotypePars);

Сделайте элементы данных общедоступными и const и измените ваш конструктор, чтобы принимать значения этих массивов в качестве аргументов и инициализировать их как часть списка инициализации конструктора:

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

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

В результате вы получите симуляцию с постоянной конфигурацией. Вы можете безопасно выставлять эти постоянные массивы на публику, не оборачивая их в какие-либо методы доступа.

1 голос
/ 22 июня 2010

Вы можете написать встроенные методы доступа, такие как:

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

Пока компилятор учитывает ваше использование inline, это должно быть так же быстро, как и прямой доступ, поскольку он говорит компилятору вставлять функцию при каждом появлении, а не генерировать вызовы функций.

1 голос
/ 22 июня 2010
public:    
const double** GetSerotypes() { return serotypePars; }
0 голосов
/ 22 июня 2010

Я не уверен, есть ли способ сделать значения доступными только для чтения для ТОЛЬКО экземпляров класса Host, но с точки зрения обеспечения их доступности только для чтения на уровне открытого доступа, я бы предложил использовать константные ссылки:

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 требуется немного больше кода, чем в некоторых других методах, но это экономит время (и ваша, и программа) в долгосрочной перспективе.

PS Важно, чтобы частные массивы объявлялись раньше общедоступных;они инициализируются в таком порядке, и частные массивы должны быть инициализированы в первую очередь, чтобы можно было инициализировать публичные ссылки, чтобы ссылаться на них.

PPS Скобки в декларациях публичных ссылок также важны.Без них компилятор попытается интерпретировать эти операторы как объявление массивов ссылок (что НЕЗАКОННО).

...