Ongedefinieerde verwijzing naar vtable

Bij het bouwen van mijn C++-programma krijg ik de foutmelding

ongedefinieerde verwijzing naar ‘vtable…

Wat is de oorzaak van dit probleem? Hoe los ik het op?


Het gebeurt zo dat ik de fout krijg voor de volgende code (de klasse in kwestie is CGameModule.) en ik kan voor mijn leven niet begrijpen wat het probleem is. Ik dacht eerst dat het te maken had met het vergeten om een ​​virtuele functie een body te geven, maar voor zover ik het begrijp, is alles hier. De overervingsketen is een beetje lang, maar hier is de bijbehorende broncode. Ik weet niet zeker welke andere informatie ik moet verstrekken.

Opmerking: de constructor is waar deze fout optreedt, zo lijkt het.

Mijn code:

class CGameModule : public CDasherModule {
 public:
  CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
  : CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
  { 
      g_pLogger->Log("Inside game module constructor");   
      m_pInterface = pInterface; 
  }
  virtual ~CGameModule() {};
  std::string GetTypedTarget();
  std::string GetUntypedTarget();
  bool DecorateView(CDasherView *pView) {
      //g_pLogger->Log("Decorating the view");
      return false;
  }
  void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }
  virtual void HandleEvent(Dasher::CEvent *pEvent); 
 private:
  CDasherNode *pLastTypedNode;
  CDasherNode *pNextTargetNode;
  std::string m_sTargetString;
  size_t m_stCurrentStringPos;
  CDasherModel *m_pModel;
  CDasherInterfaceBase *m_pInterface;
};

Erft van…

class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;
/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
 public:
  CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);
  virtual ModuleID_t GetID();
  virtual void SetID(ModuleID_t);
  virtual int GetType();
  virtual const char *GetName();
  virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
    return false;
  };
 private:
  ModuleID_t m_iID;
  int m_iType;
  const char *m_szName;
};

Wat erft van….

namespace Dasher {
  class CEvent;
  class CEventHandler;
  class CDasherComponent;
};
/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
 public:
  CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
  virtual ~CDasherComponent();
  void InsertEvent(Dasher::CEvent * pEvent);
  virtual void HandleEvent(Dasher::CEvent * pEvent) {};
  bool GetBoolParameter(int iParameter) const;
  void SetBoolParameter(int iParameter, bool bValue) const;
  long GetLongParameter(int iParameter) const;
  void SetLongParameter(int iParameter, long lValue) const;
  std::string GetStringParameter(int iParameter) const;
  void        SetStringParameter(int iParameter, const std::string & sValue) const;
  ParameterType   GetParameterType(int iParameter) const;
  std::string     GetParameterName(int iParameter) const;
 protected:
  Dasher::CEventHandler *m_pEventHandler;
  CSettingsStore *m_pSettingsStore;
};
/// @}
#endif

Antwoord 1, autoriteit 100%

De GCC FAQbevat een vermelding:

De oplossing is ervoor te zorgen dat alle virtuele methoden die niet zuiver zijn, worden gedefinieerd. Merk op dat een destructor moet worden gedefinieerd, zelfs als deze als pure-virtual [class.dtor]/7 wordt verklaard.

Daarom moet u een definitie voor de virtuele destructor opgeven:

virtual ~CDasherModule()
{ }

Antwoord 2, autoriteit 36%

Voor wat het waard is, het vergeten van een lichaam op een virtuele destructor levert het volgende op:

ongedefinieerde verwijzing naar `vtable for CYourClass’.

Ik voeg een opmerking toe omdat de foutmelding misleidend is. (Dit was met gcc versie 4.6.3.)


Antwoord 3, autoriteit 15%

TL;DR – Legt uit waarom de vtable mogelijk ontbreekt en hoe dit te repareren. Het antwoord is lang omdat het verklaart waarom de compiler zou kunnen vergeten een vtable te maken. (Editor)

Wat is een vtable?

Het kan handig zijn om te weten waar het foutbericht over gaat voordat u het probeert op te lossen. Ik begin op een hoog niveau en werk dan naar wat meer details toe. Op die manier kunnen mensen doorgaan zodra ze vertrouwd zijn met hun begrip van vtables. En er gaan een heleboel mensen vooruit nu. 🙂 Voor degenen die blijven hangen:

Een vtable is in feite de meest voorkomende implementatie van polymorfismein C++. Als vtables worden gebruikt, heeft elke polymorfe klasse ergens in het programma een vtable; je kunt het zien als een (verborgen) staticgegevenslid van de klas. Elk object van een polymorfe klasse wordt geassocieerd met de vtable voor de meest afgeleide klasse. Door deze associatie te controleren, kan het programma zijn polymorfe magie bewerken. Belangrijk voorbehoud:een vtable is een implementatiedetail. Het is niet verplicht door de C++-standaard, hoewel de meeste (alle?) C++-compilers vtables gebruiken om polymorf gedrag te implementeren. De details die ik presenteer zijn ofwel typische of redelijke benaderingen. Compilers mogen hiervan afwijken!

Elk polymorf object heeft een (verborgen) verwijzing naar de vtable voor de meest afgeleide klasse van het object (mogelijk meerdere verwijzingen, in de meer complexe gevallen). Door naar de aanwijzer te kijken, kan het programma zien wat het “echte” type van een object is (behalve tijdens de constructie, maar laten we dat speciale geval overslaan). Als een object van het type Abijvoorbeeld niet verwijst naar de vtable van A, dan is dat object eigenlijk een subobject van iets dat is afgeleid van A.

De naam “vtable” komt van “virtual function table“. Het is een tabel waarin verwijzingen naar (virtuele) functies worden opgeslagen. Een compiler kiest zijn conventie voor hoe de tabel is ingedeeld; een eenvoudige benadering is om de virtuele functies te doorlopen in de volgorde waarin ze zijn gedeclareerd binnen klassedefinities. Wanneer een virtuele functie wordt aangeroepen, volgt het programma de aanwijzer van het object naar een vtable, gaat naar het item dat is gekoppeld aan de gewenste functie en gebruikt vervolgens de opgeslagen functieaanwijzer om de juiste functie aan te roepen. Er zijn verschillende trucs om dit te laten werken, maar daar ga ik hier niet op in.

Waar/wanneer wordt een vtablegegenereerd?

Een vtable wordt automatisch gegenereerd (soms “uitgezonden”) door de compiler. Een compiler zou een vtable kunnen uitzenden in elke vertaaleenheid die een polymorfe klassedefinitie ziet, maar dat zou meestal onnodige overkill zijn. Een alternatief (gebruikt door gcc, en waarschijnlijk door anderen) is om een ​​enkele vertaling te kiezen eenheid waarin u de vtable plaatst, vergelijkbaar met hoe u een enkel bronbestand zou kiezen om de statische gegevensleden van een klasse in te plaatsen. Als dit selectieproces geen vertaaleenheden kan kiezen, wordt de vtable een ongedefinieerde referentie. Vandaar de fout, waarvan de boodschap weliswaar niet bijzonder duidelijk is.

Als het selectieproces wel een vertaaleenheid kiest, maar dat objectbestand niet aan de linker wordt geleverd, wordt de vtable een ongedefinieerde referentie. Helaas kan de foutmelding in dit geval nog minder duidelijk zijn dan in het geval dat het selectieproces is mislukt. (Met dank aan de antwoorders die deze mogelijkheid noemden. Anders was ik het waarschijnlijk vergeten.)

Het selectieproces dat door gcc wordt gebruikt, is logisch als we beginnen met de traditie om een ​​(enkel) bronbestand te wijden aan elke klasse die er een nodig heeft voor de implementatie ervan. Het zou leuk zijn om de vtable uit te zenden bij het compileren van dat bronbestand. Laten we dat ons doel noemen. Het selectieproces moet echter werken, zelfs als deze traditie niet wordt gevolgd. Laten we dus in plaats van te zoeken naar de implementatie van de hele klas, kijken naar de implementatie van een specifiek lid van de klas. Als traditie wordt gevolgd en als dat lid ook daadwerkelijk wordt geïmplementeerd dan wordt het doel bereikt.

Het lid geselecteerd door gcc (en mogelijk door andere compilers) is de eerste niet-inline virtuele functie die niet puur virtueel is. Als je deel uitmaakt van de menigte die constructeurs en destructors declareert vóór andere lidfuncties, dan heeft die destructor een goede kans om geselecteerd te worden. (Je hebt er wel aan gedacht om de destructor virtueel te maken, toch?) Er zijn uitzonderingen; Ik zou verwachten dat de meest voorkomende uitzonderingen zijn wanneer een inline-definitie wordt gegeven voor de destructor en wanneer de standaard destructor wordt gevraagd (met behulp van “= default“).

De scherpzinnige zou kunnen opmerken dat een polymorfe klasse inline-definities mag leveren voor al zijn virtuele functies. Zorgt dat er niet voor dat het selectieproces mislukt? Het doet in oudere compilers. Ik heb gelezen dat de nieuwste compilers deze situatie hebben aangepakt, maar ik ken de relevante versienummers niet. Ik zou kunnen proberen dit op te zoeken, maar het is gemakkelijker om er ofwel omheen te coderen of te wachten tot de compiler klaagt.

Samengevat zijn er drie belangrijke oorzaken van de “undefined reference to vtable”-fout:

  1. Een lidfunctie mist zijn definitie.
  2. Een objectbestand wordt niet gekoppeld.
  3. Alle virtuele functies hebben inline-definities.

Deze oorzaken zijn op zichzelf onvoldoende om de fout alleen te veroorzaken. In plaats daarvan zijn dit wat u zou aanpakken om de fout op te lossen. Verwacht niet dat het opzettelijk creëren van een van deze situaties deze fout zeker zal veroorzaken; er zijn andere eisen. Verwacht wel dat het oplossen van deze situaties deze fout zal oplossen.

(OK, nummer 3 was misschien voldoende toen deze vraag werd gesteld.)

Hoe kan ik de fout oplossen?

Welkom terug mensen die vooruit springen! 🙂

  1. Kijk naar je klassendefinitie. Zoek de eerste niet-inline virtuele functie die niet puur virtueel is (niet “= 0“) en waarvan u de definitie opgeeft (niet “= default“).
    • Als zo’n functie niet bestaat, probeer dan je klasse aan te passen zodat er een is. (Fout mogelijk opgelost.)
    • Zie ook het antwoord van Philip Thomasvoor een waarschuwing.
  2. Zoek de definitie voor die functie. Als het ontbreekt, voeg het dan toe! (Fout mogelijk opgelost.)
    • Als de functiedefinitie buiten de klassedefinitie valt, zorg er dan voor dat de functiedefinitie een gekwalificeerde naam gebruikt, zoals in ClassName::function_name.
  3. Controleer je linkcommando. Als het het objectbestand met de definitie van die functie niet vermeldt, repareer dat dan! (Fout mogelijk opgelost.)
  4. Herhaal stap 2 en 3 voor elke virtuele functie en vervolgens voor elke niet-virtuele functie, totdat de fout is opgelost. Als je nog steeds vastzit, herhaal je dit voor elk lid van de statische gegevens.

Voorbeeld
De details van wat u moet doen, kunnen variëren en soms vertakken ze zich in afzonderlijke vragen (zoals Wat is een niet-gedefinieerde referentie/onopgeloste externe symboolfout en hoe los ik deze op?). Ik zal echter een voorbeeld geven van wat te doen in een specifiek geval dat nieuwere programmeurs in de war zou kunnen brengen.

Stap 1 vermeldt het wijzigen van uw klasse zodat deze een functie van een bepaald type heeft. Als de beschrijving van die functie je te boven ging, zou je in de situatie kunnen zijn die ik van plan ben aan te pakken. Houd in gedachten dat dit een manier is om het doel te bereiken; het is niet de enige manier, en er kunnen gemakkelijk betere manieren zijn in uw specifieke situatie. Laten we je klas Anoemen. Is uw destructor gedeclareerd (in uw klassedefinitie) als ofwel

virtual ~A() = default;

of

virtual ~A() {}

? Als dat zo is, zullen twee stappen uw destructor veranderen in het type functie dat we willen. Verander eerst die regel in

virtual ~A();

Ten tweede plaats je de volgende regel in een bronbestand dat deel uitmaakt van je project (bij voorkeur het bestand met de klasse-implementatie, als je die hebt):

A::~A() {}

Dat maakt je (virtuele) destructor niet-inline en niet gegenereerd door de compiler. (Voel je vrij om dingen aan te passen om beter overeen te komen met je codeopmaakstijl, zoals het toevoegen van een koptekstcommentaar aan de functiedefinitie.)


Antwoord 4, autoriteit 14%

Dus ik heb het probleem ontdekt en het was een combinatie van slechte logica en niet helemaal bekend zijn met de wereld van automake/autotools. Ik was de juiste bestanden aan het toevoegen aan mijn Makefile.am-sjabloon, maar ik wist niet zeker welke stap in ons bouwproces de makefile zelf maakte. Dus ik was aan het compileren met een oude makefile die helemaal geen idee had van mijn nieuwe bestanden.

Bedankt voor de reacties en de link naar de GCC FAQ. Ik zal dat zeker lezen om te voorkomen dat dit probleem om een ​​echte reden optreedt.


Antwoord 5, autoriteit 12%

Als je Qt gebruikt, probeer dan qmake opnieuw uit te voeren. Als deze fout in de klasse van de widget zit, heeft qmake misschien niet opgemerkt dat de ui-klasse vtable opnieuw moet worden gegenereerd. Dit loste het probleem voor mij op.


Antwoord 6, autoriteit 12%

Ongedefinieerde verwijzing naar vtable kan ook optreden vanwege de volgende situatie. Probeer dit eens:

Klasse A bevat:

virtual void functionA(parameters)=0; 
virtual void functionB(parameters);

Klasse B bevat:

  1. De definitie voor de bovenstaande functieA.
  2. De definitie voor de bovenstaande functieB.

Klasse C bevat: Nu schrijf je een klasse C waarin je deze gaat afleiden van klasse A.

Als u nu probeert te compileren, krijgt u een ongedefinieerde verwijzing naar vtable voor klasse C als fout.

Reden:

functionAwordt gedefinieerd als puur virtueel en de definitie ervan wordt gegeven in klasse B.
functionBis gedefinieerd als virtueel (NIET PURE VIRTUAL), dus het probeert de definitie ervan in Klasse A zelf te vinden, maar u hebt de definitie in Klasse B opgegeven.

Oplossing:

  1. Maak functie B puur virtueel (als je zo’n vereiste hebt)
    virtual void functionB(parameters) =0;
    (Dit werkt, het is getest)
  2. Geef een definitie voor functionB in klasse A zelf en houd het als virtueel .
    (Ik hoop dat het werkt, want ik heb dit niet geprobeerd)

Antwoord 7, autoriteit 10%

Ik kreeg deze fout gewoon omdat mijn .cpp-bestand niet in de makefile stond.

In het algemeen, als u vergeet te compileren of te linken naar het specifieke objectbestand dat de definitie bevat, zult u deze fout tegenkomen.


Antwoord 8, autoriteit 6%

Er wordt hier veel gespeculeerd over verschillende antwoorden. Ik zal hieronder een vrij minimale code geven die deze fout reproduceert en uitleggen waarom deze zich voordoet.

Vrij minimale code om deze fout te reproduceren

IBase.hpp

#pragma once
class IBase {
    public:
        virtual void action() = 0;
};

Afgeleide.hpp

#pragma once
#include "IBase.hpp"
class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

Afgeleide.cpp

#include "Derived.hpp"
Derived::Derived(int a) { }
void Derived::action() {}

mijnklasse.cpp

#include <memory>
#include "Derived.hpp"
class MyClass {
    public:
        MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) {
        }
        void doSomething() {
            instance->action();
        }
    private:
        std::shared_ptr<Derived> instance;
};
int main(int argc, char** argv) {
    Derived myInstance(5);
    MyClass c(std::make_shared<Derived>(myInstance));
    c.doSomething();
    return 0;
}

Je kunt dit met GCC als volgt compileren:

g++ -std=c++11 -o a.out myclass.cpp Derived.cpp

U kunt de fout nu reproduceren door = 0in IBase.hpp te verwijderen. Ik krijg deze foutmelding:

~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp
/tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)':
myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o: In function `IBase::IBase()':
Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase'
/tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase'
collect2: error: ld returned 1 exit status

Uitleg

Merk op dat bovenstaande code geen virtuele destructors, constructors of andere extra bestanden vereist om succesvol te compileren (hoewel je ze wel zou moeten hebben).

De manier om deze fout te begrijpen is als volgt:
Linker zoekt constructeur van IBase. Dit zal het nodig hebben voor de constructor van Derived. Omdat Derived echter methoden van IBase overschrijft, is er vtable aan gekoppeld die naar IBase zal verwijzen. Wanneer linker “ongedefinieerde verwijzing naar vtable voor IBase” zegt, betekent dit in feite dat Derived vtable verwijzing naar IBase heeft, maar het kan geen gecompileerde objectcode van IBase vinden om naar op te zoeken. Het komt er dus op neer dat klasse IBase declaraties heeft zonder implementaties. Dit betekent dat een methode in IBase als virtueel wordt gedeclareerd, maar we zijn vergeten deze als puur virtueel te markeren OF de definitie ervan op te geven.

Tip voor afscheid

Als al het andere faalt, is een manier om deze fout te debuggen, het bouwen van een minimaal programma dat wel compileert en het dan steeds te veranderen zodat het de gewenste staat krijgt. Blijf tussendoor compileren om te zien wanneer het begint te mislukken.

Opmerking over het ROS- en Catkin-bouwsysteem

Als je bovenstaande klassen in ROS aan het compileren was met behulp van het catkin build-systeem, dan heb je de volgende regels nodig in CMakeLists.txt:

add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})

De eerste regel zegt in feite dat we een uitvoerbaar bestand met de naam myclass willen maken en de code om dit te bouwen kan in de volgende bestanden worden gevonden. Een van deze bestanden zou main() moeten hebben. Merk op dat u nergens in CMakeLists.txt .hpp-bestanden hoeft op te geven. Ook hoeft u Derived.cpp niet op te geven als bibliotheek.


Antwoord 9, autoriteit 4%

Ik ben net een andere oorzaak voor deze fout tegengekomen die u kunt controleren.

De basisklasse definieerde een pure virtuele functieals:

virtual int foo(int x = 0);

En de subklasse had

int foo(int x) override;

Het probleem was de typfout dat de "=0"buiten de haakjes moest staan:

virtual int foo(int x) = 0;

Dus als je zo ver naar beneden scrolt, heb je waarschijnlijk het antwoord niet gevonden – dit is iets anders om op te letten.


Antwoord 10, autoriteit 3%

De GNU C++-compiler moet een beslissing nemen waar de vtablemoet worden geplaatst voor het geval je de definitie van de virtuele functies van een object hebt verspreid over meerdere compilatie-eenheden (bijv. sommige virtuele functies van objecten definities staan ​​in een .cpp-bestand, andere in een ander .cpp-bestand, enzovoort).

De compiler kiest ervoor om de vtableop dezelfde plaats te plaatsen als waar de eerste gedeclareerde virtuele functie is gedefinieerd.

Als u nu om de een of andere reden bent vergeten een definitie op te geven voor die eerste virtuele functie die in het object is gedeclareerd (of per ongeluk bent vergeten het gecompileerde object toe te voegen tijdens de koppelingsfase), krijgt u deze foutmelding.

Houd er rekening mee dat u als bijwerking alleen voor deze specifieke virtuele functie niet de traditionele linkerfout krijgt, zoals u mist functie foo.


Antwoord 11, autoriteit 2%

  • Weet je zeker dat CDasherComponenteen body heeft voor de destructor? Het is zeker niet hier – de vraag is of het in het .cc-bestand staat.
  • Vanuit een stijlperspectief moet CDasherModulezijn destructor virtualexpliciet definiëren.
  • Het lijkt erop dat CGameModuleeen extra }heeft aan het einde (na de }; // for the class).
  • Is CGameModulegekoppeld aan de bibliotheken die CDasherModuleen CDasherComponentdefiniëren?

Antwoord 12, autoriteit 2%

Niet om te crossposten maar. Als je te maken hebt met erfenis, was de tweede Google-hit wat ik had gemist, namelijk. alle virtuele methoden moeten worden gedefinieerd.

Zoals:

virtual void fooBar() = 0;

Zie answare C++ Undefined Reference to vtable en overervingvoor details. Ik realiseerde me net dat het hierboven al is genoemd, maar het kan iemand helpen.


Antwoord 13

Dus ik gebruikte Qt met Windows XP en MinGW-compiler en dit ding maakte me gek.

In principe werd moc_xxx.cpp leeg gegenereerd, zelfs toen ik werd toegevoegd

Q_OBJECT

Alles verwijderen waardoor functies virtueel, expliciet en wat je ook vermoedt, werkte niet. Eindelijk begon ik regel voor regel te verwijderen en het bleek dat ik

#ifdef something

Rondom het bestand. Zelfs als de #ifdef true was, werd het moc-bestand niet gegenereerd.

Dus het verwijderen van alle #ifdefs loste het probleem op.

Dit gebeurde niet met Windows en VS 2013.


Antwoord 14

In mijn geval gebruik ik Qt en had ik een QObjectsubklasse gedefinieerd in een foo.cpp(niet .h) bestand. De oplossing was om #include "foo.moc"toe te voegen aan het einde van foo.cpp.


Antwoord 15

Voor Qt-gebruiker die CMakeList.txt gebruikt

Voeg deze regel toe aan uw CMakeLists.txt: set(CMAKE_AUTOMOC ON)

Zoals uitgelegd door Chris Morlerals je vergeet een header te moc krijg deze fout


Antwoord 16

Ik denk dat het ook het vermelden waard is dat je ook de melding krijgt wanneer je probeert te linken naar een object van elke klassedie minstens één virtuele methodeen linker kan het bestand niet vinden.
Bijvoorbeeld:

Foo.hpp:

class Foo
{
public:
    virtual void StartFooing();
};

Foo.cpp:

#include "Foo.hpp"
void Foo::StartFooing(){ //fooing }

Samengesteld met:

g++ Foo.cpp -c

En main.cpp:

#include "Foo.hpp"
int main()
{
    Foo foo;
}

Samengesteld en gekoppeld aan:

g++ main.cpp -o main

Geeft onze favoriete fout:

/tmp/cclKnW0g.o: In functie main': main.cpp:(.text+0x1a): undefined
reference to
vtable for Foo’ collect2: error: ld heeft 1 exit geretourneerd
status

Dit gebeurde vanuit mijn onwetendheid omdat:

  1. Vtable wordt per klasse gemaakt tijdens het compileren

  2. Linker heeft geen toegang tot vtable in Foo.o


Antwoord 17

Als al het andere faalt, zoek dan naar duplicatie. Ik werd op het verkeerde been gezet door de expliciete initiële verwijzing naar constructors en destructors totdat ik een verwijzing in een ander bericht las. Het is elkeonopgeloste methode. In mijn geval dacht ik dat ik de declaratie die char *xmlals parameter gebruikte, had vervangen door een declaratie met de onnodig lastige const char *xml, maar in plaats daarvan had ik een nieuwe en liet de andere op zijn plaats.


Antwoord 18

Ik heb alle gedetailleerde stappen van JaMITgeprobeerd en ik werd nog steeds gestuit door deze fout. Na een flinke kopstoot kwam ik er achter. Ik was onvoorzichtig. U zou deze pijnlijk om te zien fout moeten kunnen reproduceren met de volgende voorbeeldcode.

[jaswantp@jaswant-arch build]$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++,d --with-isl --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-install-libiberty --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-werror gdc_include_dir=/usr/include/dlang/gdc
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (GCC) 
// CelesetialBody.h
class CelestialBody{
public:
    virtual void Print();
protected:
    CelestialBody();
    virtual ~CelestialBody();
};
// CelestialBody.cpp
#include "CelestialBody.h"
CelestialBody::CelestialBody() {}
CelestialBody::~CelestialBody() = default;
void CelestialBody::Print() {}
// Planet.h
#include "CelestialBody.h"
class Planet : public CelestialBody
{
public:
    void Print() override;
protected:
    Planet();
    ~Planet() override;
};
// Planet.cpp
#include "Planet.h"
Planet::Planet() {}
Planet::~Planet() {}
void Print() {} // Deliberately forgot to prefix `Planet::`
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project (space_engine)
add_library (CelestialBody SHARED CelestialBody.cpp)
add_library (Planet SHARED Planet.cpp)
target_include_directories (CelestialBody PRIVATE ${CMAKE_CURRENT_LIST_DIR})  
target_include_directories (Planet PRIVATE ${CMAKE_CURRENT_LIST_DIR})    
target_link_libraries (Planet PUBLIC CelestialBody)
# hardened linker flags to catch undefined symbols
target_link_options(Planet 
    PRIVATE 
    -Wl,--as-needed
    -Wl,--no-undefined
)

En we krijgen onze favoriete foutmelding.

$ mkdir build
$ cd build
$ cmake ..
$ make
[ 50%] Built target CelestialBody
Scanning dependencies of target Planet
[ 75%] Building CXX object CMakeFiles/Planet.dir/Planet.cpp.o
[100%] Linking CXX shared library libPlanet.so
/usr/bin/ld: CMakeFiles/Planet.dir/Planet.cpp.o: in function `Planet::Planet()':
Planet.cpp:(.text+0x1b): undefined reference to `vtable for Planet'
/usr/bin/ld: CMakeFiles/Planet.dir/Planet.cpp.o: in function `Planet::~Planet()':
Planet.cpp:(.text+0x3d): undefined reference to `vtable for Planet'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/Planet.dir/build.make:104: libPlanet.so] Error 1
make[1]: *** [CMakeFiles/Makefile2:97: CMakeFiles/Planet.dir/all] Error 2
make: *** [Makefile:103: all] Error 2

Wat ik heb gedaan in Planet.cppmoet natuurlijk worden opgelost met deze tip

  1. Kijk naar je klassendefinitie. Zoek de eerste niet-inline virtuele functie die niet puur virtueel is (niet “= 0”) en waarvan u de definitie opgeeft (niet “= standaard”).

van JaMIT’santwoord.
Als er nog iemand is die al het bovenstaande heeft geprobeerd en niets werkte, ben jij misschien ook, net als ik, achteloos vergeten om <ClassName>::voor te voegen aan een of meer ledenfuncties.

Of ik moet mijn ogen laten controleren of ik moet gaan slapen.


Antwoord 19

Ik had dit probleem ook toen ik probeerde om Abstract Factory Pattern te implementeren, maar vergat een bibliotheek te linken. Dus, voor het geval er niets is geholpen, controleer of alle vereiste bibliotheken zijn gekoppeld

LEAVE A REPLY

Please enter your comment!
Please enter your name here

four × four =

Other episodes