Лучшая практика в классе C ++ - PullRequest
4 голосов
/ 09 марта 2009

Я хотел бы узнать некоторые рекомендации при разработке классов C ++.

Чтобы поместить это в контекст, у меня есть класс c ++ с именем Vec3.

class Vec3{
private:
    float elements[3];
public:
    Vec3(Vec3 v1){...}
    Vec3(int x, int y, int z){...}
    Vec3 add(Vec3 v1){...}
    Vec3 add(int x, int y, int z){...}
    ...
    Vec3 multiply(Vec3 v1){...}
    ...
    int dotProduct(Vec3 v1){...}
    Vec3 normalize(){...}
    ....
    int operator[](int pos){...}
};

Итак, у меня есть этот класс, который выполняет вычисления по вектору размера 3. Я хотел бы знать, что лучше. Работать с указателями или нет.

Должен ли я возвращать указатель и иметь параметры в качестве указателей или нет.

Vec3 add (Vec3 v1) или Vec3 * add (Vec3 v1) или Vec3 * add (Vec3 * v1) или ....

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

Vec3* v2 = new Vec3(1,1,1);
Vec3 sum = v1.add(*v2);

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

Vec3 add(Vec3 v2){...}
Vec3* add(Vec3* v2){...}

Но я боюсь, что это приведет к дублированию кода и может привести к накладным расходам.

Спасибо за ответы ... кстати, я мог бы использовать шаблон для изменения размера вектора, но я предпочитаю оставить свой класс Vec3 в одиночестве и создать класс Vec4 или назвать его Quaternion.

EDIT Вот решение, с которым я пришел. Не стесняйтесь комментировать или изменять или повторно использовать код. Одна вещь. Я просто хочу отметить, что в моем случае этот класс должен быть прозрачным. Также как мы добавляем числа.

int i = 10;
int j = 15;
int k = i + k;

Если перегрузка add изменяет объект, вызывающий функцию, в этом случае i. В конце концов, я бы сказал, что k - это ссылка на i, а i равно 25. Но здесь мы действительно хотим, чтобы k было равно 25, а i, k не изменилось.

Так работает мой класс. Vec3 k = i + k не изменит i или k, потому что мы создаем новое число из этих значений. Единственный случай, когда я возвращаю ссылку, это для + =, - =, ++, --..., set ([XYZ])? и нормализовать.

Может быть интересно сделать что-то вроде myvec.setX (10) .normalize (). Scale (10)

ПРИМЕЧАНИЕ: весы должны возвращать ссылку. Я этого не видел, но думаю, так должно быть лучше.

Vec3 t = myvec.normalize().scale(100).copy();

http://pastebin.com/f413b7ffb

Спасибо всем, сейчас я буду работать над классом Matrix.

Ответы [ 8 ]

8 голосов
/ 09 марта 2009

Это правила, которых я обычно придерживаюсь. Обратите внимание на «обычно», иногда есть причины поступать иначе…

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

Для параметров, которые я собираюсь изменить, я использую ссылку.

Для возвращаемых значений я буду возвращать копию, когда это возможно. Иногда удобно возвращать ссылку (это хорошо работает для одной функции get / set, где вам не нужно выполнять какую-либо специальную обработку при получении или установке элемента).

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

Надеюсь, это поможет.

3 голосов
/ 09 марта 2009

Векторы имеют известную семантику (известную для вас и пользователей вашего класса), поэтому я хотел бы рассмотреть вопрос о перегрузке операторов (+, -, + =, - =), поэтому я буду использовать обычные определения, а не менять они:

// instead of add:
class Vec3 {
public:
   Vec3& operator+=( Vec3 const & rhs );
};
// implemented as free function:
Vec3 operator+( Vec3 const &lhs, Vec3 const & rhs);

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

Предлагает как постоянный, так и непостоянный оператор []:

class Vec3 {
public:
   float operator[]( size_t pos ) const; // returns copy, data does not change
   float& operator[]( size_t pos );  // returns a reference and allows changing the contents
};

РЕДАКТИРОВАТЬ: я забыл упомянуть о детали size_t: предпочитаю использовать unsigned / size_t для параметров индекса вместо целых чисел со знаком.

3 голосов
/ 09 марта 2009

Поскольку int - это примитивы, оставьте их как есть. для всего, что связано с использованием vec3.

например.

Vec3 add(const Vec3 &v1){...}

В C вы бы использовали указатель, но в C ++ ссылка обычно лучше для объектов.

1 голос
/ 09 марта 2009

Вы почти наверняка не хотите, чтобы параметры были указателями в этом случае. Рассмотрим этот пример, почему:

// Error: not possible to take the address of the temporary
//        return value. 
v2.add(&someFunctionReturningVec3());

Для ссылок на константу это не проблема. Вы можете легко вкладывать операции даже:

// declaration: Vec3 add(Vec3 const& v);
v2.add(v1.add(v3));
1 голос
/ 09 марта 2009

Если вы реализуете операторы, такие как operator+=() и operator*=(), вы захотите, чтобы они возвращали *this как Vec3&.

Vec3& operator+=(const Vec3& v2) {
    // add op
    return *this;
}

Для других основных операторов, таких как operator+() и ваш add(), вы захотите вернуть copy :

Vec3 operator+(const Vec3& v2) {
    Vec3 ret;
    // add
    return ret;
}
0 голосов
/ 09 марта 2009

Эта страница содержит действительно хорошее обсуждение на эту тему: http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html

0 голосов
/ 09 марта 2009

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


void add(Vec3 v1);
void multiply(Vec3 v1);

Я бы также сказал, что вы должны придерживаться объектов Vec3 в качестве аргументов (а не x, y, z). Если у вас есть только x, y, z, вы можете вызвать add (Vec3 (x, y, z)).

0 голосов
/ 09 марта 2009

Как уже упоминалось в greyfade, вы должны быть обеспокоены семантикой копирования. В этом случае вам также следует добавить следующие методы:

class Vec3 {
public:
  Vec3(const Vec3& rhs) {
    copy(rhs);
  }

  Vec3 operator=(const Vec3& rhs) {
    copy(rhs);
    return *this;
  }

private:
  void copy(const Vec3& rhs) {
    // copy state from rhs
  }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...