Какова лучшая практика при кодировании математических классов / функций? - PullRequest
3 голосов
/ 18 мая 2010

Вступительное примечание : Я добровольно выбрал широкую тему. Вы знаете эту цитату об обучении кошки ловить рыбу, вот и все. Мне не нужен ответ на мой вопрос, мне нужны объяснения и советы. Я знаю, что вы, ребята, хороши в этом;)


Привет, ребята,

В настоящее время я внедряю некоторые алгоритмы в существующую программу. Короче говоря, я создал новый класс "Adder". Adder является членом другого класса, представляющего физический объект, фактически выполняющий исчисление, который вызывает adder.calc () со своими параметрами (просто список объектов, для которых выполняется математика).

Чтобы выполнить эту математику, мне нужны некоторые параметры, которые не существуют вне класса (но могут быть установлены , см. Ниже). Они не являются ни параметрами конфигурации, ни членами других классов. Это параметры D1 и D2, расстояния и три массива фиксированного размера: альфа, бета, дельта.

Я знаю, что некоторым из вас удобнее читать код, чем читать текст, так что вот вам:

class Adder
{
public:

  Adder();
  virtual Adder::~Adder();

void set( float d1, float d2 );
  void set( float d1, float d2, int alpha[N_MAX], int beta[N_MAX], int delta[N_MAX] );

  // Snipped prototypes
  float calc( List& ... );
  // ...

  inline float get_d1() { return d1_ ;};
  inline float get_d2() { return d2_ ;};

private:

  float d1_;
  float d2_;

  int alpha_[N_MAX]; // A #define N_MAX is done elsewhere
  int beta_[N_MAX]; 
  int delta_[N_MAX]; 
};

Поскольку этот объект используется как член другого класса, он объявлен в * .h:

private:
  Adder adder_;

Сделав это, я не смог инициализировать массивы (альфа / бета / дельта) непосредственно в конструкторе (int T [3] = {1, 2, 3};), не выполняя итерацию по всем трем массивам , Я думал поместить их в static const , но я не думаю, что это правильный способ решения таких проблем.

Вторым моим предположением было использование конструктора для инициализации массивов

Adder::Adder()
{
    int alpha[N_MAX] = { 0, -60, -120, 180, 120,  60 };
    int beta[N_MAX] = { 0,   0,    0,   0,   0,   0 };
    int delta[N_MAX]  = { 0,   0,  180, 180, 180,   0 };

    set( 2.5, 0, alpha, beta, delta );
}

void Adder::set( float d1, float d2 ) {
    if (d1 > 0)
        d1_ = d1;

    if (d2 > 0)
        d2_ = d2;
}

void Adder::set( float d1, float d2, int alpha[N_MAX], int beta[N_MAX], int delta[N_MAX] ) {
    set( d1, d2 );

    for (int i = 0; i < N_MAX; ++i) {
        alpha_[i] = alpha[i];
        beta_[i] = beta[i];
        delta_[i] = delta[i];
    }
}

Мой вопрос: Было бы лучше использовать другую функцию - init () - которая инициализирует массивы? Или есть лучший способ сделать это?

Мой бонусный вопрос: Вы видели какие-то ошибки или плохую практику на этом пути?

Ответы [ 4 ]

5 голосов
/ 18 мая 2010

Вы выбрали очень широкую тему, поэтому вот более широкий ответ.

  • Будь в курсе своего окружения
  • Старайтесь не изобретать велосипед

Расширение моего предыдущего пункта.

Хотя каждому, вероятно, следует написать связанный список или строковый класс в качестве упражнения, нет необходимости писать его для рабочего кода. У вас будет доступ к MFC, OWL, STL, Boost, что угодно. Если колесо уже было изобретено, используйте его и приступайте к кодированию решения реальной проблемы!

  • Подумайте, как вы собираетесь тестировать свой код

Разработка через тестирование (TDD) - это один из способов (но не единственный) обеспечения того, что ваш код можно тестировать и тестировать. Если вы подумаете о тестировании своего кода с самого начала, его будет очень легко протестировать. Тогда проверь это!

  • Написать твердый код

Страница Википедии объясняет это гораздо лучше, чем я мог.

  • Убедитесь, что ваш код читабелен

Значимые идентификаторы - это только начало. Ненужные комментарии могут также снизить читабельность, как и длинные функции, функции с длинными списками аргументов (например, ваш второй установщик) и так далее. Если у вас есть стандарты кодирования , придерживайтесь их.

  • Использование const больше

Моя главная проблема с C ++ в том, что по умолчанию все не константно! В вашем примере ваши получатели должны быть объявлены const, а ваши установщики должны передавать свои аргументы как constconst -ссылка для массивов).

  • Убедитесь, что ваш код поддерживается

Модульные тесты (как упомянуто выше) гарантируют, что следующий человек, который изменит ваш код, не нарушит вашу реализацию.

  • Убедитесь, что ваша библиотека работоспособна

Если вы будете следовать принципу наименьшего удивления и документировать свою библиотеку с помощью юнит-тестов, у программистов, использующих ее, будет меньше проблем.

Расширение предыдущего пункта. Делайте все возможное, чтобы уменьшить дублирование кода. Сегодня я был свидетелем исправления ошибки, которое должно было быть выполнено в пятнадцати отдельных местах (и было выполнено только в тринадцати из них).

4 голосов
/ 18 мая 2010

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

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

1 голос
/ 18 мая 2010
  1. Не используйте виртуальные функции, если ваш дизайн действительно не требует их.
  2. В классе, который существует главным образом для выполнения одной функции, эта функция имеет каноническое имя operator(). То есть Вы бы назвали свои как adder_(params), а не adder_.calc(params).
  3. Если вы инициализируете три массива, более эффективно использовать три цикла for. (дружественный кеш)
1 голос
/ 18 мая 2010

Хорошо. Я бы:

  1. Установить массивы в известное состояние с помощью используя memset, чтобы очистить все значения 0 (или другое значение) в пределах конструктор, прежде чем они будут использованы.
  2. Измените конструктор, чтобы разрешить передача указателей массива, которые могут использоваться для инициализации массивов в некоторые другие значения.
  3. Сохранить набор функция, которую вы должны изменить значения в массивах и ariables, которые вы используете.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...