не работает инициализация класса через new
- Войдите на сайт для отправки комментариев
Чт, 30/07/2020 - 00:10
Подскажите, почему вот такой код работает
My_Class a(A,B,C);
void setup() {
a.init();
}
void loop() {
a.run();
}
а вот такой - компилируется без единого предупреждения, но не работает?
My_Class *a;
void setup() {
a = new My_Class(A,B,C);
a->init();
}
void loop() {
a->run();
}
может при такой инициализации какой секрет есть?
Уточняю - внутри конструктора есть выделение динамического массива через malloc. пробовал переносить malloc из конструктора в init() (прочитал, что конструктор при непосредственном вызове и через new работает немного по разному... не помогло)
Прошу прощения, что выкладываю псевдокод - реальный слишком лохматый, раскидан по шести исходным фалам... Если по этому псевдо ничего сказать нельзя - так и скажите. Буду рыть сам.
нельзя
нельзя
правильно я понимаю, что между этими методами инициализации никакой разницы нет вообще? - я вроде много раз использовал и тот и другой - первый раз столкнулся с различиями.
Вот реально в огромной программе меняю только эту инициализацию класса. Эй богу не вру :) Первый код работает, второй нет.
Ладно, спасибо, покопаюсь сам.
В псевдокоду все в порядке. Можете добавить создать простой класс, который, например, печатает строку в serial , и убедитесь сами.
Разница есть, конечно. В первом случае класс живет на стеке, во втором в куче.
В псевдокоду все в порядке. Можете добавить создать простой класс, который, например, печатает строку в serial , и убедитесь сами.
я сам знаю что должно работать :)
написал выше - много раз пользовал и то и то.
Блин, значит где в остальном коде очень хитрый косяк...
Разница есть, конечно. В первом случае класс живет на стеке, во втором в куче.
а это направление для поиска. "Нерабочесть" второго варианта выглядит так, будто динамический буфер не выделился или выделился неверно...
а это направление для поиска. "Нерабочесть" второго варианта выглядит так, будто динамический буфер не выделился или выделился неверно...
А возвращаемый malloc-ом указатель ты на NULL проверяешь?
Ну, хоть конструктор-то покажите! И вообще, сделайте маленький пример. Или маленькие работают? Если так то надо "лохматый" смотреть.
Ну, хоть конструктор-то покажите! И вообще, сделайте маленький пример. Или маленькие работают? Если так то надо "лохматый" смотреть.
показываю:
Конструктор
DMD::DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi ) : pin_DMD_A(_pin_A), pin_DMD_B(_pin_B), pin_DMD_nOE(_pin_nOE), pin_DMD_SCLK(_pin_SCLK), DisplaysWide(panelsWide), DisplaysHigh(panelsHigh), SPI_DMD(_spi) { bool dbuf = false; #if (USE_DUAL_BUFFER == 1) dbuf = true; #endif DisplaysTotal = DisplaysWide * DisplaysHigh; row1 = DisplaysTotal << 4; row2 = DisplaysTotal << 5; row3 = ((DisplaysTotal << 2) * 3) << 2; mem_Buffer_Size = DMD_RAM_SIZE_BYTES * DisplaysTotal; uint16_t allocsize = (dbuf == true) ? (mem_Buffer_Size * 2) : mem_Buffer_Size; matrixbuff[0] = (uint8_t *)malloc(allocsize); // If not double-buffered, both buffers then point to the same address: matrixbuff[1] = (dbuf == true) ? &matrixbuff[0][mem_Buffer_Size] : matrixbuff[0]; backindex = 0; bDMDScreenRAM = matrixbuff[backindex]; // -> Back buffer front_buff = matrixbuff[1 - backindex]; // -> Front buffer #if defined(__STM32F1__) pin_DMD_CLK = SPI_DMD.sckPin(); pin_DMD_R_DATA = SPI_DMD.mosiPin() ; pinMode(pin_DMD_nOE, PWM); SPI_DMD.begin(); #if defined(DMD_USE_DMA) dmd_dma_buf = (byte *)malloc(DisplaysTotal * DMD_DMA_BUF_SIZE); rx_dma_buf = (byte *)malloc(DisplaysTotal * DMD_DMA_BUF_SIZE); spiDmaDev = DMA1; if (SPI_DMD.dev() == SPI1) { spiTxDmaChannel = DMA_CH3; spi_num = 1; } else { spiTxDmaChannel = DMA_CH5; spi_num = 2; } #endif #endif digitalWrite(pin_DMD_A, LOW); // digitalWrite(pin_DMD_B, LOW); // digitalWrite(pin_DMD_CLK, LOW); // digitalWrite(pin_DMD_SCLK, LOW); // digitalWrite(pin_DMD_R_DATA, HIGH); // pinMode(pin_DMD_A, OUTPUT); // pinMode(pin_DMD_B, OUTPUT); // pinMode(pin_DMD_CLK, OUTPUT); // pinMode(pin_DMD_SCLK, OUTPUT); // pinMode(pin_DMD_R_DATA, OUTPUT); // bDMDByte = 0; }Деструктор (добавил только когда появились проблема, раньше не было
DMD::~DMD() { free(matrixbuff[0]); #if defined( DMD_USE_DMA ) free(dmd_dma_buf); free(rx_dma_buf); #endif }члены и методы (сокращено)
//The main class of DMD library class DMD { public: //Instantiate the DMD DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi ); ~DMD(); virtual void init(uint16_t scan_interval = 2000); /* many methods scipped */ uint8_t spi_num = 0; uint16_t WIDTH, HEIGHT; protected: // pins byte pin_DMD_nOE; // active low Output Enable, setting this low lights all the LEDs in the selected rows. Can pwm it at very high frequency for brightness control. byte pin_DMD_A; byte pin_DMD_B; byte pin_DMD_CLK; // is SPI Clock if SPI is used byte pin_DMD_SCLK; byte pin_DMD_R_DATA ; // is SPI Master Out if SPI is used SPIClass SPI_DMD; //Mirror of DMD pixels in RAM byte *bDMDScreenRAM; uint16_t mem_Buffer_Size; uint8_t *matrixbuff[2]; volatile uint8_t backindex = 0; volatile boolean swapflag; volatile uint8_t *front_buff; #if defined(__STM32F1__) dma_channel spiTxDmaChannel; dma_dev* spiDmaDev; uint8_t *dmd_dma_buf; uint8_t *rx_dma_buf; #endif //Display information byte DisplaysWide; byte DisplaysHigh; byte DisplaysTotal; int row1, row2, row3; //scanning pointer into bDMDScreenRAM volatile byte bDMDByte; };собственно, полный код библиотеки есть на Гитхабе , правда там версия чуть старше, но в целом текст совпадает
А возвращаемый malloc-ом указатель ты на NULL проверяешь?
нет, не проверял. Теперь, конечно, проверю... Но вроде памяти дофига, статические переменные занимают 14% ОЗУ, динамические - еще от силы процентов пять...
Кроме того, в первом-то варианте все работает, значит памяти хватает?
Не, так не пойдёт. У конструктора 7 параметров без умолчательных значений, а вызове в первом посте - всего три. Он бы не скомпилировался.
Давайте полный пример, а то каша какая-то.
тогда полный пример вот
https://github.com/board707/DMD_STM32/blob/master/examples/STM32F1/dmd_cyr_chars/dmd_cyr_chars.ino
Только есть нюанс - на этом примере я не проверял, проявляется ли тут описанная проблема.
Я понимаю, что это хамство по вашей классификации, так что давайте я для начала сам попробую :)
Давайте :-)
А нормально вообще SPIClass передавать по значению?
А нормально вообще SPIClass передавать по значению?
согласен, может не слишком эффективно. Но он же небольшой. Хотя надо будет попробовать по ссылке.
Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти. А это значит что компилятор будет делать в таких ситуациях как ему удобно. Не хотите их писать, так закройте их компиляцию.
https://riptutorial.com/ru/cplusplus/example/5421/правило-пяти
Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти.
Круто!
давайте я для начала сам попробую :)
Давайте :-)
попробовал, пример с гитхаба (из стабильного релиза библиотеки) работает в обоих вариантах, из беты - во втором случае выводит на матрицу "молоко" вместо картинки. Значит я что-то в последнем релизе напортачил...
Будем искать.
Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти. А это значит что компилятор будет делать в таких ситуациях как ему удобно. Не хотите их писать, так закройте их компиляцию.
https://riptutorial.com/ru/cplusplus/example/5421/правило-пяти
про правило пяти прочитал, спасибо (впервые слышу, но в принципе все понятно и логично). А вот фразу про "не хотите их писать - закройте компиляцию" - не понял. Что мне нужно закрыть?
Чудеса
После двух пересборок глюк пропал, теперь и мой изначальный пример работает в обоих вариантах.
ХЗ что это было... может когда менял вызовы методов через точку на вызовы через стрелку, где-то опечатался... бывают такие опечатки, что фиг найдешь... если при компиляции ошибок нет.
Объявить их в классе и написать delete. Это наследие осталось когда классы были структурами и компилятор их создавал автоматически. Точнее и сейчас создаст ,если не закрыть их создание. Конечно можно сказать что пользователь с дуру не начнет создавать класс копированием исходного. Но ли. Пользователи бывают очень умными или очень дурными и компилятор начнет создавать то что не закрыто.
Например, допустим вот здесь кнопка в момент включения питания НЕ нажата. Неясно какое именно значение в строке №11 получит m_State, т.к. не определено кто первым инициализируется: thePin или theButton. Пример глупый - только, чтобы выпятить проблему.
struct CPin { CPin(const uint8_t _pin) { pinMode(_pin, INPUT_PULLUP); } }; struct CButton { bool m_State; CButton(const uint8_t _pin) { m_State = ! digitalRead(_pin); // ХЗ !!!! } }; CPin thePin(5); CButton theButton(5); void setup(void) {} void loop(void) {}Более того, порядок их инициализации может измениться при очередной компиляции от любого чиха в исходнике.
А вот в таком варианте всё чётко и однозначно.
struct CPin { CPin(const uint8_t _pin) { pinMode(_pin, INPUT_PULLUP); } }; struct CButton { bool m_State; CButton(const uint8_t _pin) { m_State = ! digitalRead(_pin); // ХЗ !!!! } }; CPin * thePin; CButton * theButton; void setup(void) { thePin = new CPin(5); theButton = new CButton(5); } void loop(void) {}Поэтому, когда нужна сложная инициализация с побочными эффектами, лучше использовать что-то однозначное. Через указатели, конечно, слишком круто - обычно сложную инициализацию делают в специальном методе типа "init()" или "begin()", а не в конструкторе, а уж методы вызывают в правильном порядке.