Делегирующий конструктор дает ошибку сегментации при использовании поля класса для аргумента
Фактически ошибка сегментации происходит в другой программе, которую я пытался скомпилировать, которая происходит из-за этого поведения.
Мой вопрос:
Это ошибка или моя ошибка?
Воспроизводится любым способом (даже если поле something
является приватным или защищенным), и вот мой пример:
main.cc:
#include <iostream>
class Test {
public:
const char* something = "SOMETHING HERE!!!";
Test(const int& number) : Test(something, number) { }
// XXX: changed `something` to `_something` to make it different
Test(const char* _something, const int& number) {
std::cout << _something << std::endl;
std::cout << number << std::endl; }
~Test() { }
};
int main(int argc, char* argv[]) {
Test te1(345);
Test te2("asdasdad", 34523);
return 0;
}
И вот что происходит при компиляции с помощью:
g++ main.cc -Os -o main
и работает с:
./main
вывод:
[email protected]:~/ $ ./main
A"�~ <-- this is random
345
asdasdad
34523
Но когда я включаю оптимизацию с помощью -O0
или -O1
или -O2
... вывод представляет собой только новую строку:
[email protected]:~/ $ ./main
[email protected]:~/ $
Версия g++:
[email protected]:~/ $ g++ --version
g++ (Raspbian 6.3.0-18+rpi1) 6.3.0 20170516
Ответы
Ответ 1
const char* something = "SOMETHING HERE!!!";
Инициализатор по умолчанию справа, как следует из его имени, используется только тогда, когда вы не предоставляете явный инициализатор в списке инициализаторов конструктора. Давайте посмотрим на ваши:
Test(const int& number) : Test(something, number) { }
Хорошо, мы делегируем другой конструктор. Этот другой конструктор выполнит полную инициализацию, поэтому инициализатор по умолчанию не используется. Но... мы передаем неинициализированное значение something
в качестве параметра.
Test(const char* _something, const int& number) { /* ... */ }
О-оу. Теперь мы пытаемся использовать значение _something
, которое является копией something
, которая является неопределенной. Undefined Поведение и пожар.
Вы действительно не должны передавать значение члена класса в качестве параметра в свой конструктор, если у вас нет бесконечной поставки огнеупорных цыплят и яиц.
Поведение, которое вы ищете, можно получить, поместив значение по умолчанию в вызов конструктору делегата:
Test(const int& number) : Test("SOMETHING HERE!!!", number) { }
... или сохранить его в заданной статической переменной:
static constexpr char *const defaultSomething = "SOMETHING HERE!!!";
Test(const int& number) : Test(defaultSomething, number) { }
Ответ 2
Это ошибка или моя ошибка?
О, это ваша вина. Инициализатор элемента по умолчанию используется только для инициализации объекта-члена в конструкторе без делегирования. Согласно [class.base.init]/9, мое внимание:
В конструкторе без делегаций, если данный потенциально сконструированный подобъект не обозначается идентификатором mem-initializer-id (включая случай, когда нет списка mem-initializer-list, поскольку конструктор не имеет ctor-инициализатора), то
- если объект является нестатическим членом данных, у которого есть инициализатор элемента по умолчанию, и либо [...] объект инициализируется из его инициализатор элемента по умолчанию, как указано в [dcl.init];
Итак, something
не инициализируется, когда вы передаете его целевому конструктору. Ваша программа имеет поведение undefined и идет спад.