Лучшее место для инициализации значений по умолчанию в классе pimpl? - PullRequest
1 голос
/ 02 апреля 2012

Я довольно широко использую PImpl, и кое-что, о чем я задумалась, - это где именно инициализировать члены структуры Pimpl. Можно создать конструктор для структуры Private и инициализировать его там или инициализировать в конструкторе основного класса.

myclass.hpp:

class MyClass {
public:
    MyClass();
    ~MyClass();
private:
    struct Private; unique_ptr<Private> p;
};

myclass.cpp:

#include "myclass.hpp"
#include <string>

struct MyClass::Private {
    int some_var;
    std::string a_string;

    // Option A
    Private() :
        some_var {42},
        a_string {"foo"}
    {}
};

MyClass::MyClass() : p(new MyClass::Private) {
    // Option B
    p->some_var = 42;
    p->a_string = "foo";
}

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

Ответы [ 2 ]

5 голосов
/ 03 апреля 2012

В любом случае, следуйте подходу RAII и инициализируйте элементы в вашем типе Private. Если вы храните вещи локально (и что более важно, в логических местах), обслуживание поблагодарит вас. Что еще более важно, вы сможете иметь постоянных членов, если вы используете вариант А.

Если вам нужно передать значения из MyClass ctor, тогда создайте правильный конструктор для Private:

struct MyClass::Private {
    int const some_var; // const members work now
    std::string a_string;

    // Option C
    Private(int const some_var, std::string const& a_string) :
        some_var {some_var},
        a_string {a_string}
    {}
};

MyClass::MyClass() : p(new MyClass::Private(42,"foo")) {
}

В противном случае ваши Private члены будут созданы по умолчанию, но будут перезаписаны позже (что не имеет значения для int s, но как насчет более сложных типов?).

1 голос
/ 03 апреля 2012

Как уже отмечалось @Charles Salvia выше, присваивание в любом из двух конструкторов влечет за собой некоторые накладные расходы, поскольку переменные создаются по умолчанию перед присвоением значения.Количество этих накладных расходов, конечно, сильно зависит от типа ваших переменных.

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

Однако учтите, что нет никакого способа обойти список инициализатора (для Private c'tor), а именно, когда ваши переменные-члены не имеют конструктора по умолчанию или когда вы используете ссылки или константы.

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

...