0

I am just learning c++ and am having trouble with inheritance, comming from a c# background I feel C++ inhertance to be a little more complicated.

I have a base class UserInterface that has virtual and pure virtual methods :

class UserInterface
{
 protected:
    LiquidCrystal* lcd;

const int ICON_WATER_DROP = 0;
const int ICON_SMILEY = 1;
const int ICON_STATION = 2;
const int ICON_WATER_DROPS = 3;
const int ICON_ALARM = 4;
const int ICON_PRESSURE = 5;

/* icon for water drop */
byte waterDrop[8] =
{
        B00100,
        B00100,
        B01010,
        B01010,
        B10001,
        B10001,
        B10001,
        B01110
};

byte bucket[8] =
{
        B01010,
        B00100,
        B00010,
        B11111,
        B10001,
        B11111,
        B10001,
        B11111
};

/* smiley face */
byte smiley[8] =
{
        B00000,
        B10001,
        B00000,
        B00000,
        B10001,
        B01110,
        B00000,
        B00000
};

byte station[8] =
{
        B11110,
        B10010,
        B11111,
        B10011,
        B10011,
        B10011,
        B10010,
        B11111
};

byte alarm[8] =
{
        B00000,
        B00100,
        B01110,
        B01110,
        B01110,
        B11111,
        B00100,
        B00000
};

byte pressure[8] =
{
        B00100,
        B01110,
        B11111,
        B00100,
        B00100,
        B11111,
        B01110,
        B00100
};
public:
UserInterface();
virtual ~UserInterface();

virtual void drawIcon(int icon);
virtual void drawInitializingScreen();
virtual void clearScreen();
virtual void drawText(const __FlashStringHelper *line1);
virtual void drawText(const __FlashStringHelper *line1, const __FlashStringHelper *line2);
virtual void drawText(const __FlashStringHelper *line1, int line2);
virtual void drawAddressSetupScreen(int address);

// All this methods need to be implemented by each subclass
virtual void drawHomeScreen(byte address) = 0;
virtual void drawWateringTotalVolumeScreen(char* remainingTime, char* litres) = 0;
virtual void drawWateringVolumePerMinuteScreen(char* remainingTime, char* litres) = 0;
virtual void drawWateringPressureScreen(char* remainingTime, char* presure) = 0;
virtual void drawWateringPausedScreen(char* remainingTime) = 0;
virtual void drawWateringPausedAlternateScreen(char* remainingTime) = 0;
virtual void drawOutOfService(byte address) = 0;
virtual void drawInvalidSelection() = 0;
};

and the class implementation:

   UserInterface::UserInterface()
{
    //lcd = new LiquidCrystal(12, 11, 5, 4, 3, 2);
    lcd = new LiquidCrystal(5, 6, 7, 8, 9, 10);
    lcd->clear();

    // create a new custom characters
    lcd->createChar(ICON_WATER_DROP, waterDrop);
    lcd->createChar(ICON_SMILEY, smiley);
    lcd->createChar(ICON_STATION, station);
    lcd->createChar(ICON_WATER_DROPS, bucket);
    lcd->createChar(ICON_ALARM, alarm);
    lcd->createChar(ICON_PRESSURE, pressure);
}

UserInterface::~UserInterface()
{

}

void UserInterface::drawIcon(int icon)
{
    // print the custom char to the lcd
    lcd->write(icon);
}

void UserInterface::drawInitializingScreen()
{
    lcd->setCursor(0, 0);
    lcd->print(F("INICIALIZANDO"));
    lcd->setCursor(0, 1);
    lcd->print(F("VER:"));
    lcd->setCursor(5, 1);
    lcd->print(__DATE__);
}

void UserInterface::clearScreen()
{
    lcd->clear();
}

void UserInterface::drawText(const __FlashStringHelper *line1)
{
    lcd->setCursor(0, 0);
    lcd->print(line1);
}

void UserInterface::drawText(const __FlashStringHelper *line1, const __FlashStringHelper *line2)
{
    lcd->setCursor(0, 0);
    lcd->print(line1);
    lcd->setCursor(0, 1);
    lcd->print(line2);
}

void UserInterface::drawText(const __FlashStringHelper *line1, int line2)
{
    lcd->setCursor(0, 0);
    lcd->print(line1);
    lcd->setCursor(0, 1);
    lcd->print(line2);
}

void UserInterface::drawAddressSetupScreen(int address)
{
    lcd->setCursor(0, 0);
    lcd->print(F("ADDRESS SETUP"));
    lcd->setCursor(0, 1);
    lcd->print(address);
}

and a subclass :

class LCD16x2: public UserInterface
{
public:
    LCD16x2();
    ~LCD16x2();

    void drawIcon(int icon);
    void drawInitializingScreen();
    void clearScreen();
    void drawText(const __FlashStringHelper *line1);
    void drawText(const __FlashStringHelper *line1, const __FlashStringHelper *line2);
    void drawText(const __FlashStringHelper *line1, int line2);
    void drawAddressSetupScreen(int address);

    // All this methods need to be implemented by each subclass
    void drawHomeScreen(byte address) override;
    void drawWateringTotalVolumeScreen(char* remainingTime, char* litres) override;
    void drawWateringVolumePerMinuteScreen(char* remainingTime, char* litres) override;
    void drawWateringPressureScreen(char* remainingTime, char* presure) override;
    void drawWateringPausedScreen(char* remainingTime) override;
    void drawWateringPausedAlternateScreen(char* remainingTime) override;
    void drawOutOfService(byte address) override;
    void drawInvalidSelection() override;
};

and the implementation :

LCD16x2::LCD16x2()
{
    lcd->begin(16, 2);
}

LCD16x2::~LCD16x2()
{

}

void LCD16x2::drawHomeScreen(byte address)
{
    lcd->setCursor(0, 0);
    lcd->print(" HUERTOS DE OCIO");

    lcd->setCursor(0, 1);
    lcd->write(ICON_STATION);

    lcd->setCursor(3, 1);
    lcd->print(F("ESTACION "));
    lcd->print(address);
}

void LCD16x2::drawWateringTotalVolumeScreen(char* remainingTime, char* volume)
{
    lcd->setCursor(0, 0);
    lcd->print(F("RIEGO EN CURSO"));

    lcd->setCursor(0, 1);
    lcd->write(ICON_ALARM);
    lcd->setCursor(1, 1);
    lcd->print(remainingTime);

    lcd->setCursor(10, 1);
    lcd->write(ICON_WATER_DROP);
    lcd->setCursor(11, 1);
    lcd->print(volume);
}

void LCD16x2::drawWateringVolumePerMinuteScreen(char* remainingTime,
        char* volumePerMinute)
{
    lcd->setCursor(0, 0);
    lcd->print(F("RIEGO EN CURSO"));

    lcd->setCursor(0, 1);
    lcd->write(ICON_ALARM);
    lcd->setCursor(1, 1);
    lcd->print(remainingTime);

    lcd->setCursor(10, 1);
    lcd->write(ICON_WATER_DROPS);
    lcd->setCursor(11, 1);
    lcd->print(volumePerMinute);
}

void LCD16x2::drawWateringPressureScreen(char* remainingTime, char* pressure)
{
    lcd->setCursor(0, 0);
    lcd->print(F("RIEGO EN CURSO"));

    lcd->setCursor(0, 1);
    lcd->write(ICON_ALARM);
    lcd->setCursor(1, 1);
    lcd->print(remainingTime);

    lcd->setCursor(10, 1);
    lcd->write(ICON_PRESSURE);
    lcd->setCursor(11, 1);
    lcd->print(pressure);
}

void LCD16x2::drawWateringPausedScreen(char* remainingTime)
{
    lcd->setCursor(0, 0);
    lcd->print(F("RIEGO EN PAUSA"));
    lcd->setCursor(0, 1);
    lcd->print(remainingTime);
}

void LCD16x2::drawWateringPausedAlternateScreen(char* remainingTime)
{
    drawWateringPausedScreen(remainingTime);
}

void LCD16x2::drawOutOfService(byte address)
{
    lcd->setCursor(3, 0);
    lcd->print(F("ESTACION "));
    lcd->print(address);
    lcd->setCursor(0, 1);
    lcd->print(F("FUERA DE SERVICIO"));
}

void LCD16x2::drawInvalidSelection()
{
    lcd->setCursor(0, 1);
    lcd->print(F("SELECCION"));
    lcd->setCursor(0, 2);
    lcd->print(F("INVALIDA"));
}

The problem is that the compiler can't seem to accepts calls being made to the virtual methods implemented in the base class :

C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x8e): undefined reference to LCD16x2::drawIcon(int)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x90): undefined reference toLCD16x2::drawInitializingScreen()' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x92): undefined reference to LCD16x2::clearScreen()' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x94): undefined reference toLCD16x2::drawText(__FlashStringHelper const*)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x96): undefined reference to LCD16x2::drawText(__FlashStringHelper const*, __FlashStringHelper const*)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x98): undefined reference toLCD16x2::drawText(__FlashStringHelper const*, int)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x9a): undefined reference to LCD16x2::drawAddressSetupScreen(int)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans3.ltrans.o: In functionupdateFirmwareCommand(unsigned char*, unsigned char*, unsigned char*)': C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:1149: undefined reference to LCD16x2::clearScreen()' C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:1152: undefined reference toLCD16x2::drawText(__FlashStringHelper const*, __FlashStringHelper const*)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans3.ltrans.o: In function selfTest': C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:693: undefined reference toLCD16x2::clearScreen()' C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:704: undefined reference to LCD16x2::drawText(__FlashStringHelper const*, int)' C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans3.ltrans.o: In functionupdateLCD(bool)': C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:160: undefined reference to LCD16x2::clearScreen()' C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:169: undefined reference toLCD16x2::drawInitializingScreen()' collect2.exe: error: ld returned 1 exit status makefile:89: recipe for target 'RMStationFirmware.elf' failed make: *** [RMStationFirmware.elf] Error 1

Marc
  • 2,023
  • 4
  • 16
  • 30
  • 2
    Possible duplicate of [What is an undefined reference/unresolved external symbol error and how do I fix it?](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Matthieu Brucher Nov 27 '18 at 10:41
  • You need to implement your non pure virtual functions (like `drawIcon`). – Matthieu Brucher Nov 27 '18 at 10:42
  • My non pure virtual methods are implemented in the base class, i want to use that implementation on my subclasses – Marc Nov 27 '18 at 10:43

1 Answers1

1
  1. ~LCD16x2() should be virtual.
  2. Do not redeclare methods in derived class, if you are not going to override them. Just remove their declaration from LCD16x2. If you are overriding, declare them as virtual in both base and derived class.
grapes
  • 8,185
  • 1
  • 19
  • 31
  • 1
    Point 2 is not exact. It should be: "If you are overriding, declare them as virtual in base class and **override** in derived class." – Xaxetrov Nov 27 '18 at 10:54
  • And this is a comment, not an answer.When you have enough reputation, you will be able to comment. – Matthieu Brucher Nov 27 '18 at 10:57
  • Agree. Just wanted to notice that both declarations should be marked as "virtual". Unlike C#, C++ doesn't provide explicit directive for overriding. – grapes Nov 27 '18 at 10:57
  • @MatthieuBrucher The compiler message error disappears if LCD16x2 does not _redeclare_ the `drawIcon` function. The issue is that this function is not implemented in LCD12x2 – Damien Nov 27 '18 at 10:59
  • Why it is not an answer, please clarify? Removing duplicate declarations will remove an error. That's exactly what was asked. About virtual dtor - yes, it is a comment. I should only swap my pts 1 and 2. – grapes Nov 27 '18 at 11:00
  • I don't get the down vote, it solved my problem, I made the required changes to my code and now it compiles and works as expected. He helped just give him a break. – Marc Nov 27 '18 at 11:07