Практическое правило для перегрузок конструктора C ++?

В C ++ я часто (почти всегда) сталкиваюсь с проблемами с конструкторами; Я никогда не уверен, как использовать параметры, и в конечном итоге я буду использовать только конструктор без параметров для каждого класса.
Затем я буду использовать сеттеры после каждого определения экземпляра.
Например:

// Obviously better
Point p(5, 3);

// ...and yet I end up using this
Point p;
p.setX(5);
p.setY(3);

Причина, по которой я в конечном итоге использую «худший» метод, заключается в том, что некоторые классы принимают так много параметров, и существует так много разных возможностей для их построения, поэтому мои мысли перепутались, и я больше не знаю, что мне делать.
Вот пример недавнего класса, который я сделал (он использует Qt):

// implements position and pixmap (image shown when painted)
class Block : QObject {
public:
Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(const QPoint &pos, const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(const Block &other);
};

Наличие только одного класса только с двумя атрибутами (position а также image), Я уже получил четыре конструктора, принимающих максимум четыре параметра.
Кроме того, я часто вижу людей, заменяющих первый конструктор Block(QObject *parent = 0); а также Block(const QPixmap &img, QObject *parent = 0); и я не уверен, что это будет хорошо.

Теперь, что, если я создаю Rectangle мой собственный класс, который наследует от Block так что тоже нужно брать image а также position как это параметры:

Rect(const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const QRect &rect, const QPixmap &pixmap = QPixmap(), QObject *parent = 0);
Rect(const QSize &size, const QPoint &pos = QPoint(), const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const QPoint &pos, const QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(int x, int y, int w = 1, int h = 1, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(int w, int h, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const QPoint &pos, int w, int h, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(int x, int y, const QSize &size, QPixmap &img = QPixmap(), QObject *parent = 0);
Rect(const Rect &rect);

Как видите, это начинает выглядеть ужасно и чертовски сложно поддерживать.
Представь Player наследование класса от Rect и внедрение items, health, mana, damage, armor
Я считаю невозможным кодировать такие конструкторы.

Итак, теперь, когда я поставил вам проблему, я не уверен в этом вопросе; Мне нужны какие-либо советы или хитрости, чтобы помочь мне с моими конструктами, когда использовать параметры по умолчанию и как часто мне нужны все конструкторы и, да, только несколько советов, чтобы помешать мне создать 500 конструкторов для одного класса; я должен просто пойти с моим оригиналом p.setX(5); пример?

1

Решение

Если вы создаете свои собственные классы, вам определенно следует избегать этого экспоненциального взрыва комбинаций конструкторов. Один из способов сделать это — предоставить другие механизмы для создания экземпляров класса. Есть несколько общих Образцы творчества которые могут заполнить эту цель для различных конкретных потребностей. Шаблоны проектирования — это отдельная огромная тема, поэтому я предлагаю вам найти материал для чтения, который поможет вам в этом.

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

2

Другие решения

Должен быть баланс между простотой и удобством использования. Если у вас есть 500 конструкторов, то кому-то придется пройтись по большому количеству документации, чтобы найти тот, который им нужно использовать. Вам также нужно будет создать много тестового кода, чтобы все они работали как положено.
Простое эмпирическое правило гласит: «Что самое меньшее, что я могу раскрыть, это самое выгодное»?

Версия ваших классов, а затем создайте самый простой интерфейс, который может использовать кто-то еще.

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

1

Взяв этот раздел за пример, он может быть значительно упрощен:

    Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(const QPoint &pos, const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);

Удалить эти два:

    Block(const QPixmap &img = Pixmap(), QObject *parent = 0);
Block(int x, int y, const QPixmap &img = Pixmap(), QObject *parent = 0);

Если вы хотите, чтобы «нет позиции», вы просто оставляете это пустым QPoint:

    Block(QPoint(), img);

И если вы хотите это в x, y, а затем сделать QPoint(x, y)

    Block(QPoint(x, y), img, parent);

(Вы могли бы, конечно, сделать это QPoint pos = QPoint(), так что если вы хотите пустой, вы можете использовать Block(),

Тот же принцип может быть применен к RectУ вас есть несколько вариантов, которые «почти одинаковы». Замените все варианты одним вариантом, который принимает QRect в качестве первого аргумента. Если у вас нет «QRect», он может быть создан как временный (и в большинстве случаев он просто «исчезнет»).

Надеемся, что это ТАКЖЕ делает конструктор набора проще, так как вы можете просто сделать

m_rect = rect;

вместо того, чтобы писать 6 разных форм: «У меня есть несколько разных предметов, которые нужно превратить в прямоугольник».

Если нет ОЧЕНЬ веской причины для добавления другого конструктора, не делайте этого. Просто потому, что код, над которым вы сейчас работаете, имеет x, y, h, а также w, не значит, что вы не можете сделать это в QRect довольно легко

0

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

0