Как убедиться, что ваш объект инициализируется нулями?
Обновление: я смотрю, есть ли способ обнуления всего класса сразу, потому что технически можно забыть добавить ' = 0
' или же ' {}
после каждого члена. В одном из комментариев упоминается, что явно заданный по умолчанию аргумент no-arg c-tor позволит инициализировать ноль во время инициализации значения формы MyClass c{};
, Глядя на http://en.cppreference.com/w/cpp/language/value_initialization я не могу понять, какие из утверждений указывают это.
Инициализация сейчас является сложной темой, поскольку C++11 изменил значение и синтаксис различных конструкций инициализации. Я не смог собрать достаточно информации об этом из других вопросов. Но посмотрите, например, написание конструктора по умолчанию для принудительной инициализации нулями?,
Конкретная проблема, с которой я сталкиваюсь, заключается в следующем: я хочу убедиться, что члены моих классов обнуляются как для (1) классов, которые объявляют c-tor по умолчанию, так и для (2) тех, которые этого не делают.
Для (2), инициализация с {}
выполняет работу, потому что это синтаксис для инициализации значения, который переводится в инициализацию с нуля или в агрегированную инициализацию, если ваш класс является агрегатным - случай, когда члены, для которых не был предоставлен инициализатор (все!), инициализируются нулями.
Но для (1) я все еще не уверен, что будет лучшим подходом. Из всей информации, которую я собираю, я узнал, что если вы предоставляете c-tor по умолчанию (например, для установки некоторых членов на некоторые значения), вы должны явно обнулить оставшиеся члены, в противном случае синтаксис MyClass c = MyClass();
или C++11 MyClass c{};
не буду делать работу. Другими словами, инициализация значения в этом случае означает просто вызов вашего c-tor, и все (без обнуления).
Вы попадаете в ту же ситуацию, если объявляете c-tor, который принимает значения и устанавливает эти значения для подмножества членов, но вы хотите, чтобы другие члены были нулевыми: для этого не существует сокращений -I задумываюсь о 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
{
struct
{
int a;
int b;
int c;
} data{};
public:
MyClass(int a) : data{a} {}
};
Перемещение элементов данных в структуру позволяет использовать инициализацию значения для выполнения нулевой инициализации. Конечно, теперь немного труднее получить доступ к этим элементам данных: data.a
вместо просто a
в MyClass
, Конструктор по умолчанию для MyClass
выполнит нулевую инициализацию data
и все его члены из-за инициализации braced для 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 of 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 предполагает, что приведенный выше пример должен вызывать инициализацию значения, что приводит к инициализации нуля, поскольку у класса нетпредоставленных пользователем конструкторов по умолчанию [dcl.init]/8.2. Последние версии clang++, кажется, инициализируют объект с нуля, а последние версии g ++ - нет. Я сообщил об этом как об ошибке g ++ # 65816.
Как насчет использования инициализации в классе?
class Foo
{
int _a{}; // zero-it
int _b{}; // zero-it
public:
Foo(int a): _a(a){} // over-rules the default in-class initialization
};
Вариант 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) {
}
}