2012-04-02 2 views
1

Я довольно широко использую PImpl, и что-то, что я обнаружил, что я waffling, это то, где именно, чтобы инициализировать членов структуры Pimpl. Параметры - создать конструктор для структуры Private и инициализировать их там, или инициализировать их в конструкторе основного класса.Лучшее место для инициализации значений по умолчанию в классе pimpl?

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 имеет тенденцию быть более читабельным и, возможно, более удобным для обслуживания. Есть ли что-то здесь, я не вижу, что может наклонить весы так или иначе?

+0

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

+0

Если класс реализации является агрегатом, вы можете сказать что-то вроде 'p (new MyClass :: Impl {'a', 1, true, {1,2,3}})'. –

ответ

5

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

Если вы должны передать значения из вашего 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, но как насчет более сложных типов?).

+0

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

1

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

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

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

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

Смежные вопросы