Не передается структура в качестве параметра в функцию?

Тема в разделе "Arduino & Shields", создана пользователем DrProg, 8 июл 2015.

  1. Megakoteyka

    Megakoteyka Оракул Модератор

    Вот так это будет выглядеть, если оформить шарики в виде класса.
    Обратите внимание - внутренние переменные класса закрыты от внешнего кода. Обратиться к ним можно только через методы класса. Это идеология инкапсуляции данных - класс "знает", как правильно с ними обращаться и не дает к ним доступа постороннему коду, который этого "не знает". Данные скрыты внутри класса, а наружу торчат только методы, которые умеют с этими данными работать.
    В больших программах с множеством разных классов это очень актуально.
    Код (Text):
    //#include "FastIO.h"

    #define DATA_PIN    13
    #define LATCH_PIN  12
    #define CLOCK_PIN  11

    byte masRG [8][8] = {    // поле
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0, 0, 0, 0,
    };

    class runBall
    {          //--------------------- класс шарика
    private:
      byte posX;              // позиция X
      byte posY;              // -!!- Y
      byte runX;              // вектор по X (1 - влево, 0 - на месте, 2 - вправо)
      byte runY;              // -!!- Y
      byte color;            // цвет
      int MS;                // время задержки между шагом (мс)
      unsigned long TS;      // таймер

    public:
      runBall(byte px, byte py, byte rx, byte ry, byte col, long unsigned int ms)// конструктор класса
      {
        posX = px;
        posY = py;
        runX = rx;
        runY = ry;
        color = col;
        MS = ms;
        TS = millis();
      }
     
      void setBall ()
      {                    // функция шага
        masRG[posY][posX] = 0;            // тушим прошлую позицию
        posX = posX + runX - 1;            // сдвигаем по X
        if ((posX > 7) or (posX < 0))      // определяем пределы поля
        {
          runX = abs(runX - 2);            // если вышли то возвращаемся и меняем направление на "отскок"
          posX = posX + (runX - 1);
          posY = posY - (runY - 1);
        }
        posY = posY + (runY - 1);          // то же самое Y
        if ((posY > 7) or (posY < 0))
        {
          runY = abs(runY - 2);
          posY = posY + (runY - 1);
          posX = posX - (runX - 1);
        }
        masRG[posY][posX] = color;          // зажигаем в новом месте
      }
     
      void update()
      {
        if ((millis() - TS) >= MS)
        {
          setBall();                          // которому пора, тот сдвигаем
          TS = millis();                      // и обнуляем ему таймер
        }  
      }
    };

    // определяем три шарика
    runBall ballR(1, 2, 0, 2, 1, 100);
    runBall ballG(6, 6, 0, 2, 2, 50);
    runBall ballY(1, 7, 2, 2, 3, 150);

    // массив указателей на шарики
    runBall* mBall[3] = {
      &ballR,
      &ballG,
      &ballY
    };
    void setup()
    {
      /*MC_SET_PIN_OUTPUT(DATA_PIN);
      MC_SET_PIN_OUTPUT(CLOCK_PIN);
      MC_SET_PIN_OUTPUT(LATCH_PIN);*/
    }

    void loop() {
      for (byte i = 0; i < 3; i++)                    // проверяем таймеры на время шага
        mBall[i]->update();
      setKadr_RG();                                    // рисуем поле с шарами
    }

    void setKadr_RG () {                                  // отрисовка кадра
      /*byte bytSTR;
      byte mR;
      byte mG;
      for (byte s = 0; s <= 7; s++) {
        bytSTR = 0;
        bitSet(bytSTR, s);
        for (byte i = 0; i <= 7; i++) {
          bitWrite(mR, i, !(bitRead(masRG[s][i], 0)));
          bitWrite(mG, i, !(bitRead(masRG[s][i], 1)));
        }
        MC_WRITE_PIN(LATCH_PIN, LOW);
        writeByte(bytSTR);
        writeByte(mR);
        writeByte(mG);
        MC_WRITE_PIN(LATCH_PIN, HIGH);
      }*/
    }

    /*void writeByte(byte byteW) {                          // аналог shiftOut, работает чуть быстрее
      for (int i = 7; i >= 0; i--)
      {
        MC_WRITE_PIN(DATA_PIN, (bitRead(byteW, i)));
        MC_WRITE_PIN(CLOCK_PIN, HIGH);
        MC_WRITE_PIN(CLOCK_PIN, LOW);
      }
    }*/
    Все упоминания библиотеки FastIO.h закомментировал, т.к. у меня ее нет.
     
    DrProg нравится это.
  2. Megakoteyka

    Megakoteyka Оракул Модератор

    Директива #define DDD false говорит препроцессору о том, что все вхождения DDD в тексте программы нужно заменить на false.
    В результате вместо #if DDD==true получится #if false==true. Странная конструкция, не правда ли?
     
  3. DrProg

    DrProg Вечный нерд

    Странная, но ведь верная в итоге то. )
     
  4. DrProg

    DrProg Вечный нерд

    Очень интересно! Получается снаружи переменных класса не видно, но изнутри класса видно глобальные переменные (в данном случае массив)?
    А вот если, к примеру, мне захотелось изменить какой-либо один параметр в шарике? Я смогу это сделать извне класса или нет?

    И еще вопрос, можно ли в принципе заполнение структуры:
    Код (Text):
      mBall[1].posX = 6;      // зеленый
      mBall[1].posY = 6;
      mBall[1].runX = 0;
      mBall[1].runY = 2;
      mBall[1].color = 2;
      mBall[1].MS = 50;
    сделать в одну строку, как в вашем примере с классом?


    пс: могу убрать FastIO, заменить обычными digitalWrite, тем более что в данном случае разница в скорости на глаз не заметна.
     
    Последнее редактирование: 10 июл 2015
  5. Megakoteyka

    Megakoteyka Оракул Модератор

    Для этого нужно написать функцию, которая будет менять этот параметр.
    Если переменную вынести в раздел public, она будет видна снаружи и доступна точно так же, как и в структуре. По сути структура - тот же класс, у которого все переменные и методы являются открытыми, но могут быть закрыты модификатором private. А класс - это структура, у которой все закрыто, но можно открыть при помощи модификатора public.
     
  6. Unixon

    Unixon Оракул Модератор

    Пишете как на джаве, где реализация в только и может быть в теле класса. В C++ так пишутся только примитивные функции в один оператор. Обычно делается так:

    Код (Text):

    class runBall
    {
    // ...
    public:
       runBall(byte px, byte py, byte rx, byte ry, byte col, long unsigned int ms);
       void setBall ();
       void update();
    };

    runBall::runBall(byte px, byte py, byte rx, byte ry, byte col, long unsigned int ms)
    {
      // ...
    }

    void runBall::setBall ()
    {
    // ...
    }

    void runBall::update()
    {
      // ...
    }
     
    При этом объявление класса остается в заголовочном .h файле, а реализация размещается в .cpp файле.
     
    DrProg нравится это.
  7. Megakoteyka

    Megakoteyka Оракул Модератор

    Это я знаю, для простоты сократил, да и лень было про два файла в 2 часа ночи рассказывать :)
     
  8. DrProg

    DrProg Вечный нерд

    Спасибо конечно, но пока игра не стоит таких свеч, разве что из образовательных целей.
     
  9. Alex19

    Alex19 Гуру

    Все зависит от привычек и рациональности. Рассмотрим пример, когда Ваше приложение разрастается по мере написания кода. Возьмем простой пример, нужно указать тип конфигурации Rov

    Вот, как было бы сделано, по схеме которую Вы использовали
    Код (Text):
    //***************** Тип конфигурации для отдельных ROV **********************//
    // Конфигурация для ROV, 1 - Simple Rov, 2 - Андрея К.
    #define CONF_ROV 1
    //***************** /Тип конфигурации для отдельных ROV *********************//
     
    Вот как у меня
    Код (Text):
    //***************** Тип конфигурации для отдельных ROV **********************//
    // Конфигурация для ROV - Simple Rov
    #define CONF_ROV_SIMPLE
    // Конфигурация для ROV - Андрея К.
    //#define CONF_ROV_ANDREI_K
    //***************** /Тип конфигурации для отдельных ROV *********************//
     
    Вроде разницы нет, но при работе с кодом была бы большая разница.
    Вот пример
    Код (Text):
    // Если тип Rov - Simple Rov
    #if CONF_ROV == 1
        // Объявляем тип конфигурации моторов
        // 2 ходовых мотора, 1 на подъем, смещен к ходовым
        #define MOTOR_CONF_2FORWARD_1UP_DISPLACED      
    #endif
    И мой вариант
    Код (Text):
    // Если тип Rov - Simple Rov
    #if defined(CONF_ROV_SIMPLE)
        // Объявляем тип конфигурации моторов
        // 2 ходовых мотора, 1 на подъем, смещен к ходовым
        #define MOTOR_CONF_2FORWARD_1UP_DISPLACED    
    #endif
    В моем варианте Вы сразу понимаете о каком типе Rov идет речь, не нужно возвращаться к месту объявления и читать комментарии. Можно возразить, у Вас мало конфигураций можно запомнить, а что если таких макроподстановок у Вас тьма

    Еще пример
    Код (Text):
    //***********************  Определяем тип ардуины  **************************//
    #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega2560__)
       // Частота процессора
      #define F_CPU 16000000UL                        

      // Определяем тип ардуины
      #if defined(__AVR_ATmega328P__)
        // Если ArduinoProMini
        #define ARDUINO_PRO_MINI
      #elif defined(__AVR_ATmega32U4__)
        // Если ArduinoProMicro
        #define ARDUINO_PRO_MICRO
      #elif defined(__AVR_ATmega2560__)
        // Если ArduinoMega
        #define ARDUINO_MEGA
      #endif
    #else
      #Error this board not support!
    #endif
    //***********************  /Определяем тип ардуины  *************************//
    И как просто читается
    Код (Text):
    //******************  Soft Serial в ATmega32U4 (Promicro)  ******************//
    #if defined(ARDUINO_PRO_MICRO)
      #define USB_CDC_TX        3
      #define USB_CDC_RX        2
    #endif
    //******************  /Soft Serial в ATmega32U4 (Promicro)  *****************//
    А если использовать ARDUINO_BOARD = 1,2,3 и т.д., код стало бы сложнее поддерживать.

    Кроме удобства чтения, макроподстановки не проверяются компилятором и если бы Вы отвлеклись и ошиблись со значением, поиск ошибки мог бы быть долгим. Поэтому, после того как наступив однажды на эти грабли избегаю лишних значений, там где они не нужны.

    Если принять правило, что комментарии пишутся над строкой кода, не вижу в этом проблемы
    Код (Text):
    // Конфигурация для ROV - Андрея К.
    //#define CONF_ROV_ANDREI_K
    UPD. Мне общее понимание указателей, дал данный справочник по СИ - http://lord-n.narod.ru/download/books/walla/programming/Spr_po_C/05/05.htm, возможно он будет полезен и Вам.
     
    Последнее редактирование: 10 июл 2015