Статические классы - что я не так делаю?

Тема в разделе "Arduino & Shields", создана пользователем Мерзкий Гуру, 20 апр 2017.

  1. Простой скетч, по всем материалам Интернета должен компилироваться, но не хочет, говорит undefined reference to `statClass::var'.

    Причем, если убрать создание экземпляра класса и вызов функуции - компилируется.

    Код (C++):
    class statClass {
    private:
      static int var;
    public:
      static void fun() { statClass::var = 1; }
    };

    void setup() {
      statClass sc;
      sc.fun();
    }

    void loop() {
    }
    Собственно, задача у меня - научиться передавать адрес метода класса, для этого он должен быть статическим, для этого все переменные класса должны тоже стать статическими, а это уже не компилируется.

    Собственно, уже понял, что это ошибка не компиляции, а сборщика, но от этого не легче. Программа синтаксически верная, но не собирается.
     
    Последнее редактирование: 20 апр 2017
  2. rkit

    rkit Гуру

    Нет и нет.

    Экземпляр для работы со статическим методом не нужен.
    statClass::fun() и всё
     
  3. Что "нет и нет" я не понял, адрес статического метода прекрасно извлекается, и работает.
    Можно вынести все переменные из класса, объявив их глобальными - тоже работает, но это некрасиво.

    Экземпляр для работы со статическим методом не нужен, но и не запрещен.
    Можно экземпляр убрать, на ошибку связывания это никак не влияет, та же ошибка:

    Код (C++):
    class statClass {
    private:
      static int var;
    public:
      static void fun() { statClass::var = 1;}
    };

    void setup() {
      statClass::fun();
    }

    void loop() {
    }
     
  4. О как. Решено, вот так работает:

    Код (C++):
    class statClass {
    private:
      static int var;
    public:
      statClass() { statClass::var = 1; }
      static void fun() { statClass::var = 2;}
    };

    int statClass::var = 0;

    void setup() {
      statClass::fun();
    }

    void loop() {
    }
     
  5. rkit

    rkit Гуру

    Оба утверждения не верны.
     
  6. Faberge

    Faberge Administrator Администратор

    Я не совсем понял, чего вы хотите добиться, но вообще, по правилам С++, а не "материалам Интернета", статическое поле класса перед использованием должно быть определено вне класса где-либо в программе, примерно как по аналогии с глобальными переменными. Достаточно такого:

    Код (C++):
    class statClass
    {
      private:
        static int var;
      public:
        statClass() {}
        void fun() {var++;}
    };

    int statClass::var = 0;
    statClass sc;

    void setup()
    {
      sc.fun();
    }

    void loop() {
    }
    Это связано с тем, что при объявлении класса выделение памяти не производится (только при создании объекта), а статическое поле класса определить все-таки требуется, поэтому оно получается внешним для класса. Как-то так.
     
  7. qwone

    qwone Гик

    Адрес функции передается указателем на функцию. Адрес метода класса передается указателем на метод класса. Вот только метод класса может быть как статическим, так и обычным, и следовательно указатели на эти методы будут разными.
     
  8. qwone, спасибо, конечно за старания по ликбезу.
    Я просто хотел, сделать примерно вот это, попробуйте собрать, и расскажите, что не так?
    Заметьте, что инициализация переменной ptr возражений у компилятора не вызывает.
    И угадайте, какое одно слово нужно добавить в описание метода fun(), чтобы всё собралось?

    Код (C++):
    class test {
    public:
      void fun() { };
    };

    void f() {}
    test obj;

    void setup() {
      void (*ptr)(void) = f;
      ptr = obj.fun;
    }

    void loop() {}
    Игры разума, я уже разобрался. Там всё логично, но не слишком очевидно.
     
  9. qwone

    qwone Гик

    Код (C++):
    class test {
      public:
        void (*fun)(void); // указатель на функцию вне класса
    };

    void f() {} // некая функция
    void (*ptr)(void) = & f; // создадим указатель на функцию f
    test obj;

    void setup() {
      obj.fun = ptr ;
    }

    void loop() {}
    но похоже вы еще не дошли до самого интересного и полезного
     
  10. mcureenab

    mcureenab Гуру

    Код (C++):
    class test {
    public:
      static void fun() { };
    };

    void f() {}
    test obj;

    void setup() {
      void (*ptr)(void) = f;
      ptr = obj.fun;
    }

    void loop() {}
    Но от этого кода не много толку. Указатель ptr ничего не знает про obj. Удобнее namespace использовать.

    Код (C++):
    class test {
    public:
      static void fun() { };
    };

    void f() {}
    test obj;

    namespace AA{
    void fun() {}
     
    };

    void setup() {
      void (*ptr)(void) = f;
      ptr = obj.fun;
      ptr = AA::fun;
    }

    void loop() {}
     
  11. qwone

    qwone Гик

    mcureenab,у меня вопрос почему void (*ptr)(void) = f;
    а не void (*ptr)(void) = &f;
     
  12. mcureenab

    mcureenab Гуру

    Автор языка придумал, что скобки разименовывают указатель в вызов функции. Поэтому имя функции без скобок, это указатель на функцию.

    Насколько знаю оба варианта допустимы и делают одно и то же.

    В самом деле, функция не может быть переменной. Из нее нельзя получить код и присвоить его другой функции. Поэтому нет нужды различать указатель на функцию и собственно функцию.
     
  13. qwone

    qwone Гик

    Код (C++):
    class test {
      public:
        void (*ptr)(void);
        loop() {
          ptr();
        }
    };
    void f() {
      Serial.println("knok-knok");
    }
    test obj;
    void setup() {
      Serial.begin(9600);
      obj.ptr = f; //<- меняя значение obj.ptr, мы можем менять обработчики событий.
    }
    void loop() {
      delay(500);
      obj.loop();
    }
     
  14. qwone

    qwone Гик

    И так может кому интересно Пакет здесь https://yadi.sk/d/XQJnIzg43HEeJa
    Код (C++):
    /* Cl_do_btn.ino
        кнопка
    */

    void none() {}
    void f1() {
      Serial.println("knok1-knok1");
    }
    void f2() {
      Serial.println("knok2-knok2");
    }
    #include "Cl_do_btn.h"
    Cl_do_btn btn1(2, 0, none); //пин 2, 0 уров наж.кнопки , выполнить функцию none()
    Cl_do_btn btn2(3, 0, none); //пин 3, 0 уров наж.кнопки , выполнить функцию none()

    void setup() {
      Serial.begin(9600);
      btn1.setup();
      btn1.Do_Btn = f1;
      btn2.setup();
      btn2.Do_Btn = f2;
    }
    void loop() {
      btn1.loop();
      btn2.loop();
    }
     
  15. mcureenab

    mcureenab Гуру

    Внешняя функция, это не очень интересно. Возможно, указатель на член класса будет практичнее.
    Но, конечно от задачи зависит.
    Указатель на член класса оформляется символом ::* .

    Кроме того можно виртуальные методы использовать.
     
  16. qwone

    qwone Гик

    Ну то что можно использовать и многое другое. Но помимо падения программы из за косяков кода, возможно и "падение"коллег-программистов из их непонимания работы этой программы, даже если код пойдет.
    1) разумеется можно использовать и указатель на член класса. Но он сложнее обычного указателя и опять же полезет перегрузка если вы захотите немного сделать универсальным. Тем более можно внешнюю функцию использовать как посредник между представителями разных классов.
    2) виртуальные методы тянут наследование, что увеличивает колличество строк в исходнике. Но даже не это важное. Надо проектировать общего предка с универсальными методами. Или еще веселее разименовывать указатель под конкретный класс. Пляска с вирт методами нужна если вы обрабатываете пакетом все используемые компоненты. Но у нас всего лишь Ардуина ,а мощное ПК с немеренным объемом ОЗУ.
     
  17. mcureenab

    mcureenab Гуру

    Код (C++):

    class test {
    public:
      void inline operator()() { (this->*f)(); }
      void (test::*f)();
      void fun1() { };
      void fun2() { };
      void fun3() { };
    };

    void setup() {
      test obj;
      obj.f = &test::fun1;
      obj(); // вызов obj.fun1
      obj.f = &test::fun2;
      obj();// вызов obj.fun2
    }

    void loop() {}

     
     
    Последнее редактирование: 22 апр 2017
  18. qwone

    qwone Гик

    А смысл? Полная бессмысленость. И ведь даже применить некуда.
    Зачем городить такой огород, если можно воспользоваться переменной. А оператор () лучше задействовать для конструктора.
     
    Последнее редактирование: 22 апр 2017
  19. mcureenab

    mcureenab Гуру

    Смысл в том, что объектов test может быть много, и у каждого будет свое состояние и поведение. А функция

    Код (C++):

    void f() {
      Serial.println("knok-knok");
    }
     
    , она одна.

    Оператор operator() здесь использован для того, чтобы объект test синтаксически выглядел как функция. Для использования в шаблонах это удобно. Можно один и тот же шаблон использовать как с функцией так и с объектом.
     
  20. Unixon

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

    Чисто процедурные игры какие-то... Зачем класс понадобился?