Сбой при использовании переменных ссылок в классах

Теоретическая 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;
}

, 👍0

Обсуждение

Похоже, для этой задачи вам действительно следует использовать наследование и полиморфизм., @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


1 ответ


3

Я не уверен, какую ценность это будет иметь для пользователей 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_sizebuttonArrayTFT; то есть 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