OOP ed ereditarietà con Arduino

Buongiorno,

ho imparato ad usare le classi con ide Arduino ma non riesco a gestire l'ereditarietà.
Ho creato una classe Forma con un metodo virtuale chiamato draw(); ed una classe quadrato che eredita Forma e vorrei sovrascrivere il metodo draw() così poi da creare tutte le figure e gestirle nel main con un unico array di tipo Forma.

questo il codice di esempio che sto usando

#ifndef forma_h
#define forma_h

class Forma {

 public:
 virtual void draw();
  
}; 
#endif

Questa è la classe derivata

class Quadrato: public Forma {
  public:
  int a;

  Quadrato(int);
  Quadrato();
  void draw(); 
  
};
Quadrato::Quadrato () {
  a =0;
}

Quadrato::Quadrato(int a_) {
  a=a_;
}

void draw() {
  Serial.println("Sono quadrato");
}

Questo è il main

#import "forma.h"
#import "quadrato.h"
Forma f; 

void setup() {
  Serial.begin(9600);
  f =   Quadrato();
}

void loop() {
  f.draw();
}

Se non metto f.draw() nel loop la compilazione va a buon fine altrimenti mi esce questo errore

sketch\Prova_Ereditariet_.ino.cpp.o:(.literal._Z4loopv+0x4): undefined reference to `Forma::draw()'
sketch\Prova_Ereditariet_.ino.cpp.o: In function `loop()':
C:\Users\39324\OneDrive\Documenti\Arduino\Prova_Ereditariet_/Prova_Ereditariet_.ino:13: undefined reference to `Forma::draw()'
sketch\Prova_Ereditariet_.ino.cpp.o:(.data.f+0x0): undefined reference to `vtable for Forma'
collect2.exe: error: ld returned 1 exit status
exit status 1
Errore durante la compilazione per la scheda ESP32 Dev Module.

NB: l'errore è lo stesso anche e utilizzo Arduino Uno rev3

In java in queste situazioni utilizzo delle interfacce e sovrascrivo i metodi, ma come posso fare con ide Arduino e c++???? (c
Premetto che ho già smanettato su vari tutorial prima di postare ma non sono riuscito a capirci molto. Qualcuno mi saprebbe indirizzare???

Ringrazio come sempre in anticipo per la pazienza e la dedizione che vi contraddistinguono

Credo che qui tu volessi scrivere:

void Quadrato::draw() {
  Serial.println("Sono quadrato");
}

altrimenti draw() è una semplice funzione slegata dalla classe e non un metodo della stessa.

Ciao, Ale.

1 Like

Grazie Ale facendo questa variazione continuava a non funzionare.
Allora ho provato ad aggiungere questo anche nella classe Forma

void Forma::drawer() {
  Serial.println("Sono Forma");
}

Questa volta funziona ma quando mando in esecuzione l'output è

Sono Forma

Mi aspettavo che venisse scritto "Sono Quadrato" visto che F è stato istanziato come quadrato.

f = Quadrato();

Il metodo non viene sovrascritto :frowning:

Cosa sbaglio???

Cmq grazie ancora Ale :wink:

Ci vuole il libro sul C++. Io devo leggerlo ma non ho il tempo.

Dovrebbe funzionare qualcosa di simile a:

class AbsForm {
    public:
    AbsForm() {}
    ~AbsForm() {}
    virtual void draw() = 0;
};

class Quadrato : public AbsForm {
    public:
    Quadrato() {}
    ~Quadrato() {}
    void draw() {
        Serial.print("quadrato\n");
    }
};

Crei istanza di Quadrato nel modo classico:

Quadrato q;

Ad una funzione tipo ad esempio render:

void render(AbsForm &f) {
    f.draw();
}

La chiami con render(q);

Ciao, scusa ma di più non so per il momento.

1 Like

La correzione di @ilguargua è fondamentale. Se non ti compilava c'e' altro da correggere.
Infatti l'errore di compilazione lo diceva chiaramente "undefined reference to `vtable for Forma'"
Hai un metodo virtuale nella Forma che NON è ridefinito nella Quadrato perchè
void draw() è un'altra cosa rispetto a void Quadrato::draw()

Ripubblica tutto il codice, non si capisce come è adesso il codice.
Cosa diavolo è drawer() ?? il metodo è draw()

1 Like
#ifndef forma_h
#define forma_h

class Forma {

 public:
 virtual void drawer();
  
}; 

void Forma::drawer() {
  Serial.println("Sono Forma");
}

#endif
#ifndef quadrato_h
#define quadrato_h
#import "forma.h"

class Quadrato: public Forma {
  public:
  int a;

  Quadrato(int);
  Quadrato();
  void drawer(); 
  
};
Quadrato::Quadrato () {
  a =0;
}

Quadrato::Quadrato(int a_) {
  a=a_;
}

void Quadrato::drawer() {
  Serial.println("Sono quadrato");
}
#import "forma.h"
#import "quadrato.h"
Forma f; 

void setup() {
  Serial.begin(9600);
  f =    Quadrato();


}

void loop() {
  f.drawer();

}

Eccolo tutto scusa avevo cambiato il metodo draw in drawer perchè ho pensato che tante volte potesse essere quache key particolare.

Quello che farei in Java è questo.
1)Creerei un'interfaccia oppure una clase astratta di nome Forma con il solo metodo draw()
2) creerei delle sotto classi (quadrato, triangolo, rettangolo....) che implementano l'interfaccia oppure ereditano la classe Forma
3) creerei un array di tipo Forma dentro il quale potrei mettere tutti gli oggetti (quadrati, triangoli e rettangoli) e poi con un ciclo for richiamerei la funzione draw() comune a tutti gli oggetti.
In questo modo posso gestire con un unico array tutti gli oggetti che ereditano Forma e li potrei far scorrere con un ciclo for.
Spero di essere stato chiaro :slight_smile:
Grazie ancora a tutti

Confesso che è la prima volta che vedo la direttiva #import . Funziona come #include?
Se usi #include cambia qualcosa?

Ciao, Ale.

1 Like

#import vecchia direttiva, dovrebbe assicurare che l'include viene fatto una sola volta. Inutile, anche compilatore dice che è deprecato (obsoleto)

1 Like

Direi che hai fatto molti errori nel programma principale.
Quel Quadrato() che butti ad un certo punto... mica esiste quell'oggetto, manca direttiva "new".
Se la classe Forma è "virtuale", ovvero non ha senso creare oggetto f di tipo Forma ma solo le sue derivazioni...
devi avere un oggetto Quadrato che assegni ad un puntatore Forma o array di puntatori Forma.
Esempio:

#include "forma.h"
#include "quadrato.h"

Forma *f;           // puntatore ad oggetto Forma (potrebbe essere un array)
Quadrato q;       // oggetto reale e globale
void setup() {
  Serial.begin(9600);
  f = (Forma*)&q ;           // f punta a indirizzo di q usando  &q
}
void loop() 
{  q.drawer();             // drawer di q
  f->drawer();            // drawer attraverso f che è puntatore quindi uso ->     punto a un Quadrato
}

oppure

#include "forma.h"
#include "quadrato.h"

Forma *f; 
void setup() {
  Serial.begin(9600);
  f = new Quadrato();
}
void loop() 
{
  f->drawer(); 
}

Esempio con array:

#include "forma.h"
#include "quadrato.h"

Forma *f[3]; 

void setup() {
  Serial.begin(9600);
  f[0] = new Quadrato(1);
  f[1] = new Quadrato(2);
  f[2] = new Quadrato(3);
}

void loop() 
{
  f[0]->drawer(); 
  f[1]->drawer(); 
  f[2]->drawer(); 
}

Meglio se però cambi la Quadrato stampando anche il valore di a:

void Quadrato::drawer() {
  Serial.print("Sono quadrato "); Serial.println(a);
}

Utile leggere (ma in inglese): "Virtual Functions and Runtime Polymorphism in C++ - GeeksforGeeks"

1 Like

Ops è stato solo un errore perché avevo in mente Java ( che devo dire comincia davvero a mancarmi)

Scusate comincio d essere un po' confuso.
In un'altro programma creo un oggetto senza new e non mi da problemi e ho visto vari tutorial che dicono che si fa così.
Ora perchè dovrei iniziare ad usarlo???
Cmq avevo provato ad usarlo ma senza dichiarare f come puntatore (*f) ma non aveva comunque funzionato.
Quanto è complicata l'ereditarietà in c???? AIUTO

Cmq appena posso nel week end faccio delle prove e vi aggiorno.
Nel frattempo veramente grazie a tutti.
Siete spettacolari :slight_smile:

  1. "What is difference between instantiating a C++ object using new vs. without new?"
  2. "new operator - C++ Object without new - Stack Overflow"

o usi questo: ClassObj newObj = ClassObj(p1, p2);
ma crei un oggetto temporaneo che poi viene assegnato
oppure direttamente ClassObj newObj(p1, p2);
ma dovendo passare da array di puntatori (quello che dici di voler fare)
ClassObj *newObj= new ClassObj (p1, p2);
Poi che non ti da problemi... che compila, magari.. ma poi funziona in esecuzione ??

Quando scrivi questo:

Forma f;                 // crei oggetto f di tipo Forma in memoria
void setup() 
{ Serial.begin(9600);
  f =    Quadrato();
}

Crei un oggetto f di tipo Forma in memoria che poi viene distrutto e rimpiazzato da un oggetto anonimo di tipo Quadrato(). Senza la new è nello stack... essendo un oggetto creato in una funzione (setup) l'oggetto muore quando finisce la setup.
Perciò f dentro a loop() punta a ... nulla
Quindi si, puoi non usare new, a patto di capire la differenza tra usarla o no e DOVE usarla.
Ricorda che Arduino ti nasconde il main() classico del C/C++, che è all'incirca:

void main()
{ setup();                 
  while(1) loop();
}

setup() e loop() sono delle semplici funzioni void che devi dichiarare in un programma Arduino. Che hanno quindi visibilità e scoope di una qualsiasi funzione C/C++ (visibilità e vita delle variabili locali/globali)

Comunque non ti scoraggiare, questi sono argomenti complessi del c++. Bisogna digerirli piano piano. Stiamo poi mischiando oggetti, ereditarietà e puntatori.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.