Vtable'a tanımlanmamış referans


356

C ++ programımı oluştururken hata mesajı alıyorum

'vtable' undefined referansı ...

Bu sorunun nedeni nedir? Nasıl düzeltebilirim?


Bu şu kod (söz konusu sınıf CGameModule olduğunu.) İçin hata alıyorum olur ve benim yaşam için sorunun ne olduğunu anlayamıyorum. İlk başta, sanal bir işleve bir beden vermeyi unutmakla ilgili olduğunu düşündüm, ama anladığım kadarıyla, her şey burada. Kalıtım zinciri biraz uzun, ama burada ilgili kaynak kodu. Başka hangi bilgileri vermem gerektiğinden emin değilim.

Not: Yapıcı bu hatanın gerçekleştiği yerdir, öyle görünmektedir.

Kodum:

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

Devralınan ...

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

Hangi miras ....

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

Hangi fonksiyon "vtable'a tanımsız referans ..." atıyor?
J. Polfer

3
Ben tamamen hata mesajı bir işlevi belirtir kaçırdı. Yapıcı olur, bu yüzden sınıf adımı gördüm ve bağlantıyı yapmadım. Yani, inşaatçı bunu atıyor. Bu detayı orijinal gönderime ekleyeceğim.
RyanG

3
Yeni bir dosya oluşturmak için önemli değişiklikler yaptıktan sonra (örneğin qmake -projectve sonra qmake) proje dosyalarınızı yeniden oluşturmadıysanız Makefile, bu, Qt kullanılırken hatanın olası bir kaynağıdır.
David C. Rankin

@ DavidC.Rankin, Qt ile ilgili başka bir sorun, ile dosya Q_OBJECTharici olarak kopyalanır, ancak henüz .pro dosyasının bir parçası değilse, o zaman iyi derlenmiş olsa da, bağlantı vermemesidir. Bunu yapabilmek için bu .h/.cppdosyayı .pro dosyasına eklemeliyiz qmake.
iammilind

Yanıtlar:


419

GCC SSS üzerinde bir girdi vardır:

Çözüm, saf olmayan tüm sanal yöntemlerin tanımlanmasını sağlamaktır. Bir yıkıcı saf-sanal [class.dtor] / 7 olarak bildirilmiş olsa bile tanımlanmalıdır.


17
nm -C CGameModule.o | grep CGameModule::sınıf uygulamanızın tümünün mantıksal nesne dosyasına gittiğini varsayarak, tanımlanan yöntemleri listeler. Neyi kaçırdığınızı anlamak için sanal olarak tanımlanan ile karşılaştırabilirsiniz.
Troy Daniels

133
FFS, derleyici neden bunu denetlemiyor ve bir hata iletisi yazdırmıyor?
Lenar Hoyt

20
Açıkçası, bu sadece bağlayıcı tarafından keşfedilebilir, derleyici tarafından değil.
Xoph

2
Benim durumumda yıkıcı uygulaması olmayan soyut bir sınıfımız vardı. Boş uygulamayı koymak zorunda kaldım ~ MyClass () {}
Shefy Gur-ary

1
Bağlanmaya çalıştığınız nesneler arşivde (libxyz.a dosyası) eksik olduğunda böyle bir hata alabilirsiniz: `` objfilename için vtable '' undefined referansı
Kemin Zhou

161

Değeri için, sanal bir yıkıcıdaki bir bedeni unutmak aşağıdakileri üretir:

"vour for CYourClass" için tanımlanmamış başvuru.

Hata mesajı aldatıcı olduğu için bir not ekliyorum. (Bu, gcc 4.6.3 sürümündeydi.)


23
Boş sanal yıkımımın gövdesini tanım dosyasına (* .cc) açıkça koymak zorunda kaldım. Başlıkta olması hala hatayı verdi.
PopcornKing

4
Sanal yıkıcıyı uygulama dosyasına eklediğimde, gcc bana başka bir işlevde eksik bir gövde olan gerçek hatayı söyledi.
moodboom

1
@PopcornKing Aynı sorunu gördüm. ~Destructor = default;Başlık dosyasında tanımlamak bile yardımcı olmadı. Gcc'ye karşı belgelenmiş bir hata var mı?
RD

bu farklı bir sorun olabilir, ancak benim sorunum sadece sanal olmayan bir yıkıcı için bir uygulamaya sahip değildi (benzersiz / paylaşılan işaretçilere geçiyordu ve kaynak dosyadan kaldırıyordu, ancak üstbilgide "uygulama" yoktu )
svenevs

bu sorunumu çözdü, sanal yıkıcı için boş bir {} gövde ekleyerek hatayı önledi.
Bogdan Ionitza

56

Bu yüzden, sorunu çözdüm ve kötü mantığın bir kombinasyonu oldu ve otomobil / otomobil araçları dünyasına tamamen aşina olmama. Makefile.am şablonuma doğru dosyaları ekliyordum, ancak oluşturma işlemimizde hangi adımın aslında makefile'ın kendisini oluşturduğundan emin değildim. Yani, yeni dosyalarım hakkında hiçbir fikri olmayan eski bir marka ile derliyordum.

Yanıtlar ve GCC SSS bağlantısı için teşekkür ederiz. Gerçek bir nedenden dolayı meydana gelen bu problemden kaçınmak için bunu mutlaka okuyacağım.


43
Kısaca: .cpp sadece yapıya dahil edilmedi. Hata mesajı gerçekten yanıltıcı.
Offirmo

67
Qt kullanıcıları için: bir başlığı moc unutmayı unutursanız aynı hatayı alabilirsiniz.
Chris Morlier

8
Sanırım Alexandre Hamez'in cevabını kabul etmelisin. Bu hatayı arayan kişilerin sizin çözümünüz yerine büyük olasılıkla çözümü gerekir.
Tim

13
-1 Bu sorununuzun çözümü olabilir, ancak asıl sorunun cevabı değildir. Doğru cevap, basitçe gerekli sembolleri içeren bir nesne dosyası sağlamadığınızdır. Onları neden sağlayamadınız, başka bir hikaye.
Walter

12
@Walter: Aslında aradığım tam cevap buydu. Diğerleri açık ve dolayısıyla yararsızdır.
Edgar Bonet

50

Qt kullanıyorsanız, qmake'yi yeniden çalıştırmayı deneyin. Bu hata widget sınıfındaysa, qmake ui sınıfı vtable öğesinin yeniden oluşturulması gerektiğini fark etmemiş olabilir. Bu benim için sorunu düzeltti.


2
Ben de çalıştı ile inşa tüm klasörü sildim.
Tomáš Zato - Monica'yı yeniden görevlendir

Bu benzersiz değil qmake, ben de aynıydı cmake. Sorunun bir kısmı, her iki aracın da başlık dosyalarıyla ilgili bir sorunla karşılaşması olabilir, bu da gerektiğinde her zaman yeniden oluşturmayı tetiklemeyebilir.
MSalters

2
Düşünce "Yeniden" reran qmake otomatik ... görünüşe göre değil. Ben senin öneride "Run qmake" yaptım, sonra "Yeniden" ve benim sorunum düzeltildi.
yano

45

Aşağıdaki duruma bağlı olarak vtable'a tanımlanmamış referans da oluşabilir. Sadece şunu deneyin:

A Sınıfı İçeriği:

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

B Sınıfı İçeriği:

  1. Yukarıdaki fonksiyonA'nın tanımıA.
  2. Yukarıdaki fonksiyonB'nin tanımı.

C Sınıfı İçeriği: Şimdi A Sınıfından türeteceğiniz bir C Sınıfı yazıyorsunuz.

Derlemeye çalışırsanız, C sınıfı için vtable'a hata olarak Tanımsız referans alırsınız.

Sebep:

functionAsaf sanal olarak tanımlanır ve tanımı B Sınıfında sağlanır. functionBSanal (PURE VIRTUAL DEĞİL) olarak tanımlanır, bu nedenle tanımını A Sınıfında bulmaya çalışır, ancak tanımını B Sınıfında sağladınız.

Çözüm:

  1. B işlevini saf sanal yapın (böyle bir gereksiniminiz varsa) virtual void functionB(parameters) =0; (Bu, Test Edilir)
  2. A Sınıfının kendisinde onu sanal olarak tutan functionB için Tanım sağlayın. (Umarım bunu denemediğim gibi çalışır)

@ ilya1725 Önerilen düzenlemeniz yalnızca biçimlendirmeyi ve benzerlerini düzeltmekle kalmaz, aynı zamanda yanıtı da değiştirirsiniz, örneğin C sınıfının A yerine B'den türetildiğini ve ikinci çözümü değiştirdiğinizi söyleyerek. Bu cevabı büyük ölçüde değiştirir. Bu durumlarda, lütfen yazara bir yorum bırakın. Teşekkür ederim!
Fabio, Reinstate Monica'yı

@FabioTurati classC o zaman hangi sınıfı devralır? Cümle net değil. Ayrıca, "C Sınıfı İçeriği:" ne demektir?
ilya1725

@ ilya1725 Bu cevap çok açık değil ve onu düzenlemeye ve geliştirmeye karşı değilim. Söylediğim şey, düzenlemenizin cevabın anlamını değiştirmesi ve bu çok sert bir değişiklik. Umarım, yazar içeri girer ve ne demek istediğini açıklar (uzun süre hareketsiz olmasına rağmen).
Fabio, Reinstate Monica'ya

Teşekkürler! Benim durumumda, hiyerarşimde sadece 2 sınıf vardı. A sınıfı saf bir sanal yöntem ilan etti. B Sınıfı'nın açıklaması, bu yöntemi geçersiz kılacağını belirtti, ancak henüz geçersiz kılma yönteminin tanımını henüz yazmamıştım.
Nick Desaulniers

44

Bu hatayı aldım çünkü cpp dosyam makefile içinde değildi.


Gerçekten de, undefined reference to {function/class/struct}ilgili virtualşeyler olduğunda mesaj normalden biraz değişiyor gibi görünüyor . Beni fırlattı.
Keith M

31

Nedir vtable?

Düzeltmeye çalışmadan önce hata mesajının ne hakkında konuştuğunu bilmek yararlı olabilir. Yüksek bir seviyeden başlayacağım, sonra biraz daha ayrıntıya ineceğim. Bu şekilde insanlar, bildiklerini anlama konusunda rahat olduklarında ileri atlayabilirler. … Ve şimdi bir grup insan ileri atlıyor. :) Yapışmayanlar için:

Bir vtable temel olarak C ++ 'da polimorfizmin en yaygın uygulamasıdır . Vtable kullanıldığında, her polimorfik sınıf programda bir vtable vardır; sınıfın (gizli) bir veri üyesi olarak düşünebilirsiniz . Bir polimorfik sınıfın her nesnesi, en türetilmiş sınıfı için vtable ile ilişkilidir. Bu ilişkiyi kontrol ederek, program polimorfik büyüsünü çalıştırabilir. Önemli uyarı: bir vtable bir uygulama detayıdır. Çoğu (tümü?) C ++ derleyicisinin polimorfik davranışı uygulamak için vtables kullanmasına rağmen, C ++ standardı tarafından zorunlu kılınmamıştır. Sunduğum ayrıntılar ya tipik ya da makul yaklaşımlardır. Derleyicilerin bundan sapmasına izin verilir!static

Her polimorfik nesnenin, nesnenin en türetilmiş sınıfı (muhtemelen daha karmaşık durumlarda çoklu işaretçiler) için vtable'a (gizli) bir işaretçisi vardır. İşaretçiye bakarak, program bir nesnenin "gerçek" türünün ne olduğunu söyleyebilir (yapım hariç, ancak bu özel durumu atlayalım). Örneğin, türdeki bir nesne Avtable'ı göstermiyorsa A, o nesne aslında türetilmiş bir şeyin alt nesnesidir A.

" Vtable " adı " v irtual function table " dan gelir . İşaretçileri (sanal) işlevlere depolayan bir tablodur. Bir derleyici, tablonun nasıl düzenlendiğine ilişkin kuralını seçer; basit bir yaklaşım, sanal işlevlerden sınıf tanımları içinde bildirildikleri sırayla geçmektir. Bir sanal işlev çağrıldığında, program nesnenin işaretçisini bir vtable'a kadar takip eder, istenen işlevle ilişkili girişe gider, ardından doğru işlevi çağırmak için depolanan işlev işaretçisini kullanır. Bu işi yapmak için çeşitli hileler var, ama burada bunlara girmeyeceğim.

Nerede / ne zaman vtableüretilir?

Bir derleyici otomatik olarak derleyici tarafından oluşturulur (bazen "yayılır" olarak da adlandırılır). Bir derleyici, polimorfik sınıf tanımını gören her çeviri biriminde bir vtable yayabilir, ancak bu genellikle gereksiz bir aşırı dolum olacaktır. Bir alternatif ( gcc tarafından ve muhtemelen başkaları tarafından kullanılır ), sınıfın statik veri üyelerini koymak için tek bir kaynak dosyasını nasıl seçeceğinize benzer şekilde, vtable'ın yerleştirileceği tek bir çeviri birimi seçmektir. Bu seçim işlemi herhangi bir çeviri birimi seçemezse, vtable tanımsız bir başvuru haline gelir. Bu nedenle, mesajı özellikle açık olmayan bir hata.

Benzer şekilde, seçim işlemi bir çeviri birimi seçerse, ancak bu nesne dosyası bağlayıcıya sağlanmazsa, vtable tanımsız bir başvuru haline gelir. Ne yazık ki, hata mesajı bu durumda seçim işleminin başarısız olduğu durumdan daha az net olabilir. (Bu olasılıktan bahseden cevap verenler sayesinde. Muhtemelen bunu unuturdum.)

Gcc tarafından kullanılan seçim süreci, uygulanması için bir sınıfa ihtiyaç duyan her sınıfa bir (tek) kaynak dosyası ayırma geleneğiyle başlarsak mantıklıdır. Bu kaynak dosyayı derlerken vtable yayınlamak güzel olurdu. Buna hedefimiz diyelim. Bununla birlikte, bu gelenek takip edilmese bile seçim sürecinin çalışması gerekir. Yani tüm sınıfın uygulanmasını aramak yerine, sınıfın belirli bir üyesinin uygulanmasına bakalım. Eğer gelenek takip edilirse - ve bu üye gerçekte uygulanmışsa - bu hedefe ulaşır.

Gcc tarafından seçilen üye (ve muhtemelen diğer derleyiciler tarafından) saf sanal olmayan ilk satır içi olmayan sanal işlevdir. Diğer üye işlevlerinden önce yapıcıları ve yıkıcıları bildiren kalabalığın bir parçasıysanız, o yıkıcı seçilme şansı yüksektir. (Yıkıcıyı sanal yapmayı hatırladın, değil mi?) İstisnalar var; En yaygın istisnaların yıkıcı için satır içi tanım sağlandığında ve varsayılan yıkıcı istendiğinde (" = default" kullanarak ) olmasını beklerim .

Zeki, bir polimorfik sınıfın tüm sanal işlevleri için satır içi tanımlamalar yapmasına izin verildiğini fark edebilir. Bu seçim sürecinin başarısız olmasına neden olmaz mı? Eski derleyicilerde yapar. En son derleyicilerin bu duruma hitap ettiğini okudum, ancak ilgili sürüm numaralarını bilmiyorum. Bunu aramayı deneyebilirim, ama etrafındaki kodu ya da derleyicinin şikayet etmesini beklemek daha kolay.

Özetle, "vtable'a tanımsız başvuru" hatasının üç temel nedeni vardır:

  1. Üye işlevinin tanımı eksik.
  2. Bir nesne dosyası bağlı değil.
  3. Tüm sanal işlevlerin satır içi tanımları vardır.

Bu nedenler tek başına hataya neden olmak için yetersizdir. Bunun yerine, hatayı çözmek için bunlar ele alırsınız. Bu durumlardan birini kasıtlı olarak oluşturmanın kesinlikle bu hatayı üretmesini beklemeyin; başka şartlar da var. Bu durumları çözmenin bu hatayı çözeceğini düşünün.

(Tamam, bu soru sorulduğunda 3 sayısı yeterli olabilir.)

Hata nasıl düzeltilir?

İleriye atlayan insanları tekrar hoş geldiniz! :)

  1. Sınıf tanımınıza bakın. Saf sanal olmayan (" = 0" değil ) ve tanımını sağladığınız (" = default" değil ) ilk satır içi olmayan sanal işlevi bulun .
    • Böyle bir işlev yoksa, sınıfınızı bir tane olacak şekilde değiştirmeyi deneyin. (Hata muhtemelen çözülmüştür.)
    • Ayrıca bir uyarı için Philip Thomas'ın cevabına bakınız .
  2. Bu işlevin tanımını bulun. Eksikse, ekleyin! (Hata muhtemelen çözülmüştür.)
  3. Bağlantı komutunuzu kontrol edin. Bu işlevin tanımına sahip nesne dosyasından bahsetmiyorsa, düzeltin! (Hata muhtemelen çözülmüştür.)
  4. Hata çözülünceye kadar her sanal işlev için, ardından sanal olmayan her işlev için 2. ve 3. adımları tekrarlayın. Hala takılı kalırsanız, her statik veri üyesi için tekrarlayın.

Örnek
Ne yapmanın ayrıntıları değişebilir ve bazen ayrı sorulara ayrılabilir ( Tanımlanmamış referans / çözülemeyen harici sembol hatası nedir ve nasıl düzeltebilirim? ). Yine de, daha yeni programcıların başına geçebilecek belirli bir durumda ne yapacağımıza bir örnek vereceğim.

Adım 1, sınıfınızı belirli bir işleve sahip olacak şekilde değiştirmekten bahseder. Eğer bu işlevin açıklaması başınızın üzerinden geçerse, ele almayı düşündüğüm durumda olabilirsiniz. Bunun hedefe ulaşmak için bir yol olduğunu unutmayın; tek yol bu değildir ve özel durumunuzda kolayca daha iyi yollar olabilir. Sınıfınızı arayalım A. Yıkıcınız (sınıf tanımınızda) ya

virtual ~A() = default;

veya

virtual ~A() {}

? Eğer öyleyse, iki adım yıkıcıyı istediğimiz işlev türüne dönüştürecektir. İlk olarak, bu satırı şu şekilde değiştirin:

virtual ~A();

İkinci olarak, aşağıdaki satırı projenizin bir parçası olan bir kaynak dosyaya koyun (tercihen sınıf uygulamanız olan dosya varsa):

A::~A() {}

Bu, (sanal) yıkıcıyı satır içi olmayan yapar ve derleyici tarafından oluşturulmaz. (İşlev tanımına başlık yorumu eklemek gibi kod biçimlendirme stilinize daha uygun olacak şekilde değişiklik yapmaktan çekinmeyin.)


Oh, bravo! Çok detaylı ve çok kapsamlı açıklama için.
David C. Rankin

Bunu okumak için çok aşağı kaydırmak zorunda kaldı. Mükemmel açıklama için bir oy verin!
Thomas

24

Burada çeşitli cevaplarda çok fazla spekülasyon var. Aşağıda bu hatayı yeniden üreten ve neden oluştuğunu açıklayan oldukça minimal bir kod vereceğim.

Bu Hatayı Yeniden Oluşturmak için Oldukça Minimum Kod

IBase.hpp

#pragma once

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

Derived.hpp

#pragma once

#include "IBase.hpp"

class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

derived.cpp

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

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

Bunu GCC'yi aşağıdaki gibi kullanarak derleyebilirsiniz:

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

Şimdi = 0IBase.hpp dosyasında kaldırarak hatayı yeniden oluşturabilirsiniz. Bu hatayı alıyorum:

~/.../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

açıklama

Yukarıdaki kodun, derlemenin başarılı olması için herhangi bir sanal yıkıcı, kurucu veya başka bir ekstra dosya gerektirmediğine dikkat edin (bunlara sahip olmalısınız).

Bu hatayı anlamanın yolu şöyledir: Linker IBase'nin kurucusunu arıyor. Bu, Derived'in kurucusu için buna ihtiyaç duyacaktır. Bununla birlikte, Derived, IBase'den yöntemleri geçersiz kıldığından, IBase'e atıfta bulunacak vtable eklenmiştir. Bağlayıcı "IBase için vtable'a tanımlanmamış başvuru" derse, temel olarak Derived'in IBase'e vtable referansı olduğu anlamına gelir, ancak bakmak için IBase'in derlenmiş nesne kodunu bulamaz. Sonuç olarak, IBase sınıfının uygulamaları olmayan beyanları vardır. Bu, IBase'deki bir yöntemin sanal olarak bildirildiği anlamına gelir, ancak bunu saf sanal VEYA tanımını sağladığı olarak işaretlemeyi unuttuk.

Ayrılık İpucu

Her şey başarısız olursa, o zaman bu hatayı ayıklamak için bir yol derlemek ve daha sonra istediğiniz duruma alır böylece değiştirmeye devam minimum program oluşturmaktır. Arasında, ne zaman başarısız olmaya başladığını görmek için derlemeye devam edin.

ROS ve Catkin derleme sistemi hakkında not

Catkin derleme sistemini kullanarak ROS'taki sınıf kümesinin üstünü derliyorsanız, CMakeLists.txt dosyasında aşağıdaki satırlara ihtiyacınız olacaktır:

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

İlk satır temelde myclass adlı bir yürütülebilir dosya yapmak istediğimizi ve bunu oluşturmak için kodun aşağıdaki dosyaları bulabileceğini söylüyor. Bu dosyalardan birinde main () bulunmalıdır. CMakeLists.txt dosyasının herhangi bir yerinde .hpp dosyalarını belirtmeniz gerekmediğine dikkat edin. Ayrıca Derived.cpp dosyasını kütüphane olarak belirtmeniz gerekmez.


18

Bu hatayı kontrol edebileceğiniz başka bir nedenle karşılaştım.

Temel sınıf, saf bir sanal işlevi şu şekilde tanımladı :

virtual int foo(int x = 0);

Ve alt sınıf

int foo(int x) override;

Sorun "=0"parantez dışında olması gereken yazım hatasıydı :

virtual int foo(int x) = 0;

Yani, bu kadar aşağı kaydırırsanız, muhtemelen cevabı bulamazsınız - bu kontrol edilmesi gereken başka bir şeydir.


12

Tanımı olan nesne dosyasına bağlamayı unutursanız bu oldukça kolay olabilir.


1
Lütfen cevabınıza ve olası düzeltmenize biraz daha açıklama ekleyin.
Mohit Jain

1
Bağlamayı unutmak, derleme talimatlarını eklemeyi unutmayı içerebilir. Benim durumumda, cpp dosyasını kaynak listesine (CMakeLists.txt dosyamda eklemeyi unuttum hariç) mükemmel bir şekilde tanımlanmış her şeye sahiptim, ancak aynı .pro dosyası gibi diğer derleme sistemlerinde de olabilir. Sonuç olarak, her şey derlendi ve daha sonra bağlantı zamanında hatayı aldım ...
adaçayı

@Mohit Jain Nesne dosyalarına nasıl bağlanılacağı ortam kurulumuna ve araçlarına bağlıdır. Ne yazık ki bir kişi için belirli bir düzeltme diğeri için farklı olabilir (örneğin CMake vs tescilli araç vs IDE vs)
Hazok

11

GNU C ++ derleyicisi, vtablebirden çok derleme birimine yayılmış bir nesnenin sanal işlevlerinin tanımına sahip olmanız durumunda nereye koymanız gerektiğine karar vermelidir (örneğin, bazı sanal işlev tanımları bir .cpp dosyasında diğerlerinde yer almaktadır. cpp dosyası vb.).

Derleyici vtable, ilk bildirilen sanal işlevin tanımlandığı yere koymayı seçer .

Herhangi bir nedenle, nesnede bildirilen ilk sanal işlev için bir tanım sağlamayı unuttuysanız (veya yanlışlıkla derleme nesnesini bağlama aşamasında eklemeyi unuttuysanız), bu hatayı alırsınız.

Bir yan etki olarak, sadece bu belirli sanal işlev için foo işlevini kaçırdığınız gibi geleneksel linker hatasını almayacağınızı unutmayın .


8

Yazı çapraz değil ama. Eğer miras ile uğraşıyorsanız , ikinci google hit ne kaçırmıştı yani. tüm sanal yöntemler tanımlanmalıdır.

Gibi:

virtual void fooBar() = 0;

Ayrıntılar için bkz. Answare C ++ Tanımsız ve kalıtım referansı . Az önce yukarıda zikredildiğini fark ettim, ama birisine yardımcı olabilir.


8

Tamam, bunun çözümü, tanımı kaçırmış olabilirsiniz. Vtable derleyici hatasını önlemek için aşağıdaki örneğe bakın:

// In the CGameModule.h

class CGameModule
{
public:
    CGameModule();
    ~CGameModule();

    virtual void init();
};

// In the CGameModule.cpp

#include "CGameModule.h"

CGameModule::CGameModule()
{

}

CGameModule::~CGameModule()
{

}

void CGameModule::init()    // Add the definition
{

}

7
  • CDasherComponentYıkıcı için bir vücuda sahip olduğundan emin misin ? Kesinlikle burada değil - soru, .cc dosyasında olup olmadığıdır.
  • Stil perspektifinden, CDasherModuleyıkıcısını açıkça tanımlamalıdır virtual.
  • Sonunda (sonra ) CGameModuleekstra bir var gibi görünüyor .}}; // for the class
  • Is CGameModuletanımlayan kütüphanelere karşı bağlantılı olmakla CDasherModuleve CDasherComponent?

- Evet, CDasherComponent'in cpp'de bir yıkıcı gövdesi var. Bunu gönderdiğimde .h'de bildirildiğini düşündüm. - Tam olarak not edildi. - Bu, belgeleri silerken yanlışlıkla eklediğim ekstra bir braketti. - Anladığım kadarıyla, evet. Yazmadığım bir automake dosyasını değiştirdim, ama aynı sınıflardan aynı miras desenine sahip diğer sınıflar için çalışmış modelleri takip ediyorum, bu yüzden aptal bir hata yapmadıkça (Tamamen mümkün) , Sanmıyorum.
RyanG

@RyanG: tüm sanal işlev tanımlarını sınıf tanımına taşımayı deneyin. Hepsinin orada olduğundan emin olun ve sonucun değişip değişmediğine bakın.
Stephen

5

Belki de sanal yıkıcı eksik faktör katkıda bulunuyor?

virtual ~CDasherModule(){};

5

Bu benim için ilk arama sonucuydu, bu yüzden kontrol etmek için başka bir şey ekleyeceğimi düşündüm: sanal işlevlerin tanımının aslında sınıfta olduğundan emin olun. Benim durumumda, bu vardı:

Başlık dosyası:

class A {
 public:
  virtual void foo() = 0;
};

class B : public A {
 public:
  void foo() override;
};

ve .cc dosyamda:

void foo() {
  ...
}

Bu okumalı

void B::foo() {
}


3

Burada pek çok cevap var ama hiçbiri sorunumun ne olduğunu anlatmamıştı. Aşağıdaki vardı:


class I {
    virtual void Foo()=0;
};

Ve başka bir dosyada (elbette derleme ve bağlantıya dahil)

class C : public I{
    void Foo() {
        //bar
    }
};

Bu işe yaramadı ve herkesin bahsettiği hatayı aldım. Bunu çözmek için, Foo'nun gerçek tanımını şu şekilde sınıf deklarasyonundan çıkarmak zorunda kaldım:

class C : public I{
    void Foo();
};

C::Foo(){
   //bar
}

Ben hiçbir C ++ guru değilim, bu yüzden neden daha doğru olduğunu açıklayamam ama benim için sorunu çözdü.


Ben hiçbir C ++ guru değilim, ama aynı dosyada beyan ve tanımı, ek bir tanım dosyası ile karıştırma ile ilgili gibi görünüyor.
Terry G Lorber

1
İşlev tanımı sınıf tanımınızın içindeyse, örtük olarak "satır içi" olarak bildirildi. Bu noktada, tüm sanal işlevleriniz satır içine alındı. İşlevi sınıf tanımının dışına taşıdığınızda, artık bir "satır içi" işlev değildi. Çizgisiz sanal bir işleve sahip olduğunuzdan, derleyiciniz vtable'ı nerede yayacağını biliyordu. (Daha kapsamlı bir açıklama bir yoruma
uymayacaktır

3

Bu yüzden Windows XP ve MinGW derleyicisi ile Qt kullanıyordum ve bu şey beni deli ediyordu.

Temelde moc_xxx.cpp eklenmiş olsa bile boş üretildi

Q_OBJECT

Her şeyi silmek, işlevleri sanal, açık ve tahmin ettiğiniz her şeyi yapmak işe yaramaz. Sonunda satır satır kaldırmaya başladım ve

#ifdef something

Dosyanın etrafında. #İfdef gerçek olsa bile moc dosyası oluşturulmadı.

Tüm #ifdef'leri kaldırmak sorunu çözdü.

Bu şey Windows ve VS 2013'te gerçekleşmiyordu.


Q_OBJECT satırını yorumlamak, basit test uygulamamın bir ova ile oluşturulmasını sağladı g++ *.cpp .... (Hızlı ve kirli bir şeye ihtiyacım vardı ama qmake keder doluydu.)
Nathan Kidd

2

Her şey başarısız olursa, çoğaltma arayın. Başka bir gönderideki bir referansı okuyana kadar kuruculara ve yıkıcılara ilk açık referansla yanlış yönlendirildim. Bu var herhangi çözümlenmemiş yöntemi. Benim durumumda, gereksiz sorunlu const char * xml kullanarak char * xml parametresi olarak kullanılan bildirimi değiştirdiğimi düşündüm, ama bunun yerine, yeni bir tane oluşturdum ve diğerini yerinde bıraktım.


2

Bu hataya neden olan birçok olasılık var ve eminim ki birçoğu hataya neden oluyor. Benim durumumda, kaynak dosyanın çoğaltılması nedeniyle aynı sınıfın başka bir tanımı vardı . Bu dosya derlendi, ancak bağlantılı değil, bu yüzden linker dosyayı bulamamaktan şikayet ediyordu.

Özetlemek gerekirse, sınıfa yeterince uzun süre baktıysanız ve hangi sözdizimi sorununun neden olabileceğini göremiyorsanız, eksik dosya veya çoğaltılmış dosya gibi derleme sorunlarını arayın.


2

Benim durumumda Qt kullanıyorum ve (değil ) dosyasında bir QObjectalt sınıf tanımlamıştım . Düzeltme sonunda eklemek oldu .foo.cpp.h#include "foo.moc"foo.cpp


2

Ben aynı zamanda bir nesneye bağlantı çalıştığınızda da mesajı alacak bahsetmemiz olduğunu düşünüyorum herhangi sınıftan vardır en azından bir sanal yöntem ve bağlayıcı bulamıyorum dosyayı. Örneğin:

Foo.hpp:

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

foo.cpp:

#include "Foo.hpp"

void Foo::StartFooing(){ //fooing }

Derleme tarihi:

g++ Foo.cpp -c

Ve main.cpp:

#include "Foo.hpp"

int main()
{
    Foo foo;
}

Derlendi ve şunlarla bağlantılı:

g++ main.cpp -o main

Favori hatamızı verir:

/tmp/cclKnW0g.o: main': main.cpp:(.text+0x1a): undefined reference toFoo 'collect2 için vtable işlevinde: hata: ld 1 çıkış durumu döndürdü

Bu benim anlaşılmadığım nedenden kaynaklanıyor:

  1. Vtable, derleme zamanında sınıf başına oluşturulur

  2. Linker'ın Foo.o'da bulunan vtable'a erişimi yok


1

Aşağıdaki senaryoda bu hatayı aldım

Üstbilgi dosyasında bir sınıfın üye işlevlerinin uygulanmasını tanımladığınız bir durumu düşünün. Bu üstbilgi dosyası dışa aktarılmış bir üstbilgidir (başka bir deyişle, bazı ortak / doğrudan kod tabanınıza dahil edilebilir) kopyalanabilir. Şimdi üye işlevlerinin uygulanmasını .cpp dosyasına ayırmaya karar verdiniz. Uygulamayı .cpp dosyasına ayırdıktan / taşıdıktan sonra, üstbilgi dosyası artık sınıf içindeki üye işlevlerinin prototiplerine sahiptir. Yukarıdaki değişikliklerden sonra kod tabanınızı oluşturursanız "vtable ... için tanımsız başvuru" hatası alabilirsiniz.

Bunu düzeltmek için, derlemeden önce, ortak / include dizinindeki başlık dosyasını (üzerinde değişiklik yaptığınız) sildiğinizden emin olun. Ayrıca, yeni oluşturduğunuz .cpp dosyasından oluşturulan yeni .o dosyasını karşılamak / eklemek için makefile'ınızı değiştirdiğinizden emin olun. Bu adımları uyguladığınızda derleyici / bağlayıcı artık şikayet etmeyecektir.


garip. Bir başlık başka bir yere kopyalanacaksa, derleme sistemi orijinal değiştirilir değiştirilmez ve başka bir dosyaya eklenmeden önce kopyayı otomatik olarak güncellemelidir. Manuel olarak yapmak zorunda kalırsanız, vidalanırsınız.
Offirmo

1

Nesnenin arşive eklenmesini engelleyen bir hata bulduğumda bir nesneye bağlanmaya çalıştığım durumlarda bu tür bir hata aldım.

Diyelim ki int bioseq.o olması gereken libXYZ.a var ama yok.

Bir hata aldım:

combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'

Bu, yukarıdakilerin hepsinden farklıdır. Arşiv probleminde bu eksik nesneyi çağırırım.


0

Ayrıca şöyle bir mesaj almanız da mümkündür

SomeClassToTest.host.o: In function `class1::class1(std::string const&)':
class1.hpp:114: undefined reference to `vtable for class1'
SomeClassToTest.host.o: In function `class1::~class1()':
class1.hpp:119: undefined reference to `vtable for class1'
collect2: error: ld returned 1 exit status
[link] FAILED: 'g++' '-o' 'stage/tests/SomeClassToTest' 'object/tests/SomeClassToTest.host.o' 'object/tests/FakeClass1.SomeClassToTest.host.o'

Başka bir sınıf SomeClass için birim testi bağlamaya çalıştığınızda FakeClass1 sınıfının sanal bir işlevini tanımlamayı unutursanız.

//class declaration in class1.h
class class1
{
    public:
    class1()
    {
    }
    virtual ~class1()
    {
    }
    virtual void ForgottenFunc();
};

Ve

//class definition in FakeClass1.h
//...
//void ForgottenFunc() {} is missing here

Bu durumda bir kez daha class1 için sahte kontrol etmenizi öneririz. Muhtemelen ForgottenFuncsahte sınıfınızda sanal bir işlev tanımlamayı unutmuş olabileceğinizi fark edeceksiniz .


0

Varolan bir kaynak / üstbilgi çiftine ikinci bir sınıf eklediğimde bu hatayı aldım. Aynı .h dosyasındaki iki sınıf üstbilgisi ve aynı .cpp dosyasındaki iki sınıf için işlev tanımları.

Bunu daha önce başarılı bir şekilde yaptım, birlikte çalışmayı amaçlayan sınıflarla, ama görünüşe göre bir şey bu sefer benden hoşlanmadı. Hala ne olduğunu bilmiyorum, ancak bunları derleme birimi başına bir sınıfa bölmek onu düzeltti.


Başarısız deneme:

_gui_icondata.h:

#ifndef ICONDATA_H
#define ICONDATA_H

class Data;
class QPixmap;

class IconData
{
public:
    explicit IconData();
    virtual ~IconData();

    virtual void setData(Data* newData);
    Data* getData() const;
    virtual const QPixmap* getPixmap() const = 0;

    void toggleSelected();
    void toggleMirror();
    virtual void updateSelection() = 0;
    virtual void updatePixmap(const QPixmap* pixmap) = 0;

protected:
    Data* myData;
};

//--------------------------------------------------------------------------------------------------

#include "_gui_icon.h"

class IconWithData : public Icon, public IconData
{
    Q_OBJECT
public:
    explicit IconWithData(QWidget* parent);
    virtual ~IconWithData();

    virtual const QPixmap* getPixmap() const;
    virtual void updateSelection();
    virtual void updatePixmap(const QPixmap* pixmap);

signals:

public slots:
};

#endif // ICONDATA_H

_gui_icondata.cpp:

#include "_gui_icondata.h"

#include "data.h"

IconData::IconData()
{
    myData = 0;
}

IconData::~IconData()
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    //don't need to clean up any more; this entire object is going away anyway
}

void IconData::setData(Data* newData)
{
    if(myData)
    {
        myData->removeIcon(this);
    }
    myData = newData;
    if(myData)
    {
        myData->addIcon(this, false);
    }
    updateSelection();
}

Data* IconData::getData() const
{
    return myData;
}

void IconData::toggleSelected()
{
    if(!myData)
    {
        return;
    }

    myData->setSelected(!myData->getSelected());
    updateSelection();
}

void IconData::toggleMirror()
{
    if(!myData)
    {
        return;
    }

    myData->setMirrored(!myData->getMirrored());
    updateSelection();
}

//--------------------------------------------------------------------------------------------------

IconWithData::IconWithData(QWidget* parent) :
    Icon(parent), IconData()
{
}

IconWithData::~IconWithData()
{
}

const QPixmap* IconWithData::getPixmap() const
{
    return Icon::pixmap();
}

void IconWithData::updateSelection()
{
}

void IconWithData::updatePixmap(const QPixmap* pixmap)
{
    Icon::setPixmap(pixmap, true, true);
}

Yine, yeni bir kaynak / üstbilgi çifti eklemek ve orada "sadece çalıştı" IconWithData sınıf kelimesi söz / kesme / yapıştırma.


0

Benim durumum aptalca bir davaydı, yanlışlıkla "sonra fazladan vardı #includeve tahmin et ne oldu?

undefined reference to vtable!

Bir şeylerin değişip değişmediğini görmek için sanal işlevleri yorumlayarak saatlerce başımı ve yüzümü avladım ve son olarak ekstraları kaldırarak "her şey düzeltildi! Bu tür şeyler gerçekten bağlantı hatası değil derleme hatası ile sonuçlanması gerekir.

Ek olarak ", demek istediğim:

#include "SomeHeader.h""

0

Benim durumumda, Kişi adında bir temel sınıfım ve Öğrenci ve Profesör adında iki türetilmiş sınıfım vardı.

Programım sabit var nasıl yapılmış, 1. oldu tüm fonksiyonları temel sınıf içinde Pure Virtual. kullandığım 2. tüm sanal yıkıcı olarakdefault ones.


-3

Bu hatayı sadece yapıcı argümanının adı başlık dosyasında ve uygulama dosyasında farklı olduğu için aldım. Kurucu imzası

PointSet (const PointSet & pset, Parent * parent = 0);

ve uygulamada yazdıklarım

PointSet (const PointSet & pest, Parent * parent)

bu yüzden yanlışlıkla "pset" yerine "pest" yazdım. Derleyici, bu konuda ve hiç hata olmayan iki kurucudan şikayet ediyordu. Ubuntu altında g ++ sürüm 4.9.1 kullanıyorum. Ve bu türetilmiş sınıfta sanal bir yıkıcı tanımlamak hiçbir fark yaratmadı (temel sınıfta tanımlanır). Eğer yapıcıların gövdelerini başlık dosyasına yapıştırmasaydım, böylece onları sınıfta tanımlasaydım bu hatayı asla bulamazdım.


7
Bu hiçbir fark yaratmaz, başka bir yerde hatayla karşılaşmış ve yanlışlıkla düzeltmiş olmalısınız.
MM
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.