【问题标题】:C++ class inherance, undefined reference to base class member fuctionC++类继承,对基类成员函数的未定义引用
【发布时间】:2019-04-29 01:58:42
【问题描述】:

我刚学c++,继承有问题,有c#背景,感觉C++的继承有点复杂。

我有一个基类 UserInterface,它具有虚拟和纯虚拟方法:

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;
};

和类实现:

   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);
}

和一个子类:

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;
};

和实施:

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"));
}

问题在于编译器似乎无法接受对基类中实现的虚拟方法的调用:

C:\Users\marcp\AppData\Local\Temp\ccjzyoeN.ltrans2.ltrans.o:(.rodata+0x8e): 未定义引用 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): 未定义引用 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): 未定义引用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): 未定义引用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: 未定义引用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:在函数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:未定义对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: 未定义引用LCD16x2::clearScreen()' C:\Users\marcp\Dropbox\Source\RiegoMatic\HDO.RiegoMatic.Station.Firmware\Release/..\RMStationFirmware.cpp:169: undefined reference toLCD16x2::drawInitializingScreen()' collect2.exe:错误:ld 返回 1 退出状态 makefile:89: 目标“RMStationFirmware.elf”的配方失败 make: *** [RMStationFirmware.elf] 错误 1

【问题讨论】:

标签: c++ inheritance


【解决方案1】:
  1. ~LCD16x2() 应该是虚拟的。
  2. 不要在派生类中重新声明方法,如果您不打算覆盖它们。只需从 LCD16x2 中删除他们的声明即可。如果要覆盖,请在基类和派生类中将它们声明为虚拟。

【讨论】:

  • 第 2 点不准确。应该是:“如果您要覆盖,请在基类中将它们声明为虚拟,在派生类中声明为 覆盖。”
  • 这是一个评论,不是一个答案。当你有足够的声望,你就可以评论了。
  • 同意。只是想注意两个声明都应该标记为“虚拟”。与 C# 不同,C++ 不提供用于覆盖的显式指令。
  • @MatthieuBrucher 如果 LCD16x2 未重新声明 drawIcon 函数,编译器消息错误就会消失。问题是 LCD12x2 中没有实现此功能
  • 为什么不是答案,请澄清?删除重复声明将删除错误。这正是被问到的。关于虚拟 dtor - 是的,这是一条评论。我应该只交换我的 pts 1 和 2。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多