Страницы

Поиск по вопросам

воскресенье, 29 декабря 2019 г.

Избежание продублированного кода в конструкторах класса. С++

#cpp


class Boo
{
public:
    Boo()
    {
        Сделать X
    }
    Boo(int value)
    {
        Сделать Х
        СделатЬ У
    }
};


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

// 1 вариант
class Boo
{
public:
    Boo()
    {
        Сделать X
    }
    Boo(int value):Boo()
    {
        СделатЬ У
    }
};




// 2 Вариант

class Boo
{
public:
    Boo()
    {
        doX();
    }
    Boo(int value)
    {
        doX();
        Сделать У;
    }

    void doX()
    {
        Делает X;
    }
};


Какой из них лучше ? Почему ? Или может есть получше ?
    


Ответы

Ответ 1



Лучше_ это понятие относительное. Лучше, когда все соответствует вашим пожеланиям и пожеланиям и удобствам пользователей. Если пользователью может понадобится метод void doX(), то лучше второй вариант, потому что в первом нет этого метода, а если нет, то лучше первый вариант, поскольку в первом меньше вызовов функций. Конечно, в основном бывает полезнее вариант такой: class Boo { public: Boo(int value = 0) { Сделать X if (value ) СделатЬ У } }; Поскольку тут нет лишных вызовов(лучше в плане производительности) и меньше кода (лучше для читабельности и наверное компиляции). Тут разницы такие мизерные, что можно говорить в принципе только о читабельноси... Т.е. по возможности меньшим кодом нужно добится желаемого результата. Обновление: Еще раз говорю, все зависит от ваших намерений, потому что однозначно говорить как нужно, невозможно. Например если у меня есть такие тривиальные определения: void B() { std::cout << "I am Boo\n"; } void foo(int n) { std::cout << "i created with " << --n << '\n'; } class Boo { public: Boo(int value) { B(); foo(value); } Boo() { B(); } }; class His_variant { public: His_variant(int value) { B(); foo(value); } //разве это то, что я хотел выразить?... His_variant() : His_variant(0) {} }; class My_version { public: My_version(int value = 0) { B(); if (value) foo(value); } }; То, если мы напишем программу: // первый вариант Boo b, bb(22); std::cout <<"\n----------------" << std::endl; // вариант с другого ответа His_variant h, hh(22); std::cout <<"\n----------------" << std::endl; // вариант, о котором говорил я My_version m, mm(22); Первыи и третий вариант выдадут одинаковый результат, с той разницей, о чем я говорил, а вариант второй(о чем говорилось в другом ответе), естественно, выдаст совершенно другой результат

Ответ 2



Если "действия", о которых вы говорите, должны производиться именно в теле конструктора (между {}), то никакой принципиальной разницы между этими подходами нет. Второй вариант лишь заводит лишнюю сущность - дополнительный метод класса. Если закрыть на это глаза, то без разницы как делать. Но тут надо заметить, что в конструкторе в первую очередь нас интересует устранение повторений в списке инициализации, т.е. именно для class Boo { public: Boo(x, y) : хитро_инициализировать_A(x, y), хитро_инициализировать_B(x, y) {} Boo() : хитро_инициализировать_A(3, 42), хитро_инициализировать_B(3, 42) {} }; Раньше нам приходилось извращаться через class Boo { public: Boo(x, y) : // "Ложная" инициализация, возможно неявная инициализировать_A_какой-то-хренью, инициализировать_B_какой-то-хренью { do(x, y); } Boo() : // "Ложная" инициализация, возможно неявная инициализировать_A_какой-то-хренью, инициализировать_B_какой-то-хренью { do(3, 42); } void do(x, y) { // Вторая, "настоящая" инициализация хитро_переинициализировать_A_по-новому(x, y); хитро_переинициализировать_B_по-новому(x, y); } }; Т.е. в этом варианте A и B в общем случае сначала пройдут через некую инициализацию "по умолчанию", а затем - через дополнительную "доинициализацию" (или "переинициализацию") уже в функции. Это нерационально. Это неприятное явление известно как проблема двойной инициализации. И к тому же этот подход вообще невозможно никак применить для полей, не допускающих инициализацию по умолчанию или переинициализацию (поля-ссылки, константные поля и т.п.) Вот тут-то нам и приходит на помощь ... Boo() : Boo(3, 42) {} ... Этот вариант был введен в язык "относительно недавно" в первую очередь для того, чтобы решить вышеописанную проблему двойной инициализации. Подход с делегирующим вызовом конструктора свободен от этого недостатка. Выполняется именно инициализация и выполняется она ровно один раз.

Комментариев нет:

Отправить комментарий