g ++ türüne tanımsız başvuru


209

Sadece aşağıdaki hatayla karşılaştım (ve çözümü çevrimiçi buldum, ancak Stack Overflow'da mevcut değil):

(.gnu.linkonce. [stuff]): [yöntem] [nesne dosyası] için tanımsız başvuru :(. gnu.linkonce. [stuff]): `[classname] için typeinfo '' ya tanımsız başvuru

Neden biri "tipinfo'ya tanımsız referans" bağlantı hatalarından birini alabilir?

(Perde arkasında neler olup bittiğini açıklayabilirseniz bonus puan.)


31
Eski bir gönderi olduğunu biliyorum, ama bugün aynı sorunu yaşadım ve çözüm sadece sanal işlevimi temel abc () yerine temel sınıfta sanal abc () {} olarak tanımlamaktı; hangi hata verdi.
Nav

15
daha iyi olarak virtual void abc() =0;(temel sürüm hiç çağrılmazsa)
dhardy

3
@Nav: Eğer böyle tanımlarsanız , türetilmiş sınıfta abc()kolayca yeniden tanımlamayı unutabilir abc()ve her şeyin yolunda olduğunu düşünebilirsiniz, çünkü yine de fonksiyonu sorunsuz bir şekilde çağırabileceksiniz. Bu makalede saf sanal işlevleri uygulamak için iyi bir uygulama bulunur ve bu işlevi "Saf sanal işlev denir" yazdırma yapmak ve sonra programı çökmesini sağlamaktır.
HelloGoodbye

1
aynı hatayla karşılaşıyordum. "lib" referansları sırasını değiştirmek yardımcı olabilir bulduk. Ben sadece sorun lib's başlangıcından listenin sonuna taşındı ve bu sorunu çözdü
javapowered

2
GAH. Bu en azından şimdi tam olarak bu sayfaya gittiğimde, @dhardy'nin yorumunu okumak ve kendime 'Doh' demek. Sadece bazı çılgın davranışları izlemeye çalışırken 45 dakika geçirdi ve tüm ihtiyacım oldu = 0;.
dwanderson

Yanıtlar:


223

Olası nedenlerden biri, sanal bir işlevi tanımlamadan bildirmenizdir.

Aynı derleme biriminde tanımlamadan bildirdiğinizde, bunun başka bir yerde tanımlandığını belirtirsiniz - bu, bağlayıcı fazın onu diğer derleme birimlerinden (veya kitaplıklardan) birinde bulmaya çalışacağı anlamına gelir.

Sanal işlevi tanımlamanın bir örneği:

virtual void fn() { /* insert code here */ }

Bu durumda, bildirime bir tanım eklersiniz, yani bağlayıcının bunu daha sonra çözmesi gerekmez.

Çizgi

virtual void fn();

fn()bunu tanımlamadan bildirir ve sorduğunuz hata mesajına neden olur.

Bu koda çok benzer:

extern int i;
int *pi = &i;

tamsayı i, bağlantı zamanında çözülmesi gereken başka bir derleme biriminde bildirilir (aksi takdirde piadrese ayarlanamaz).


28
Bunun virtual void fn() = 0bir tanım olduğunu söylemek yanlış . Bu bir tanım değil, yalnızca bir bildiridir . Bağlayıcının bunu çözmeye çalışmamasının tek nedeni, karşılık gelen VMT girişinin bir işlev gövdesine başvurmamasıdır (büyük olasılıkla boş gösterici içerecektir). Ancak, hiç kimse bu saf sanal işlevi sanal olmayan bir şekilde, yani tam olarak nitelenmiş bir ad kullanarak çağırmanızı yasaklamaz. Bu durumda bağlayıcı olacak vücut için bakmak ve işlevi tanımlamak gerekecektir. Ve evet, olabilir saf sanal fonksiyon için bir gövde tanımlamalıdır.
Haziran'da AnT

1
Ve bazen saf bir sanal işlev için bir beden bile ilan edilmelidir.
işaretleyin

3
Derleyici (g ++) size eksik sembolün ne olduğunu söyleyecektir. Not: Dinamik kütüphane bağlantısı olması durumunda, karışık bir ad alabilirsiniz. Okunabilir bir biçimde elde etmek için c ++ filtresini <mangledNameVariable> kullanın. Sınıf adı ile typeinfo hatası bazı temel sınıf eksik sanal yıkıcı uygulaması nedeniyle benim durumumda oldu.
chmike

1
Soru, rtti ile ilgili olan eksik tipinfo olduğundan bahsediyor. Stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick

1
@gbmhunter, yeterince adil. Değişiklik yaptım.
paxdiablo

150

Eğer mix Bu aynı zamanda gerçekleşebilir -fno-rttive -frttikodu. Daha sonra type_info, -frttikodda erişilen herhangi bir sınıfın anahtar yönteminin derlendiğinden emin olmanız gerekir -frtti. Bu erişim, sınıfın bir nesnesini oluşturduğunuzda, kullandığınızda dynamic_castvb.

[ kaynak ]


20
ÇOK TEŞEKKÜR EDERİM. Bu, 5 saat aramanın ardından sorunumu çözdü.
steipete

1
kaynak bağlantı öldü, kesinlikle permalink.gmane.org/gmane.comp.gcc.help/32475
matematik

1
Bunu işaret ettiğiniz için teşekkürler. Asıl sayfa hala burada mevcuttur: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov 14:02

3
StackOverflow.com tekrar kurtarmaya! Keşke bir kereden fazla yükselebilseydim. Başımı klavyeye bir saat kadar vurduktan sonra cevabım ihtiyacım olan şeydi.
spartygw

1
n + 1 hayat kurtardı ve hala sayıyor :)
Gabriel

53

Bu, bildirilen (salt olmayan) sanal işlevler eksik gövdeler olduğunda oluşur. Sınıf tanımınızda şuna benzer:

virtual void foo();

Tanımlanmalıdır (satır içi veya bağlantılı bir kaynak dosyada):

virtual void foo() {}

Veya salt sanal olarak beyan edildi:

virtual void foo() = 0;

27

Gcc kılavuzundan alıntı :

Polimorfik sınıflar için (sanal fonksiyonlu sınıflar), vtable ile birlikte type_info nesnesi yazılır [...] Diğer tüm türler için, kullanıldığında type_info nesnesini yazarız: bir ifadeye `typeid 'uygularken, bir nesneyi atma veya bir catch yan tümcesinde veya istisna belirtimindeki bir türe gönderme.

Ve aynı sayfada biraz daha erken:

Sınıf, satıriçi olmayan, saf olmayan herhangi bir sanal işlev bildirirse, ilki sınıf için “anahtar yöntem” olarak seçilir ve vtable yalnızca anahtar yöntemin tanımlandığı çeviri biriminde yayılır.

Bu nedenle, bu anahtar, daha önce de belirtildiği gibi, "anahtar yöntem" tanımını kaçırdığında oluşur.


2
Benim durumumda, saf sanal olmayan sanal yöntemleri tanımlayan ancak tanımlamamış bir temel sınıfım vardı. Onları saf sanal hale getirdiğimde, demek istediğim, linker hataları ortadan kalktı.
Tatiana Racheva

@TatianaRacheva Teşekkürler! Bağlayıcıdan gelen hata raporlaması yardımcı olmaktan daha azdır ve geniş bir arayüz için '= 0' eksikliğini kaçırmak çok kolaydır. saf sanal için!
rholmes

21

Bir .so dosyasını diğerine bağlıyorsanız, bir olasılık daha gcc veya g ++ 'da "-fvisibility = hidden" ile derlemektir. Her iki .so dosyası da "-fvisibility = hidden" ile oluşturulmuşsa ve anahtar yöntem, sanal işlevin başka bir uygulamasında olduğu gibi .so ile aynı değilse, ikincisi öncekinin vtable veya typeinfo'sunu görmez. Bağlayıcıya göre, bu, uygulanmamış bir sanal fonksiyona benziyor (paxdiablo ve cdleary'nin cevaplarında olduğu gibi).

Bu durumda, temel sınıfın görünürlüğü için bir istisna yapmalısınız.

__attribute__ ((visibility("default")))

sınıf beyanında. Örneğin,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Başka bir çözüm, elbette, "-fvisibility = hidden" kullanmamaktır. Bu, derleyici ve bağlayıcı için, muhtemelen kod performansının zarar görmesi için işleri karmaşık hale getirir.


1
Temel sınıfı soyut veya kullanılmamışsa, yalnızca sanal olmayan işlevleri, normalde yalnızca yapıcıyı dışa aktarmanız (göstermeniz) gerekmez. Türetilmiş diğer yandan sınıflarının kullanıldığı takdirde, dışa gerekir.
Chris Huang-Leaver

bir hile gibi hissettiriyor, ama benim tarafımdaki semptomları çözdü. Teşekkürler !
malat

16

Önceki yanıtlar doğrudur, ancak bu hata, sanal işlevi olmayan bir sınıfın nesnesinde typeid kullanmaya çalışılarak da oluşturulabilir . C ++ RTTI bir vtable gerektirir, bu nedenle tür tanımlaması yapmak istediğiniz sınıflar en az bir sanal işlev gerektirir.

Yazım bilgilerinin, gerçekten herhangi bir sanal işlev istemediğiniz bir sınıfta çalışmasını istiyorsanız, yıkıcıyı sanal yapın.


2
Upmodded çünkü bu özel hata mesajının nedeni daha muhtemel olduğunu düşünüyorum (tanımlanmamış yöntemler daha genel bir durumun aksine ...)
Alastair

4
SO'ya alışmak zorunda olduğum bir şey, siparişin oylara göre değişebileceğinden "yukarıdaki" cevaplara değinmemek. Şimdi silinebildiğim için genellikle başka cevaplara değinmiyorum. İnancım, cevapların bağımsız olması gerektiğidir. Yine de ilişkilendirme için kullanıcı adlarına başvuruyorum.
paxdiablo

Vtable olmadan typeid kullanabilirsiniz; gcc kılavuzundaki alıntılar için cevabım bakın.
CesarB

11

Bu hatayı birkaç saat geçirdim ve buradaki diğer cevaplar neler olup bittiğini anlamama yardımcı olurken, özel sorunumu çözmediler.

Ben derler her ikisini birden kullanarak bir proje üzerinde çalışıyorum clang++ve g++. Kullanarak hiçbir bağlantı sorunları yaşıyordu clang++, ama undefined reference to 'typeinfo forhata alıyordu g++.

Önemli olan nokta: MADDELER ile siparişin ilişkilendirilmesi g++. Bağlamak istediğiniz kütüphaneleri yanlış bir sırayla listelerseniz,typeinfo hatayı .

/ İle sipariş bağlantılandırmayla ilgili daha fazla ayrıntı için bu SO sorusuna bakın .gccg++


Teşekkür ederim!!! Neden bu hatayı aldığımı öğrenmeye bir gün geçirdim ve bu yanıtı ve bağlantı kurduğunuzu görene kadar hiçbir şey işe yaramadı. Çok teşekkürler!!
Irene

10

RTTI ve RTTI olmayan kütüphanelerle ilgilenen kod için olası çözümler:

a) Her şeyi -frtti veya -fno-rtti ile yeniden derleyin
b) a) sizin için mümkün değilse aşağıdakileri deneyin:

Libfoo'nun RTTI olmadan oluşturulduğunu varsayın. Kodunuz libfoo kullanır ve RTTI ile derler. Libfoo'da sanalları olan bir sınıf (Foo) kullanırsanız, muhtemelen bir bağlantı zamanı hatasıyla karşılaşırsınız: sınıf Foo için tipinfo eksik.

Sanal olmayan başka bir sınıf (örn. FooAdapter) tanımlayın ve çağrıları kullandığınız Foo'ya yönlendirin.

FooAdapter'ı RTTI kullanmayan ve yalnızca libfoo sembollerine bağlı olan küçük bir statik kütüphanede derleyin. Bunun için bir başlık sağlayın ve bunu kodunuzda (RTTI kullanan) kullanın. FooAdapter'ın sanal bir işlevi olmadığından herhangi bir typeinfo'ya sahip olmayacak ve ikili dosyalarınızı bağlayabileceksiniz. Libfoo'dan çok farklı sınıflar kullanıyorsanız, bu çözüm uygun olmayabilir, ancak bu bir başlangıçtır.


Farklı RTTI ayarlarına sahip bir kütüphaneye bağlanmak benim için buydu.
bataklık

6

Yukarıdaki RTTI, NO-RTTI tartışmasına benzer şekilde, dynamic_cast kullanırsanız ve sınıf uygulamasını içeren nesne kodunu ekleyemezseniz bu sorun oluşabilir.

Cygwin üzerine inşa edip kodu Linux'a taşıyarak bu problemle karşılaştım. Marka dosyaları, dizin yapısı ve hatta gcc sürümleri (4.8.2) her iki durumda da aynıydı, ancak kod Cygwin'de doğru bir şekilde bağlandı ve çalıştırıldı, ancak Linux'ta bağlanamadı. Red Hat Cygwin, nesne kodu bağlama gereksinimini önleyen derleyici / bağlayıcı değişiklikleri yapmış gibi görünüyor.

Linux linker hata mesajı beni düzgün şekilde dynamic_cast satırına yönlendirdi, ancak bu forumdaki önceki mesajlar asıl problemden ziyade eksik fonksiyon uygulamalarını aradı: eksik nesne kodu. Benim geçici çözüm, dynamic_cast kullanmak yerine, temel ve türetilmiş sınıf, örneğin, sanal int isSpecialType () bir sanal tür işlev yerine oldu. Bu teknik, dynamic_cast'in düzgün çalışması için nesne uygulama kodunu bağlama gereksinimini ortadan kaldırır.


5

Temel sınıfta (soyut bir temel sınıf) sanal bir yıkıcı beyan edersiniz ve bir yıkıcıyı saf bir sanal fonksiyon olarak ilan edemeyeceğiniz için, ya tam burada soyut sınıfta tanımlamanız gerekir, sadece sanal ~ base ( ) {} veya türetilen sınıfın herhangi birinde gerçekleştirir.

Bunu yapamazsanız, bağlantı zamanında bir "tanımsız sembol" elde edersiniz. VMT, türetilmiş sınıftaki uygulamaya bağlı olarak tabloyu güncelleştirirken eşleşen NULL değerine sahip tüm saf sanal işlevler için bir girdi içerdiğinden. Ancak saf olmayan ancak sanal işlevler için, bağlantı zamanında tanımlamaya ihtiyaç duyar, böylece VMT tablosunu güncelleyebilir.

Sembolü demangle etmek için c ++ filtresini kullanın. $ C ++ filt _ZTIN10storageapi8BaseHostE gibi "storageapi :: BaseHost için typeinfo" gibi bir çıktı verir.


3

Şu anda bu hataların çoğunu aldım. Ne oldu, ben sadece bir başlık dosyası sınıfı bir başlık dosyası ve bir cpp dosyası bölmek. Ancak, derleme sistemimi güncellemedim, bu yüzden cpp dosyası derlenmedi. Yalnızca başlıkta bildirilen ancak uygulanmayan işlevlere tanımlanmamış referanslara sahip olmak arasında, bu typeinfo hatalarının çoğunu aldım.

Çözüm, yeni cpp dosyasını derlemek ve bağlamak için derleme sistemini yeniden çalıştırmaktı.


3

benim durumumda, başlık dosyaları ve böylece dosya ile bir üçüncü taraf kütüphane kullandım. bir sınıfın alt sınıfı ve alt sınıfımı örneklemeye çalıştığınızda bu gibi bağlantı hatası oluştu.

@sergiy tarafından belirtildiği gibi, 'rtti' sorunu olabileceğini bilerek , yapıcı uygulamasını ayrı bir .cpp dosyasına koyarak ve '-fno-rtti' derleme bayraklarını dosyaya uygulayarak geçici bir çözüm bulmayı başardım . iyi çalışıyor.

hala bu bağlantı hatası iç hakkında net değil gibi, benim çözüm genel olup olmadığından emin değilim. Ancak, @francois tarafından belirtildiği gibi adaptör yolu denemeden önce bir atış değer düşünüyorum. ve elbette, tüm kaynak kodları mevcutsa (benim durumumda değil), mümkünse '-frtti' ile yeniden derlemeniz daha iyi olur.

bir şey daha var, eğer çözümümü denemeyi seçerseniz, ayrı dosyayı olabildiğince basit hale getirmeye çalışın ve C ++ 'nın bazı süslü özelliklerini kullanmayın. takviye ile ilgili şeylere özel dikkat gösterin, bunun çoğu rtti'ye bağlıdır.


2

Arayüzüm (tüm saf sanal işlevlerle) bir fonksiyona daha ihtiyaç duyduğunda aynı hatayla karşılaştım ve "null" yapmayı unuttum.

sahiptim

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

Son vaClose sanal değil bu yüzden derleme bunun için nerede uygulama bilmiyordu ve böylece karıştı. mesajım şuydu:

... TCPClient.o :(. Rodata + 0x38): `ICommProvider için typeinfo'ya tanımsız başvuru

Basit değişim

virtual int vaClose();

için

virtual int vaClose() = 0;

sorunu çözdü. Umarım yardımcı olur


1

Nadir bir durumla karşılaşıyorum, ancak bu benzer durumdaki diğer arkadaşlara yardımcı olabilir. Gcc 4.4.7 ile eski bir sistem üzerinde çalışmam gerekiyor. Kod c ++ 11 veya üstü desteği ile derlemek zorunda, bu yüzden gcc 5.3.0 en son sürümünü oluşturmak. Kodumu oluştururken ve bağımlılık eski derleyici ile oluşturulmuşsa bağımlılıklara bağlanırken, bağlantı yolunu açıkça -L / path / to / lib -llibname ile tanımlamış olsam bile 'undefined reference to' hatası aldım. Takviye ve cmake ile oluşturulan projeler gibi bazı paketler genellikle eski derleyiciyi kullanma eğilimindedir ve genellikle bu tür sorunlara neden olurlar. Yeni derleyiciyi kullandıklarından emin olmak için uzun bir yol kat etmelisiniz.


1

Benim durumumda, dynamic_cast çağrım olsa bile tamamen bir kütüphane bağımlılığı sorunu. Makefile'a yeterince bağımlılık eklendikten sonra bu sorun ortadan kalktı.


0

Bağımlılıklarınızın olmadan derlendiğini kontrol edin -f-nortti.

Bazı projeler için RocksDB'de olduğu gibi açıkça ayarlamanız gerekir:

USE_RTTI=1 make shared_lib -j4

0

Benim durumumda, saf bir sanal olarak tanımlanmayan bir arabirim sınıfındaki sanal bir işlevdi.

class IInterface
{
public:
  virtual void Foo() = 0;
}

Biraz unuttum = 0.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.