Właśnie natrafiłem na następujący błąd (i znalazłem rozwiązanie online, ale nie występuje ono w przepełnieniu stosu):
(.gnu.linkonce. [stuff]): niezdefiniowane odniesienie do [metody] [plik obiektowy] :(. gnu.linkonce. [stuff]): niezdefiniowane odniesienie do `typeinfo for [classname] '
Dlaczego można dostać jeden z tych błędów linkera „niezdefiniowane odniesienie do typeinfo”?
(Punkty bonusowe, jeśli potrafisz wyjaśnić, co dzieje się za kulisami.)
virtual void abc() =0;
(jeśli wersja podstawowa nigdy nie jest wywoływana)abc()
ten sposób, możesz łatwo zapomnieć o przedefiniowaniuabc()
w klasie pochodnej i pomyśleć, że wszystko jest w porządku, ponieważ nadal będziesz mógł wywoływać tę funkcję bez żadnego problemu. Dobra praktyka implementacji funkcji czysto wirtualnych znajduje się w tym artykule i polega na tym, aby funkcja wypisała „czystą funkcję wirtualną o nazwie”, a następnie zawiesiła program.= 0;
.Odpowiedzi:
Jednym z możliwych powodów jest to, że deklarujesz funkcję wirtualną bez jej definiowania.
Kiedy deklarujesz to bez definiowania go w tej samej jednostce kompilacji, oznacza to, że jest on zdefiniowany gdzie indziej - oznacza to, że faza linkera spróbuje go znaleźć w jednej z innych jednostek kompilacji (lub bibliotek).
Przykładem zdefiniowania funkcji wirtualnej jest:
W takim przypadku dołączasz definicję do deklaracji, co oznacza, że linker nie musi jej później rozwiązywać.
Linia
deklaruje
fn()
bez jego zdefiniowania i spowoduje komunikat o błędzie, o który pytasz.Jest bardzo podobny do kodu:
który stwierdza, że liczba całkowita
i
jest zadeklarowana w innej jednostce kompilacji, którą należy rozwiązać w czasie łączenia (w przeciwnym raziepi
nie można ustawić jej adresu).źródło
virtual void fn() = 0
jest to definicja. To nie jest definicja, ale zwykła deklaracja . Jedynym powodem, dla którego linker nie próbuje go rozwiązać, jest to, że odpowiedni wpis VMT nie będzie odwoływał się do treści funkcji (najprawdopodobniej będzie zawierał wskaźnik zerowy). Jednak nikt nie zabrania ci wywoływania tej czystej funkcji wirtualnej w sposób nie-wirtualny, tj. Przy użyciu w pełni kwalifikowanej nazwy. W tym przypadku łącznik będzie wyglądać dla ciała, i trzeba będzie zdefiniować funkcję. I tak, można zdefiniować ciało czystej funkcji wirtualnej.Może się to zdarzyć również podczas miksowania
-fno-rtti
i-frtti
kodowania. Następnie musisz upewnić się, że każda klasa, któratype_info
jest dostępna w-frtti
kodzie, ma skompilowaną metodę klucza-frtti
. Taki dostęp może nastąpić po utworzeniu obiektu klasy, użyciudynamic_cast
itp.[ źródło ]
źródło
Dzieje się tak, gdy w zadeklarowanych (nieczystych) funkcjach wirtualnych brakuje obiektów. W twojej definicji klasy coś takiego:
Należy zdefiniować (wbudowany lub w połączonym pliku źródłowym):
Lub zadeklarowany jako czysty wirtualny:
źródło
Cytowanie z podręcznika gcc :
I nieco wcześniej na tej samej stronie:
Tak więc ten błąd występuje, gdy „kluczowa metoda” nie ma swojej definicji, jak już wspomniano w innych odpowiedziach.
źródło
Jeśli łączysz jeden .so z drugim, jeszcze jedną możliwością jest kompilacja z „-fvisibility = hidden” w gcc lub g ++. Jeśli oba. Dla linkera wygląda to na niezaimplementowaną funkcję wirtualną (jak w odpowiedziach paxdiablo i cdleary).
W takim przypadku musisz zrobić wyjątek dla widoczności klasy podstawowej za pomocą
w deklaracji klasowej. Na przykład,
Innym rozwiązaniem jest oczywiście nieużywanie „-fvisibility = hidden”. To komplikuje kompilator i linker, być może ze szkodą dla wydajności kodu.
źródło
Poprzednie odpowiedzi są poprawne, ale ten błąd może być również spowodowany próbą użycia typeid na obiekcie klasy, który nie ma funkcji wirtualnych. C ++ RTTI wymaga vtable, więc klasy, na których chcesz przeprowadzić identyfikację typu, wymagają co najmniej jednej funkcji wirtualnej.
Jeśli chcesz, aby informacje o typie działały na klasie, dla której tak naprawdę nie chcesz żadnych funkcji wirtualnych, ustaw wirtualny destruktor.
źródło
Właśnie spędziłem kilka godzin na tym błędzie i chociaż inne odpowiedzi tutaj pomogły mi zrozumieć, co się dzieje, nie rozwiązały mojego konkretnego problemu.
Pracuję nad projektem, który kompiluje się przy użyciu obu
clang++
ig++
. Nie miałem problemów z łączeniem sięclang++
, ale otrzymywałemundefined reference to 'typeinfo for
błądg++
.Ważna kwestia: Powiązanie zamówienia ma znaczenie
g++
. Jeśli podasz biblioteki, które chcesz połączyć w niepoprawnej kolejności, możesz uzyskaćtypeinfo
błąd.Zobacz to pytanie SO, aby uzyskać więcej informacji na temat łączenia zamówienia z
gcc
/g++
.źródło
Możliwe rozwiązania dla kodu obsługującego biblioteki RTTI i inne niż RTTI:
a) Zrekompiluj wszystko za pomocą opcji -frtti lub -fno-rtti
b) Jeśli a) nie jest dla Ciebie możliwe, spróbuj wykonać następujące czynności:
Załóżmy, że libfoo jest zbudowany bez RTTI. Twój kod używa libfoo i kompiluje się z RTTI. Jeśli użyjesz klasy (Foo) w libfoo, która ma wirtuale, najprawdopodobniej napotkasz błąd czasu łącza, który mówi: brak typu info dla klasy Foo.
Zdefiniuj inną klasę (np. FooAdapter), która nie ma wirtualnego i będzie przekierowywać wywołania do używanego Foo.
Skompiluj FooAdapter w małej statycznej bibliotece, która nie używa RTTI i zależy tylko od symboli libfoo. Podaj dla niego nagłówek i użyj go zamiast tego w kodzie (który używa RTTI). Ponieważ FooAdapter nie ma funkcji wirtualnej, nie będzie miał żadnych informacji typu i będziesz mógł połączyć swój plik binarny. Jeśli używasz wielu różnych klas z libfoo, to rozwiązanie może nie być wygodne, ale jest to początek.
źródło
Podobnie jak w powyższej dyskusji RTTI, NO-RTTI, ten problem może również wystąpić, jeśli użyjesz dynamicznej wersji i nie podasz kodu obiektowego zawierającego implementację klasy.
Wystąpił ten problem, budując na Cygwin, a następnie przenosząc kod na Linuksa. Pliki make, struktura katalogów, a nawet wersje gcc (4.8.2) były identyczne w obu przypadkach, ale kod łączył się i działał poprawnie na Cygwin, ale nie mógł połączyć się z Linuksem. Red Hat Cygwin najwyraźniej dokonał modyfikacji kompilatora / linkera, które unikają wymogu łączenia kodu obiektowego.
Komunikat o błędzie linkera Linuksa poprawnie skierował mnie do linii dynamic_cast, ale wcześniejsze komunikaty na tym forum sprawiły, że szukałem brakujących implementacji funkcji, a nie rzeczywistego problemu: brakującego kodu obiektu. Moim obejściem było zastąpienie funkcji typu wirtualnego w klasie bazowej i pochodnej, np. Virtual int isSpecialType (), zamiast używania dynamic_cast. Ta technika pozwala uniknąć wymogu łączenia kodu implementacji obiektu tylko po to, aby Dynamic_cast działał poprawnie.
źródło
W klasie bazowej (abstrakcyjnej klasie bazowej) deklarujesz wirtualny destruktor, a ponieważ nie możesz zadeklarować destruktora jako czystej funkcji wirtualnej, musisz zdefiniować go tutaj w klasie abstrakcyjnej, po prostu fikcyjna definicja wirtualna ~ baza ( ) {} zrobi lub w dowolnej klasie pochodnej.
Jeśli tego nie zrobisz, w czasie połączenia skończysz jako „niezdefiniowany symbol”. Ponieważ VMT ma wpis dla wszystkich czysto wirtualnych funkcji z pasującym NULL, ponieważ aktualizuje tabelę w zależności od implementacji w klasie pochodnej. Ale w przypadku nieoczyszczonych, ale wirtualnych funkcji, potrzebuje definicji w czasie połączenia, aby mógł zaktualizować tabelę VMT.
Użyj c ++ filt, aby rozplątać symbol. Jak $ c ++ filt _ZTIN10storageapi8BaseHostE wyświetli coś w rodzaju „typeinfo for storageapi :: BaseHost”.
źródło
Właśnie otrzymałem wiele z tych błędów. Stało się to, że podzieliłem klasę zawierającą tylko pliki nagłówkowe na plik nagłówkowy i plik cpp. Jednak nie zaktualizowałem mojego systemu kompilacji, więc plik CPP nie został skompilowany. Oprócz zwykłych niezdefiniowanych odwołań do funkcji zadeklarowanych w nagłówku, ale niezaimplementowanych, mam wiele takich błędów typu info.
Rozwiązaniem było ponowne uruchomienie systemu kompilacji w celu skompilowania i połączenia nowego pliku CPP.
źródło
w moim przypadku użyłem biblioteki innej firmy z plikami nagłówkowymi, a więc i plikami. podklasowałem jedną klasę, a błąd połączenia wystąpił podczas próby utworzenia instancji mojej podklasy.
jak wspomniała @sergiy, wiedząc, że może to być problem „rtti”, udało mi się to obejść, umieszczając implementację konstruktora w osobnym pliku .cpp i stosując do pliku flagi kompilacji „-fno-rtti” . to dobrze działa.
ponieważ nadal nie jestem całkiem pewien, jaki jest wewnętrzny błąd tego łącza, nie jestem pewien, czy moje rozwiązanie jest ogólne. myślę jednak, że warto spróbować przed próbą użycia adaptera, o czym wspomniał @francois. i oczywiście, jeśli wszystkie kody źródłowe są dostępne (nie w moim przypadku), lepiej skompiluj ponownie z '-frtti', jeśli to możliwe.
jeszcze jedno, jeśli zdecydujesz się wypróbować moje rozwiązanie, spróbuj uczynić oddzielny plik tak prostym, jak to możliwe, i nie używaj fantazyjnych funkcji C ++. zwracaj szczególną uwagę na rzeczy związane z doładowaniem, ponieważ wiele z nich zależy od rtti.
źródło
Mam ten sam błąd, gdy mój interfejs (ze wszystkimi czystymi funkcjami wirtualnymi) potrzebował jeszcze jednej funkcji i zapomniałem ją „zerować”.
miałem
class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };
Ostatnia wersja vaClose nie jest wirtualna, więc skompilowana nie wiedziała, gdzie uzyskać dla niej implementację i dlatego się pomyliła. moja wiadomość była:
Prosta zmiana z
do
naprawiono problem. mam nadzieję, że to pomoże
źródło
Występuje rzadka sytuacja, ale może to pomóc innym przyjaciołom w podobnej sytuacji. Muszę pracować na starszym systemie z gcc 4.4.7. Muszę skompilować kod z obsługą c ++ 11 lub nowszą, więc buduję najnowszą wersję gcc 5.3.0. Podczas budowania mojego kodu i łączenia z zależnościami, jeśli zależność jest budowana za pomocą starszego kompilatora, dostałem błąd „niezdefiniowane odniesienie do” błędu, mimo że jasno zdefiniowałem ścieżkę łączenia za pomocą opcji -L / path / to / lib -llibname. Niektóre pakiety, takie jak boost i projekty budowane za pomocą cmake, zwykle mają tendencję do używania starszego kompilatora i zwykle powodują takie problemy. Musisz przejść długą drogę, aby upewnić się, że korzystają z nowszego kompilatora.
źródło
W moim przypadku jest to wyłącznie kwestia zależności biblioteki, nawet jeśli mam wywołanie dynamic_cast. Po dodaniu wystarczającej zależności do pliku makefile problem zniknął.
źródło
Sprawdź, czy Twoje zależności zostały skompilowane bez
-f-nortti
.W przypadku niektórych projektów musisz to ustawić jawnie, na przykład w RocksDB:
źródło
W moim przypadku była to funkcja wirtualna w klasie interfejsu, która nie została zdefiniowana jako czysta wirtualna.
= 0
Trochę zapomniałem .źródło