Сбой при использовании переменных ссылок в классах
Теоретическая Q
У меня возникли некоторые проблемы при использовании ссылочной переменной в классе.
Класс A
имеет значение int a=0
. Класс B
имеет экземпляр класса A
и ссылается на int &a=A. a
, и таким же образом класс C
имеет экземпляр класса B
и int &a=B. a
. Код разбился при попытке обновить переменную при использовании экземпляра C.
Релевантный Пример
Я создаю библиотеку для TFT+сенсорного экрана 2.4, чтобы легче обрабатывать кнопки.
Библиотеки XPT2046_Touchscreen
и Adafruit_ILI9341
включены здесь, но инициированы в файле main .ino
для облегчения обработки в цикле
и большего контроля в основном коде.
1) Сообщение
Был создан "базовый" класс, называемый MessageTFT
. Этот класс создает графику и сохраняет ее геометрические свойства и цвет, как показано ниже.
2) Кнопка
ButtonTFT
на основе
MessageTFT, добавляющего к нему сенсорные возможности. Его геометрические и цветовые параметры указаны в MessageTFT
. При создании экземпляра кнопки
пользователь определяет ее свойства, но на самом деле она ссылается на MessageTFT
.
ButtonTFT
и MessagesTFT
работают, как и ожидалось. Например, кнопка определена в .ino
:
ButtonTFT butt;
и определено в настройке void()
:
butt.a = 200;
butt.b = 60;
butt.xc = tft.width() / 2;
butt.yc = tft.height() - butt.b / 2;
butt.txt_size = 1;
butt.face_color = ILI9341_CASET;
butt.txt_color = ILI9341_DARKGREY;
butt.border_color = ILI9341_MAGENTA;
butt.roundRect = false;
butt.createButton("Press!");
и в пустом цикле()
:
if (ts.touched())
{
TS_Point p = ts.getPoint();
if (butt.checkPress(p))
{
Serial.print("Pressed");
}
}
3) Застежка на пуговицы
Эволюционирует в buttonArrayTFT
, который включает до 8 кнопок x и y по центру экрана. Здесь снова используется тот же способ ссылки на соответствующие свойства (в buttonArrayTFT
размер и положение каждой кнопки не определяются пользователем, но рассчитываются для оптимизации размера и положения). Теперь переменные ссылаются на экземпляр ButtonTFT, например
&txt_size = _button0.txt_size,
как показано ниже.
Поскольку txt_size
является общим для всех элементов кнопки в этом массиве кнопок, на него ссылаются все кнопки позже в коде.
Также buttonArrayTFT
работает так, как ожидалось, при определении его экземпляра как:
char *a[] = {"All Windows", "Saloon", "Room", "Specific"};
buttonArrayTFT mainWindows;
и создал:
mainWindows.create_array(4, 1, a);
в виде массива кнопок 4X1 с текстом для каждой кнопки, как в a
.
Проблема (сбой кода)
при попытке передать значение для любого из указанных параметров ( в качестве примера возьмем txt_size ):
mainWindows.txt_size = 3; // <----- это
mainWindows.create_array(4, 1, a);
В то время как, делая то же самое при запуске кнопки (см. Вверх), я использую тот же способ обновления размера текста кнопки (который привязан к MessageTFT
).
В чем может быть причина этого ??
Парень
файл .h:
#ifndef TFT_GUI_h
#define TFT_GUI_h
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <XPT2046_Touchscreen.h>
/* Screen calibration */
#define TS_MIN_X 350
#define TS_MIN_Y 350
#define TS_MAX_X 3800
#define TS_MAX_Y 3800
/* ~~~~~~~~~~~~~~~~~ */
/* For Wemos Mini and TFT screen 2.4" */
#define TFT_CS D0
#define TFT_DC D8
#define TFT_RST -1
#define TS_CS D3
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
const uint8_t _pos_corr_factor_x = 3;
const uint8_t _pos_corr_factor_y = 4;
extern XPT2046_Touchscreen ts; /* Touch screen */
extern Adafruit_ILI9341 tft; /* Graphics */
class MessageTFT
{
public:
int xc = 0;
int yc = 0;
uint8_t a = 0;
uint8_t b = 0;
uint8_t txt_size = 3;
uint8_t border_thickness = 1;
char txt_buf[20];
bool roundRect = true;
uint8_t screen_rotation = 0;
uint16_t face_color = ILI9341_GREENYELLOW;
uint16_t txt_color = ILI9341_BLACK;
uint16_t border_color = ILI9341_RED;
Adafruit_ILI9341 *TFT[1];
public:
MessageTFT(Adafruit_ILI9341 &_tft = tft);
void createMSG(char *txt);
void clear_screen(uint8_t c = 0);
private:
void _put_text(char *txt);
void _drawFace();
void _drawBorder();
};
class ButtonTFT
{
public:
/* Following vars belong to MSG class*/
MessageTFT MSGwindow;
int &xc = MSGwindow.xc;
int &yc = MSGwindow.yc;
uint8_t &a = MSGwindow.a;
uint8_t &b = MSGwindow.b;
uint8_t &txt_size = MSGwindow.txt_size;
uint8_t &screen_rotation = MSGwindow.screen_rotation;
uint16_t &face_color = MSGwindow.face_color;
uint16_t &txt_color = MSGwindow.txt_color;
uint16_t &border_color = MSGwindow.border_color;
bool &roundRect = MSGwindow.roundRect;
char *txt_buf = MSGwindow.txt_buf;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
XPT2046_Touchscreen *TS[1];
public:
ButtonTFT(XPT2046_Touchscreen &_ts = ts, Adafruit_ILI9341 &_tft = tft);
void createButton(char *txt);
bool wait4press();
bool checkPress(TS_Point &p);
private:
int _tft_x, _tft_y;
private:
void _put_text();
void _press_cb();
/* fucntions below converts touchscreen values to TFT coordinates */
void _conv_ts_tft(TS_Point &p);
bool _check_press_geometry(TS_Point &p);
int _TS2TFT_x(int px);
int _TS2TFT_y(int py);
};
class buttonArrayTFT
{
public:
buttonArrayTFT(XPT2046_Touchscreen &_ts = ts, Adafruit_ILI9341 &_tft = tft);
void create_array(uint8_t R, uint8_t C, char *but_txt[]);
uint8_t checkPress(TS_Point &p);
public:
uint8_t &txt_size = _button0.txt_size;
uint16_t &txt_color = _button0.txt_color;
uint16_t &border_color = _button0.border_color;
bool &roundRect = _button0.roundRect;
int shift_array = 0;
private:
ButtonTFT _button0;
ButtonTFT _button1;
ButtonTFT _button2;
ButtonTFT _button3;
ButtonTFT _button4;
ButtonTFT _button5;
ButtonTFT _button6;
ButtonTFT _button7;
ButtonTFT *_buttons[8] = {&_button0, &_button1, &_button2, &_button3,
&_button4, &_button5, &_button6, &_button7};
private:
uint8_t _num_items = 0;
};
#endif
Edit1: Как определяются конструкторы в файле .cpp
MessageTFT::MessageTFT(Adafruit_ILI9341 &_tft)
{
TFT[0] = &_tft; // <--- stored in a pointer
}
и
ButtonTFT::ButtonTFT(XPT2046_Touchscreen &_ts, Adafruit_ILI9341 &_tft) : MSGwindow(_tft)
{
MSGwindow.TFT[0] = &_tft;
TS[0] = &_ts;
}
и
buttonArrayTFT::buttonArrayTFT(XPT2046_Touchscreen &_ts, Adafruit_ILI9341 &_tft)
: _button0(_ts, _tft), _button1(_ts, _tft), _button2(_ts, _tft),
_button3(_ts, _tft), _button4(_ts, _tft), _button5(_ts, _tft),
_button6(_ts, _tft), _button7(_ts, _tft)
{
_button0.MSGwindow.TFT[0] = &_tft;
_button0.TS[0] = &_ts;
}
@Guy . D, 👍0
Обсуждение1 ответ
Я не уверен, какую ценность это будет иметь для пользователей Arduino, но вот так. Ваш вопрос, по-видимому, является чрезмерно сложной формой этого следующего:
Мой код:
struct MessageTFT {uint8_t txt_size = 3;};
struct ButtonTFT {
MessageTFT MSGwindow;
uint8_t &txt_size = MSGwindow.txt_size;
};
struct buttonArrayTFT {
uint8_t &txt_size = _button0.txt_size;
ButtonTFT _button0;
};
buttonArrayTFT bat;
void setup() {
Serial.begin(9600);
Serial.println(F("\n\n\nStart."));
}
void loop() {
Serial.println(bat.txt_size++, DEC);
delay(1000);
}
Мой вопрос:
Почему это происходит? ...или не выводите правильное значение? или что-то в этом роде.
Я не думаю, что я подменяю (за неимением лучшего термина) ваш вопрос. Но вопрос какой-то запутанный, так что это лучший ответ на него. Если я прояснил вопрос до такой степени, что отвлекся от темы, так тому и быть. Если у меня есть правильная дистилляция, если ничего другого, то люди могут извлечь из этого то, что ваши вопросы здесь обычно можно упростить.
Объявленный порядок членов в классе имеет отношение к порядку инициализации; в этом есть нечто большее, чем просто это. В любом случае _button0
не был инициализирован до вашего uint8_t &txt_size = _button0.txt_size;
Механизм, лежащий в основе ссылки на элемент в классе, в основном является механизмом указателя. По сути, вы копируете неинициализированный указатель из _button0
в элемент
txt_size
buttonArrayTFT; то есть uint8_t &txt_size = msgWindow.txt_size
еще не выполнен.
Вы можете исправить это, просто переупорядочив участников следующим образом:
struct buttonArrayTFT {
ButtonTFT _button0;
uint8_t &txt_size = _button0.txt_size;
};
Теперь uint8_t &txt_size = msgWindow.txt_size;
в ButtonTFT
выполняется до uint8_t &txt_size = _button0.txt_size;
в buttonArrayTFT
.
Это устранит проблему, как я ее представил. Стоит ли вам это делать? Я не уверен. Но в любом случае это не то, о чем меня спрашивали. Тем не менее, вам может быть полезнее избавиться от ссылочных элементов или преобразовать их во встроенные функции, возвращающие ссылочный тип. Или любое количество других стратегий.
Как минимум, это проблема, с которой сталкивается ваш код.
Re “ _ Вы, по сути, копируете неинициализированный указатель_”: Я понимаю, что, хотя " _button0` на данный момент не был _инициализирован_, он уже _ распределен_. Таким образом, он копирует указатель на неинициализированные данные, а не неинициализированный указатель. Я что-то пропустил?, @Edgar Bonet
Механизм, лежащий в основе " uint8_t &txt_size в
ButtonTFT, грубо говоря, является указателем. Когда ссылка в "ButtonTFT" *привязана* к полю "txt_size" в "msgWindow", она содержит два байта "ButtonTFT" с адресом поля в "msgWindow". Эти два байта (которые в основном являются указателем) не были инициализированы до
uint8_t &txt_size = _button0.txt_size;
uint8_t &txt_size = _button0.txt_size; "привязывает ссылку" txt_size в "buttonArrayTFT
к ее аналогу в элементе "ButtonTFT".Эта привязка фактически является копированием (неинициализированного) указателя., @timemage
Я не уверен, что отдаю должное этому в комментарии, я довольно сильно исказил вышесказанное, пытаясь привести его в соответствие. Если вы хотите, вы можете найти меня на libera.chat, и я объясню там. Или потратьте на это некоторое время, и если это все еще не имеет смысла, я постараюсь обновить ответ позже. "Указатели", о которых я говорю, предназначены только для того, чтобы проиллюстрировать то, что происходит под ссылками, с точки зрения того, что уже может быть понято., @timemage
Теперь у меня в голове все прояснилось, спасибо!, @Edgar Bonet
@timemage Я не понимаю, почему вы предположили, что "_button0" инициализирован неправильно. пожалуйста, смотрите ПРАВКУ 1 в моем вопросе, @Guy . D
В "buttonArrayTFT" у вас есть "uint8_t &txt_size = _button0.txt_size;" и семь строк спустя у вас есть: ButtonTFT _button0;
Если это есть в вашем коде, как вы это показали, независимо от того, что еще есть в вашем коде, тогда то, что я сказал, применимо к ответу., @timemage
- Скетч с несколькими классами (.h и .cpp) – как соединить классы
- Передача двумерного массива в функцию
- Как работают массивы?
- Как передать объект Encoder конструктору другого класса
- Возникла проблема с доступом к значениям из указателя
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Ошибка: "недопустимое использование нестатической функции-члена" при вызове функции из моего собственного класса-метода
- Массив динамического размера в качестве члена класса
Похоже, для этой задачи вам действительно следует использовать наследование и полиморфизм., @Majenko
Например, в моей библиотеке отображения [twButton](https://github.com/CariadDisplayLibrary/Widgets/blob/master/src/twButton.h) наследуется от [Виджета](https://github.com/CariadDisplayLibrary/Cariad/blob/master/src/Cariad.h#L541), который сам по себе является формой [Изображения](https://github.com/CariadDisplayLibrary/Cariad/blob/master/src/Cariad.h#L476) это тогда виртуальная [Кариада](https://github.com/CariadDisplayLibrary/Cariad/blob/master/src/Cariad.h#L135) устройство отображения., @Majenko
@Majenko не могли бы вы, пожалуйста, сказать, что не так в моем коде?, @Guy . D