Проблема с передачей указателя строки от дочернего к родительскому
Я пытаюсь передать указатель на const char * от дочернего к родительскому, но явно не понимаю, как это сделать правильно. Вот код, который содержит три класса: 1) родитель 2) Альфа (ребенок) 3_ Бета (дочерняя)
#pragma once
class Parent {
protected:
char *childName;
public:
Parent ( char* _childName ) {
childName = _childName;
}
char *getChildName () {
return childName;
}
};
class Alpha: public Parent {
protected:
const char* alphaName = "ALPHA";
public:
Alpha (): Parent ( alphaName ) {
Serial.print ( F ( "My name is " ) );
Serial.println ( getChildName () );
}
};
class Beta: public Parent {
protected:
const char* betaName = "BETA";
public:
Beta (): Parent ( betaName ) {
Serial.print ( F ( "My name is " ) );
Serial.println ( getChildName () );
}
};
Alpha *alpha;
Beta *beta;
void setup () {
Serial.begin ( 115200 );
while (!Serial.availableForWrite ()) {}
alpha = new Alpha ();
beta = new Beta ();
}
void loop() {}
И вот что я получаю: Меня зовут �
Меня зовут ˵
@Bob Jones, 👍1
Обсуждение1 ответ
Лучший ответ:
Проблема в том, что alphaName
и betaName
являются переменными-членами. Это означает, что они инициализируются во время конструкторов Alpha
и Beta
. Однако конструктор суперкласса Parent
всегда вызывается первым, до вызова конструкторов дочернего класса. Результат: Parent::Parent(char *)
вызывается с неинициализированным указателем.
Вот ваш код в Compiler Explorer: https://godbolt.org/z/otAuQU
Как видите, Alpha::Alpha()
компилируется в следующее:
call Parent::Parent(char*)
ldd r24,Y+1
ldd r25,Y+2
ldi r18,lo8(.LC0)
ldi r19,hi8(.LC0)
.LC0
— это строка "АЛЬФА".
Решение здесь состоит в том, чтобы сделать имена статическими. Таким образом, они инициализируются до вызова родительского конструктора.
class Parent {
protected:
const char *const childName;
public:
Parent(const char* childName) : childName(childName) {}
const char *getChildName () const {
return childName;
}
};
class Alpha : public Parent {
protected:
constexpr static const char *alphaName = "ALPHA";
public:
Alpha() : Parent(alphaName) {
Serial.print(F("My name is "));
Serial.println(getChildName());
}
};
Компилируется в:
ldi r22,lo8(.LC0)
ldi r23,hi8(.LC0)
call Parent::Parent(char const*)
Как уже упоминалось, указатели на строковые литералы всегда должны быть константными. Единственная причина, по которой это не дает ошибки, заключается в том, что разработчики Arduino решили, что было бы неплохо скомпилировать все с помощью -fpermissive
...
Изменение строкового литерала является поведением undefined, поэтому возникает ошибка.
И награда достается тттапе, который своим точным и правильно работающим образцом кода поджарил несколько оставшихся клеток моего мозга. Спасибо, тттапа! И теперь у меня много кода, который нужно исправить., @Bob Jones
@BobJones, я думаю, что использования const char*
должно быть достаточно, и Эдгар Бонет сообщил, что это работает в комментарии. Но мы не знаем, какую платформу вы используете., @Juraj
@Juraj, я думаю, что это все еще неопределенное поведение. Причина, по которой он работает, заключается в том, что компилятор оптимизирует его, потому что знает, что он будет постоянным. Однако нет никакой гарантии, что указатель должен быть инициализирован до вызова родительского конструктора, это просто счастливое совпадение., @tttapa
Ссылка на C++ «Перед началом выполнения составного оператора, формирующего тело функции конструктора, завершается инициализация всех прямых баз, виртуальных баз и нестатических элементов данных». https://en.cppreference.com/w/cpp/language/initializer_list, @Juraj
@Juraj, я не говорю о теле конструктора, я говорю о родительском конструкторе. Сначала вызывается конструктор Parent::Parent(const char *)
, инициализируются переменные-члены Parent
(childName
), затем инициализируются элементы данных Alpha
(alphaName
), и, наконец, выполняется тело Alpha::Alpha()
.
Я только что протестировал исходный код с добавленной константой на UNO с версией 1.6.23 AVR Core. Это не работает. Когда имена статичны, это работает., @tttapa
- Почему считается плохой практикой использовать ключевое слово "new" в Arduino?
- Работает с gcc, но не с Arduino. ошибка: taking address of temporary array
- Получить массив символов с помощью модуля SIM900
- Динамическое изменение стека вызовов с помощью указателей
- Доступ к функции в объекте через `->` приводит к сбою (сбросу) Arduino
- Разбор массива объекта в конструкторе библиотеки
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
попробуйте добавить const ко всем char*. на какой архитектуре mcu вы его компилируете и запускаете?, @Juraj
Я попробовал ваш код после добавления отсутствующего
const
для его компиляции и получил ожидаемый результат: «Меня зовут АЛЬФА\r\nМеня зовут БЕТА\r\n»., @Edgar BonetДавным-давно старший программист сказал мне, что правильный способ определения литералов состоит в том, чтобы сделать
const char betaName[] = "BETA";
, к сожалению, я не могу вспомнить точных деталей, но это было связано с тем, как была организована память. выделено. * ОДНАКО * это было еще в 90-х, так что, надеюсь, сейчас компиляторы лучше., @Code GorillaКороткая версия заключается в том, что const побуждает компилятор использовать память программы (которой обычно больше) для хранения значения, а не память данных (которой обычно меньше). Заметили все «определения», которые я использовал? Это связано с тем, что разные комбинации компиляторов и платформ, скорее всего, будут давать разные результаты., @st2000