1

Обновление: Я смотрю, есть ли способ ноль-инициализировать весь класс сразу, потому что технически можно забыть добавить «= 0» или «{}» после каждого члена. В одном из комментариев упоминается, что явно дефолтный аргумент no-arg будет включить нулевую инициализацию во время инициализации значения формы MyClass c{};. Глядя на http://en.cppreference.com/w/cpp/language/value_initialization У меня возникли проблемы с выяснением, какое из утверждений указывает это.Как убедиться, что ваш объект инициализирован нолем?

Инициализация - сложная тема, поскольку C++ 11 изменил значение и синтаксис различных конструкций инициализации. Я не смог собрать достаточно хорошую информацию об этом из других вопросов. Но см., Например, Writing a Default Constructor Forces Zero-Initialization?.

Конкретная проблема, с которой я столкнулся, заключается в следующем: я хочу, чтобы члены моих классов были обнулены как для классов (1), объявляющих по умолчанию c-tor, так и для (2) тех, которые этого не делают.

Для (2) инициализация с помощью {} выполняет эту работу, потому что это синтаксис инициализации значения, который преобразуется в нулевую инициализацию или агрегирует инициализацию, если ваш класс является совокупным случаем, в котором члены, для которых нет инициализатора (все!) ноль-инициализированы.

Но для (1) Я все еще не уверен, какой был бы лучший подход. Из всей информации, которую я собираю, я узнал, что , если вы предоставили по умолчанию c-tor (например, для установки некоторых членов на некоторые значения), вы должны явно нулевать остальные элементы, иначе синтаксис MyClass c = MyClass(); или C++ 11 MyClass c{}; не будет сделайте работу. Другими словами, инициализация значения в этом случае означает просто вызов вашего c-tor, и это все (без нуля).

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

class MyClass 
{ 
    int a; 
    int b; 
    int c; 

    MyClass(int a) 
    { 
    this->a = a; 
    // now b and c have indeterminate values, what to do? (before setting 'a') 


    // option #1 
    *this = MyClass{}; // we lost the ability to do this since it requires default c-tor which is inhibited by declaring this c-tor; even if we declare one (private), it needs to explicitly zero members one-by-one 

    // option #2 
    std::memset(this, 0, sizeof(*this)); // ugly C call, only works for PODs (which require, among other things, a default c-tor defaulted on first declaration) 

    // option #3 
    // don't declare this c-tor, but instead use the "named constructor idiom"/factory below 
    } 


    static MyClass create(int a) 
    { 
    MyClass obj{}; // will zero-initialize since there are no c-tors 
    obj.a = a; 
    return obj; 
    } 
}; 

Является ли это моим правилом? Какой из 3 вариантов будет вы выбрать?

+3

'class MyClass {int a = 0; int b = 0; int c = 0;/* ваши конструкторы * /}; '. Это может быть агрегатом с C++ 14. –

+0

* ", даже если мы объявим один (закрытый), ему необходимо явно обнулить элементы по одному" * Нет, если вы по умолчанию это '= default'. – dyp

+0

@dyp Из того, что я знаю, дефолтный не-arg-c-tor не будет обнулять членов? – haelix

ответ

1

По моему скромному мнению, самый простой способ обеспечить нулевой инициализации для добавления слоя абстракции:

class MyClass 
{ 
    struct 
    { 
     int a; 
     int b; 
     int c; 
    } data{}; 
public: 
    MyClass(int a) : data{a} {} 
}; 

Перемещение элементов данных в структуру позволяет нам использовать инициализацию инициализации для выполнения нулевой инициализации. Конечно, сейчас немного более громоздко получить доступ к этим членам данных: data.a вместо a в пределах MyClass. Конструктор по умолчанию для MyClass выполнит нулевую инициализацию data и всех его членов из-за инициализатора с привязкой для data. Кроме того, мы можем использовать агрегатную инициализацию в конструкторах MyClass, которые также инициализируют инициализацию этих элементов данных, которые явно не инициализированы.

Недостатком косвенного доступа членов данных могут быть преодолены с помощью наследования вместо агрегацию:

struct my_data 
{ 
    int a; 
    int b; 
    int c; 
}; 

class MyClass : private my_data 
{ 
    MyClass() : my_data() {} 
public: 
    MyClass(int a) : MyClass() { this->a = a; } 
}; 

Путем явного указания базового-инициализатор my_data(), стоимость инициализации вызывается как ну, что приводит к нулевой инициализации. Этот конструктор по умолчанию должен быть помечен как constexpr и noexcept. Обратите внимание, что это уже не тривиально. Мы можем использовать инициализацию вместо присваивания с помощью агрегированных инициализации или переадресации конструкторов:

class MyClass : private my_data 
{ 
public: 
    MyClass(int a) : my_data{a} {} 
}; 

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

template<typename T> 
struct zero_init_helper : public T 
{ 
    zero_init_helper() : T() {} 
}; 

struct my_data 
{ 
    int a; 
    int b; 
    int c; 
}; 

class MyClass : private zero_init_helper<my_data> 
{ 
public: 
    MyClass(int a) { this->a = a; } 
}; 

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

template<typename T> 
struct zero_init_helper : public T 
{ 
    zero_init_helper() : T() {} 

    template<typename... Args> 
    zero_init_helper(Args&&... args) : T{std::forward<Args>(args)...} {} 
}; 

class MyClass : private zero_init_helper<my_data> 
{ 
public: 
    MyClass(int a) : zero_init_helper(a) {} 
}; 

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


Также можно реализовать ваш вариант # 1 следующим образом:

class MyClass 
{ 
    int a; 
    int b; 
    int c; 

    MyClass() = default; // or public, if you like 

public: 
    MyClass(int a) 
    { 
    *this = MyClass{}; // the explicitly defaulted default ctor 
         // makes value-init use zero-init 
    this->a = a; 
    } 
}; 

насчет конструктора делегации?

class MyClass 
{ 
    int a; 
    int b; 
    int c; 

    MyClass() = default; // or public, if you like 

public: 
    MyClass(int a) : MyClass() // ctor delegation 
    { 
     this->a = a; 
    } 
}; 

[class.base.init]/7 показывает, что в приведенном выше примере будет ссылаться на значение инициализации, что приводит к нулевой инициализации так как класс не имеет user- предоставленных по умолчанию конструкторов [DCL. INIT] /8.2. Недавние версии clang ++ кажутся нулевым инициализацией объекта, последние версии g ++ этого не делают. Я сообщил об этом как g++ bug #65816.

+0

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

+0

@haelix Я подал отчет об ошибке g ++. – dyp

4

Как насчет использования инициализации в классе?

class Foo 
{ 
    int _a{}; // zero-it 
    int _b{}; // zero-it 
public: 
    Foo(int a): _a(a){} // over-rules the default in-class initialization 
}; 
3

Вариант 4 и 5:

вариант 4:


MyClass(int a) :a(a), b(0), c(0) 
    { 
    } 

вариант 5:


class MyClass 
    { 
     int a = 0; 
     int b = 0; 
     int c = 0; 

     MyClass(int a) : a(a) { 
     } 
    }