Podczas budowania mojego programu C ++ pojawia się komunikat o błędzie
niezdefiniowane odniesienie do „vtable ...
Co jest przyczyną tego problemu? Jak to naprawić?
Zdarza się, że pojawia się błąd dla następującego kodu (klasa, o której mowa, to CGameModule.) I przez całe życie nie mogę zrozumieć, na czym polega problem. Na początku myślałem, że ma to związek z zapomnieniem nadania funkcji wirtualnej ciała, ale o ile rozumiem, wszystko jest tutaj. Łańcuch dziedziczenia jest trochę długi, ale tutaj znajduje się powiązany kod źródłowy. Nie jestem pewien, jakie inne informacje powinienem podać.
Uwaga: Wydaje się, że w tym miejscu występuje błąd.
Mój kod:
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;
};
Dziedziczy po ...
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;
};
Który dziedziczy po ...
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
qmake -project
A następnieqmake
) w celu wygenerowania nowegoMakefile
, jest to prawdopodobne źródło błędu podczas korzystania z Qt.Q_OBJECT
jest kopiowany zewnętrznie, ale nie jest jeszcze częścią pliku .pro, to mimo że kompiluje się dobrze, nie łączy się. Musimy dodać ten.h/.cpp
plik do pliku .pro, aby mócqmake
.Odpowiedzi:
W GCC FAQ jest wpis:
źródło
nm -C CGameModule.o | grep CGameModule::
wyświetli listę zdefiniowanych metod, zakładając, że cała implementacja klasy trafi do pliku obiektu logicznego. Możesz porównać to z wirtualnym, aby dowiedzieć się, co przegapiłeś.Za to, co jest warte, zapomnienie ciała na wirtualnym destruktorze generuje:
Dodam notatkę, ponieważ komunikat o błędzie jest zwodniczy. (To było z wersją gcc 4.6.3.)
źródło
~Destructor = default;
w pliku nagłówkowym nie pomogło. Czy istnieje udokumentowany błąd zgłoszony przeciwko gcc?Więc wymyśliłem problem i było to połączenie złej logiki i nieznajomości świata automake / autotools. Dodawałem prawidłowe pliki do mojego szablonu Makefile.am, ale nie byłem pewien, który krok w naszym procesie kompilacji faktycznie utworzył sam plik makefile. Kompilowałem więc ze starym makefile, który nie miał pojęcia o moich nowych plikach.
Dziękujemy za odpowiedzi i link do GCC FAQ. Przeczytam to, aby uniknąć tego problemu z prawdziwego powodu.
źródło
Jeśli używasz Qt, spróbuj ponownie uruchomić qmake. Jeśli ten błąd występuje w klasie widgetu, qmake mógł nie zauważyć, że vtable klasy interfejsu użytkownika powinien zostać zregenerowany. To rozwiązało problem.
źródło
qmake
, miałem to samo zcmake
. Częścią problemu może być to, że oba narzędzia mają niewielki problem z plikami nagłówkowymi, co może nie zawsze powodować przebudowę w razie potrzeby.Nieokreślone odwołanie do vtable może również wystąpić z powodu następującej sytuacji. Po prostu spróbuj tego:
Klasa A zawiera:
Klasa B zawiera:
Klasa C zawiera: Teraz piszesz klasę C, w której zamierzasz ją wyprowadzić z klasy A.
Teraz, jeśli spróbujesz skompilować, otrzymasz jako niezdefiniowane odniesienie do vtable dla klasy C.
Powód:
functionA
jest zdefiniowany jako czysty wirtualny, a jego definicja jest podana w klasie B.functionB
jest zdefiniowany jako wirtualny (NIE CZYSTY WIRTUALNY), więc próbuje znaleźć swoją definicję w samej klasie A, ale podałeś jej definicję w klasie B.Rozwiązanie:
virtual void functionB(parameters) =0;
(to działa, jest testowana)źródło
Po prostu dostałem ten błąd, ponieważ mojego pliku CPP nie ma w pliku makefile.
źródło
undefined reference to {function/class/struct}
gdy w grę wchodząvirtual
rzeczy. Rzucił mnie.Co jest
vtable
?Przed próbą naprawienia warto wiedzieć, o czym mówi komunikat o błędzie. Zacznę od wysokiego poziomu, a potem dopracuję więcej szczegółów. W ten sposób ludzie mogą przejść do przodu, gdy tylko poczują się dobrze ze zrozumieniem tabel. … I jest teraz grupa ludzi skaczących przed siebie. :) Dla osób pozostających w pobliżu:
Vtable jest w zasadzie najczęstszą implementacją polimorfizmu w C ++ . Kiedy używane są vtables, każda klasa polimorficzna ma gdzieś w programie vtable; możesz myśleć o tym jako o (ukrytym)
static
elemencie danych w klasie. Każdy obiekt klasy polimorficznej jest powiązany z tabelą vt dla swojej najbardziej pochodnej klasy. Sprawdzając to powiązanie, program może działać na swoją magię polimorficzną. Ważne zastrzeżenie: vtable to szczegół implementacji. Nie jest to wymagane przez standard C ++, nawet jeśli większość (wszystkich?) Kompilatorów C ++ używa vtables do implementacji zachowania polimorficznego. Przedstawione przeze mnie szczegóły są typowymi lub rozsądnymi podejściami. Kompilatory mogą się od tego różnić!Każdy obiekt polimorficzny ma (ukryty) wskaźnik do tabeli vt dla najbardziej pochodnej klasy obiektu (być może wielu wskaźników, w bardziej złożonych przypadkach). Patrząc na wskaźnik, program może stwierdzić, jaki jest „prawdziwy” typ obiektu (z wyjątkiem budowy, ale pomińmy ten szczególny przypadek). Na przykład, jeśli obiekt typu
A
nie wskazuje na vtableA
, wówczas ten obiekt jest w rzeczywistości podobiektem czegoś, z czego pochodziA
.Nazwa „vtable” pochodzi od „ v rzeczywistej tabeli funkcji ”. Jest to tabela przechowująca wskaźniki do funkcji (wirtualnych). Kompilator wybiera konwencję dotyczącą układu tabeli; prostym podejściem jest przejście przez funkcje wirtualne w kolejności, w jakiej są zadeklarowane w definicjach klas. Po wywołaniu funkcji wirtualnej program podąża za wskaźnikiem obiektu do tabeli vt, przechodzi do pozycji powiązanej z żądaną funkcją, a następnie używa wskaźnika funkcji zapisanej do wywołania poprawnej funkcji. Jest wiele różnych sztuczek, żeby to zrobić, ale nie będę się tutaj zajmował.
Gdzie / kiedy jest
vtable
generowany?Vtable jest automatycznie generowany (czasem nazywany „emitowanym”) przez kompilator. Kompilator może emitować vtable w każdej jednostce tłumaczenia, która widzi definicję klasy polimorficznej, ale zwykle byłoby to niepotrzebne przesadne działanie. Alternatywą ( używaną przez gcc i prawdopodobnie przez innych) jest wybranie pojedynczej jednostki tłumaczeniowej, w której umieści się vtable, podobnie jak w przypadku wybrania pojedynczego pliku źródłowego, w którym można umieścić statyczne elementy danych klasy. Jeśli podczas tego procesu wyboru nie zostaną wybrane żadne jednostki tłumaczeniowe, tabela vt staje się niezdefiniowanym odwołaniem. Stąd błąd, którego przesłanie nie jest wprawdzie szczególnie jasne.
Podobnie, jeśli proces selekcji powoduje wybór jednostki tłumaczeniowej, ale plik obiektowy nie jest udostępniany konsolidatorowi, wówczas tabela vt staje się niezdefiniowanym odwołaniem. Niestety komunikat o błędzie może być w tym przypadku jeszcze mniej wyraźny niż w przypadku, gdy proces wyboru nie powiódł się. (Dzięki autorom odpowiedzi, którzy wspominali o tej możliwości. Prawdopodobnie w innym przypadku o tym zapomniałbym.)
Proces selekcji używany przez gcc ma sens, jeśli zaczniemy od tradycji poświęcania (pojedynczego) pliku źródłowego każdej klasie, która potrzebuje jednej do jego implementacji. Byłoby miło wyemitować vtable podczas kompilacji tego pliku źródłowego. Nazwijmy to naszym celem. Proces selekcji musi jednak działać, nawet jeśli nie przestrzega się tej tradycji. Zamiast szukać implementacji całej klasy, spójrzmy na implementację konkretnego członka klasy. Jeśli przestrzega się tradycji - a ten członek jest rzeczywiście wdrażany - osiąga to cel.
Element wybrany przez gcc (i potencjalnie przez inne kompilatory) jest pierwszą nieliniową funkcją wirtualną, która nie jest czysto wirtualna. Jeśli należysz do tłumu, który deklaruje konstruktory i destruktory przed innymi funkcjami składowymi, to ten destruktor ma dużą szansę zostać wybrany. (Pamiętasz, jak zrobić wirtualny destruktor, prawda?) Istnieją wyjątki; Spodziewałbym się, że najczęstszymi wyjątkami są sytuacje, gdy dla destruktora wprowadzona jest definicja wbudowana i gdy żądany jest domyślny destruktor (użycie „
= default
”).Bystry może zauważyć, że klasa polimorficzna może dostarczać wbudowane definicje wszystkich swoich funkcji wirtualnych. Czy to nie powoduje niepowodzenia procesu selekcji? Robi to w starszych kompilatorach. Czytałem, że najnowsze kompilatory rozwiązały tę sytuację, ale nie znam odpowiednich numerów wersji. Mógłbym spróbować to sprawdzić, ale łatwiej jest albo go zakodować, albo poczekać, aż kompilator narzeka.
Podsumowując, istnieją trzy kluczowe przyczyny błędu „niezdefiniowane odwołanie do vtable”:
Przyczyny te same w sobie nie są wystarczające, aby samodzielnie spowodować błąd. Zamiast tego należy rozwiązać problem. Nie oczekuj, że celowe utworzenie jednej z tych sytuacji z pewnością spowoduje ten błąd; są inne wymagania. Spodziewaj się, że rozwiązanie tych sytuacji rozwiąże ten błąd.
(OK, liczba 3 mogła być wystarczająca, gdy zadano pytanie).
Jak naprawić błąd?
Witaj z powrotem ludzi, którzy skaczą do przodu! :)
= 0
”) i której definicję podasz (nie „= default
”).Przykład
Szczegółowe informacje na temat tego, co należy zrobić, mogą się różnić, a czasem rozgałęzić na osobne pytania (np. Co to jest niezdefiniowany błąd odniesienia / nierozwiązany błąd symbolu zewnętrznego i jak to naprawić? ). Podam jednak przykład tego, co należy zrobić w konkretnym przypadku, który może być kłopotliwy dla nowszych programistów.
Krok 1 wspomina o modyfikacji klasy, tak aby miała ona funkcję określonego typu. Jeśli opis tej funkcji przeszedł ci przez głowę, być może znajdujesz się w sytuacji, którą zamierzam rozwiązać. Pamiętaj, że jest to sposób na osiągnięcie celu; nie jest to jedyny sposób, a w twojej konkretnej sytuacji mogą być łatwiejsze sposoby. Zadzwońmy do twojej klasy
A
. Czy twój destruktor jest zadeklarowany (w definicji klasy) jako jeden z nich?lub
? Jeśli tak, dwa kroki zmienią twój destruktor w pożądany typ funkcji. Najpierw zmień tę linię na
Po drugie, wstaw następujący wiersz do pliku źródłowego, który jest częścią twojego projektu (najlepiej plik z implementacją klasy, jeśli taki masz):
To sprawia, że Twój (wirtualny) destruktor nie jest wbudowany i nie jest generowany przez kompilator. (Zachęcamy do modyfikowania elementów, aby lepiej pasowały do stylu formatowania kodu, takich jak dodawanie komentarza nagłówka do definicji funkcji).
źródło
W różnych odpowiedziach dzieje się wiele spekulacji. Poniżej podam dość minimalny kod, który odtwarza ten błąd i wyjaśniam, dlaczego się pojawia.
Dość minimalny kod do odtworzenia tego błędu
IBase.hpp
Derived.hpp
Derived.cpp
myclass.cpp
Możesz skompilować to za pomocą GCC w następujący sposób:
Możesz teraz odtworzyć błąd, usuwając go
= 0
z pliku IBase.hpp. Otrzymuję ten błąd:Wyjaśnienie
Zauważ, że powyższy kod nie wymaga żadnych wirtualnych niszczycieli, konstruktorów ani żadnych innych dodatkowych plików, aby kompilacja zakończyła się powodzeniem (chociaż powinieneś je mieć).
Sposób zrozumienia tego błędu jest następujący: Linker szuka konstruktora IBazy. Będzie to potrzebne dla konstruktora Derived. Jednak, ponieważ pochodna zastępuje metody z IBazy, dołączono do niej vtable, która będzie odwoływać się do IBazy. Kiedy linker mówi „niezdefiniowane odwołanie do vtable dla IBase”, oznacza to po prostu, że Derived ma vtable odwołanie do IBase, ale nie może znaleźć żadnego skompilowanego kodu obiektowego IBase do wyszukania. Najważniejsze jest to, że klasa IBase ma deklaracje bez implementacji. Oznacza to, że metoda w IBase jest zadeklarowana jako wirtualna, ale zapomnieliśmy oznaczyć ją jako czystą wirtualną LUB podać jej definicję.
Porada na rozstanie
Jeśli wszystko inne zawiedzie, jednym ze sposobów debugowania tego błędu jest zbudowanie minimalnego programu, który się kompiluje, a następnie zmiana go, aby uzyskać pożądany stan. W międzyczasie kompiluj dalej, aby zobaczyć, kiedy zacznie się nie udać.
Uwaga dotycząca systemu kompilacji ROS i Catkina
Jeśli kompilujesz powyżej zestawu klas w ROS przy użyciu systemu budowania catkin, będziesz potrzebować następujących wierszy w CMakeLists.txt:
Pierwszy wiersz w zasadzie mówi, że chcemy stworzyć plik wykonywalny o nazwie myclass, a kod do jego zbudowania można znaleźć następujące pliki. Jeden z tych plików powinien mieć main (). Zauważ, że nie musisz określać plików .hpp w dowolnym miejscu w CMakeLists.txt. Nie musisz też określać Derived.cpp jako biblioteki.
źródło
Właśnie natrafiłem na inną przyczynę tego błędu, którą możesz sprawdzić.
Klasa podstawowa zdefiniowała czystą funkcję wirtualną jako:
I podklasa miała
Problem polegał na tym, że literówka
"=0"
powinna znajdować się poza nawiasami:Tak więc, jeśli przewijasz tak daleko, prawdopodobnie nie znalazłeś odpowiedzi - jest to coś innego do sprawdzenia.
źródło
Może się to zdarzyć dość łatwo, jeśli zapomnisz połączyć z plikiem obiektowym, który ma definicję.
źródło
Kompilator GNU C ++ musi podjąć decyzję, gdzie umieścić,
vtable
w przypadku gdy masz definicję funkcji wirtualnych obiektu rozmieszczonych w wielu jednostkach kompilacji (np. Niektóre definicje funkcji wirtualnych obiektów znajdują się w pliku .cpp, inne w innym. plik cpp itd.).Kompilator wybiera umieszczenie tego
vtable
samego miejsca, w którym zdefiniowana jest pierwsza zadeklarowana funkcja wirtualna.Teraz, jeśli z jakiegoś powodu zapomniałeś podać definicję pierwszej funkcji wirtualnej zadeklarowanej w obiekcie (lub omyłkowo zapomniałeś dodać skompilowany obiekt w fazie łączenia), pojawi się ten błąd.
Jako efekt uboczny należy pamiętać, że tylko w przypadku tej konkretnej funkcji wirtualnej nie pojawi się tradycyjny błąd linkera, tak jak w przypadku braku funkcji foo .
źródło
Nie przechodzić przez pocztę, ale. Jeśli masz do czynienia z dziedziczeniem, drugim hitem w Google było to, czego mi brakowało, tj. wszystkie metody wirtualne powinny być zdefiniowane.
Jak na przykład:
Aby uzyskać szczegółowe informacje, zobacz answare C ++ Undefined Odniesienie do vtable i dziedziczenia . Właśnie zdałem sobie sprawę, że już wspomniano powyżej, ale do cholery może to komuś pomóc.
źródło
Ok, rozwiązaniem tego może być brak definicji. Zobacz poniższy przykład, aby uniknąć błędu kompilatora vtable:
źródło
CDasherComponent
ciało dla destruktora? Na pewno go tu nie ma - pytanie brzmi, czy znajduje się w pliku .cc.CDasherModule
powinien wyraźnie zdefiniować swój destruktorvirtual
.CGameModule
ma dodatkowy}
na końcu (po}; // for the class
).CGameModule
jest powiązany z bibliotekami, które definiująCDasherModule
iCDasherComponent
?źródło
Być może brak wirtualnego destruktora jest czynnikiem?
źródło
To był dla mnie pierwszy wynik wyszukiwania, więc pomyślałem, że dodam jeszcze jedną rzecz do sprawdzenia: upewnij się, że definicja funkcji wirtualnych jest rzeczywiście w klasie. W moim przypadku miałem to:
Plik nagłówka:
i w moim pliku .cc:
To powinno przeczytać
źródło
Może nie. Zdecydowanie
~CDasherModule() {}
brakuje.źródło
Tak wiele odpowiedzi tutaj, ale żadna z nich nie obejmowała mojego problemu. Miałem następujące:
I w innym pliku (oczywiście zawartym w kompilacji i linkowaniu)
Cóż, to nie zadziałało i mam błąd, o którym wszyscy mówią. Aby go rozwiązać, musiałem przenieść rzeczywistą definicję Foo z deklaracji klasy jako takiej:
Nie jestem guru C ++, więc nie mogę wyjaśnić, dlaczego jest to bardziej poprawne, ale rozwiązało to problem.
źródło
Używałem Qt z Windows XP i kompilatorem MinGW i to doprowadzało mnie do szału.
Zasadniczo plik moc_xxx.cpp został wygenerowany pusty, nawet gdy zostałem dodany
Q_OBJECT
Usunięcie wszystkiego, co czyni funkcje wirtualnymi, jawnymi i cokolwiek zgadniesz, nie działa. W końcu zacząłem usuwać wiersz po wierszu i okazało się, że tak
Wokół pliku. Nawet gdy #ifdef był prawdziwy plik moc nie został wygenerowany.
Usunięcie wszystkich #ifdefs naprawiło problem.
To nie działo się z Windows i VS 2013.
źródło
g++ *.cpp ...
. (Potrzebował czegoś szybkiego i brudnego, ale qmake był pełen żalu.)Jeśli wszystko inne zawiedzie, poszukaj duplikacji. Zostałem źle skierowany przez jawne początkowe odniesienie do konstruktorów i destruktorów, dopóki nie przeczytałem odniesienia w innym poście. To jakakolwiek nierozwiązana metoda. W moim przypadku myślałem, że zastąpiłem deklarację, która używała char * xml jako parametru, jedną z niepotrzebnie kłopotliwej const char * xml, ale zamiast tego stworzyłem nową i pozostawiłem drugą na miejscu.
źródło
Wspomniano wiele możliwości spowodowania tego błędu i jestem pewien, że wiele z nich powoduje błąd. W moim przypadku istniała inna definicja tej samej klasy z powodu duplikacji pliku źródłowego. Ten plik został skompilowany, ale nie połączony, więc linker narzekał na niemożność jego znalezienia.
Podsumowując, powiedziałbym, że jeśli patrzysz na klasę wystarczająco długo i nie widzisz, jaki potencjalny problem ze składnią może być przyczyną, poszukaj problemów z kompilacją, takich jak brakujący plik lub duplikat pliku.
źródło
W moim przypadku używam Qt i zdefiniowałem
QObject
podklasę wfoo.cpp
(nie.h
) pliku. Poprawka miała zostać dodana#include "foo.moc"
na końcufoo.cpp
.źródło
Myślę, że warto również wspomnieć, że otrzymasz wiadomość, gdy spróbujesz połączyć się z obiektem dowolnej klasy, która ma co najmniej jedną metodę wirtualną i linker nie może znaleźć pliku. Na przykład:
Foo.hpp:
Foo.cpp:
Kompilowany z:
I main.cpp:
Skompilowane i powiązane z:
Podaje nasz ulubiony błąd:
To zdarzyło się z mojego niezrozumiałego, ponieważ:
Vtable jest tworzony dla każdej klasy w czasie kompilacji
Linker nie ma dostępu do vtable, który znajduje się w Foo.o
źródło
Wystąpił ten błąd w następującym scenariuszu
Rozważ przypadek, w którym zdefiniowałeś implementację funkcji członkowskich klasy w samym pliku nagłówkowym. Ten plik nagłówka jest wyeksportowanym nagłówkiem (innymi słowy, można go skopiować do jakiegoś wspólnego pliku / include bezpośrednio w bazie kodu). Teraz zdecydowałeś się oddzielić implementację funkcji członka do pliku .cpp. Po rozdzieleniu / przeniesieniu implementacji do pliku .cpp plik nagłówka zawiera teraz tylko prototypy funkcji składowych w klasie. Po wykonaniu powyższych zmian, jeśli zbudujesz bazę kodu, może pojawić się błąd „niezdefiniowane odniesienie do 'vtable ...”.
Aby to naprawić, przed budowaniem upewnij się, że usunąłeś plik nagłówka (do którego wprowadziłeś zmiany) we wspólnym katalogu / include. Upewnij się także, że zmieniłeś swój plik makefile, aby dostosować / dodać nowy plik .o, który jest zbudowany z nowego właśnie utworzonego pliku .cpp. Po wykonaniu tych kroków kompilator / linker nie będzie już narzekał.
źródło
Wystąpił tego rodzaju błąd w sytuacjach, gdy próbowałem połączyć się z obiektem, gdy dostałem błąd make, który uniemożliwił dodanie obiektu do archiwum.
Powiedzmy, że mam libXYZ.a, który powinien mieć bioseq.o w int, ale nie ma.
Mam błąd:
Jest to zupełnie inne niż wszystkie powyższe. Nazwałbym ten brakujący obiekt problemem archiwizacji.
źródło
Możliwe jest również, że otrzymasz wiadomość typu „jak”
jeśli zapomnisz zdefiniować funkcję wirtualną klasy FakeClass1, gdy próbujesz połączyć test jednostkowy z inną klasą SomeClass.
I
W takim przypadku sugeruję, abyś jeszcze raz sprawdził swoją fałszywość dla klasy 1. Prawdopodobnie okaże się, że zapomniałeś zdefiniować funkcję wirtualną
ForgottenFunc
w swojej fałszywej klasie.źródło
Wystąpił ten błąd, gdy dodałem drugą klasę do istniejącej pary źródło / nagłówek. Dwa nagłówki klas w tym samym pliku .h oraz definicje funkcji dla dwóch klas w tym samym pliku .cpp.
Zrobiłem to już wcześniej, z lekcjami, które mają ściśle ze sobą współpracować, ale najwyraźniej tym razem coś mnie nie lubiło. Nadal nie wiem co, ale podział na jedną klasę na jednostkę kompilacji naprawił to.
Nieudana próba:
_gui_icondata.h:
_gui_icondata.cpp:
Ponownie dodając nową parę źródło / nagłówek i wycinając / wklejając dosłownie klasę IconWithData „po prostu działało”.
źródło
Mój przypadek był głupi jeden, miałem dodatkową
"
po#include
przez pomyłkę i wiecie co?Godzinami drapię się po palcach po głowie i twarzy, komentując funkcje wirtualne, aby zobaczyć, czy coś się zmieniło, i wreszcie usuwając dodatkowe
"
, wszystko zostało naprawione! Tego rodzaju rzeczy naprawdę muszą skutkować błędem kompilacji, a nie błędem łącza.Przez dodatkowe
"
rozumiem:źródło
W moim przypadku miałem klasę podstawową o nazwie Osoba i dwie klasy pochodne o nazwie Student i Professor.
Jak naprawiłem mój program, to: 1. Wykonałem wszystkie funkcje w klasie bazowej
Pure Virtual.
2. Użyłem wszystkich wirtualnych destruktorów jakodefault ones.
źródło
Wystąpił ten błąd tylko dlatego, że nazwa argumentu konstruktora różniła się w pliku nagłówkowym i pliku implementacji. Sygnatura konstruktora to
i od tego zacząłem pisać
dlatego przypadkowo zastąpiłem „pset” słowem „szkodnik”. Kompilator narzekał na ten jeden i dwa inne konstruktory, w których nie wystąpił żaden błąd. Używam g ++ w wersji 4.9.1 pod Ubuntu. A zdefiniowanie wirtualnego destruktora w tej klasie pochodnej nie miało znaczenia (jest zdefiniowane w klasie bazowej). Nigdy nie znalazłbym tego błędu, gdybym nie wkleił ciał konstruktorów w pliku nagłówkowym, definiując je w klasie.
źródło