Ketika membangun program C++, I'm mendapatkan pesan kesalahan
undefined referensi untuk 'vtable...
Apa penyebab dari masalah ini? Bagaimana saya memperbaikinya?
Hal ini terjadi bahwa saya'm mendapatkan kesalahan kode berikut (kelas yang dimaksud adalah CGameModule.) dan aku tidak bisa untuk kehidupan saya mengerti apa masalahnya. Pada awalnya, saya pikir itu terkait dengan melupakan untuk memberikan virtual fungsi tubuh, tapi sejauh yang saya mengerti, semuanya di sini. Rantai warisan lebih sedikit panjang, tapi di sini adalah yang berkaitan dengan source code. I'm tidak yakin apa informasi lain yang harus saya berikan.
Catatan: constructor adalah di mana kesalahan ini terjadi,'d tampak.
Kode saya:
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;
};
Mewarisi dari...
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;
};
Yang mewarisi dari....
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
The GCC FAQ memiliki sebuah entri di atasnya:
solusinya adalah untuk memastikan bahwa semua metode virtual yang tidak murni didefinisikan. Perhatikan bahwa sebuah destruktor harus didefinisikan bahkan jika itu dinyatakan murni-virtual [kelas.dtor]/7.
Untuk apa itu layak, melupakan tubuh pada virtual destructor menghasilkan berikut ini:
undefined referensi untuk `vtable untuk CYourClass'.
Saya menambahkan catatan karena galat adalah menipu. (Ini adalah dengan versi gcc 4.6.3.)
Jadi, saya've tahu masalah ini dan itu adalah kombinasi yang buruk logika dan tidak menjadi benar-benar akrab dengan automake/autotools dunia. Saya menambahkan file yang benar untuk saya Makefile.am template, tapi saya wasn't yakin langkah dalam membangun kami proses benar-benar dibuat makefile itu sendiri. Jadi, saya sedang menyusun sebuah makefile yang tidak memiliki ide tentang my new file apapun.
Terima kasih untuk tanggapan dan link ke GCC FAQ. Aku akan pastikan untuk membaca bahwa untuk menghindari masalah ini terjadi untuk alasan yang nyata.
Jika anda menggunakan Qt, mencoba siarang qmake. Jika kesalahan ini di widget's class, qmake mungkin telah gagal untuk melihat bahwa ui kelas vtable harus diregenerasi. Ini tetap masalah untuk saya.
Undefined referensi untuk vtable dapat terjadi karena situasi berikut juga. Hanya mencoba ini:
Kelas Yang Berisi:
virtual void functionA(parameters)=0;
virtual void functionB(parameters);
Kelas B Berisi:
Kelas C Berisi: Sekarang anda're menulis Kelas C di mana anda akan berasal dari Kelas A.
Sekarang jika anda mencoba untuk mengkompilasi anda akan mendapatkan referensi tidak Terdefinisi untuk vtable untuk Kelas C sebagai kesalahan.
Alasan:
fungsional
didefinisikan sebagai virtual murni dan definisi yang diberikan di Kelas B.
functionB
didefinisikan sebagai virtual (TIDAK MURNI VIRTUAL) sehingga ia mencoba untuk menemukan definisi dari dalam Kelas itu sendiri tetapi anda berikan definisi di Kelas B.
Solusi:
virtual void functionB(parameter) =0;
(Ini bekerja Diuji)Ada banyak spekulasi yang terjadi di berbagai jawaban di sini. I'll di bawah ini memberikan cukup minim kode yang mereproduksi kesalahan ini dan menjelaskan mengapa hal ini terjadi.
Cukup Minim Kode untuk Mereproduksi Kesalahan Ini
IBase.hpp
#pragma once
class IBase {
public:
virtual void action() = 0;
};
Berasal.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;
}
Anda dapat mengkompilasi ini menggunakan GCC seperti ini:
g++ -std=c++11 -o a.out myclass.cpp Derived.cpp
Anda sekarang dapat mereproduksi kesalahan dengan menghapus = 0
di IBase.hpp. Saya mendapatkan error ini:
~/.../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
Penjelasan
Perhatikan bahwa kode di atas tidak memerlukan virtual destructors, konstruktor atau file tambahan untuk mengkompilasi untuk menjadi sukses (meskipun anda harus memilikinya).
Cara untuk memahami kesalahan ini adalah sebagai berikut: Linker mencari konstruktor IBase. Ini akan perlu untuk konstruktor Berasal. Namun karena Berasal menimpa metode dari IBase, telah vtable melekat padanya yang akan referensi IBase. Ketika linker mengatakan "undefined referensi untuk vtable untuk IBase" ini pada dasarnya berarti bahwa Turunan memiliki vtable referensi untuk IBase tetapi dapat't menemukan disusun kode objek dari IBase untuk melihat ke. Jadi intinya adalah bahwa kelas IBase memiliki deklarasi tanpa implementasi. Ini berarti suatu metode dalam IBase dinyatakan sebagai virtual tapi kita lupa untuk menandainya sebagai virtual murni ATAU memberikan definisi.
Perpisahan Tip
Jika semuanya gagal maka salah satu cara untuk men-debug kesalahan ini adalah untuk membangun minimal program yang tidak mengkompilasi dan kemudian terus berubah sehingga sampai ke negara yang anda inginkan. Di antaranya, menjaga kompilasi untuk melihat ketika mulai gagal.
Catatan atas ROS dan Catkin membangun sistem
Jika anda adalah menyusun set atas kelas-kelas di ROS menggunakan catkin membangun sistem maka anda akan perlu menambahkan baris berikut di CMakeLists.txt:
add_executable(myclass src/myclass.cpp src/Derived.cpp)
add_dependencies(myclass theseus_myclass_cpp)
target_link_libraries(myclass ${catkin_LIBRARIES})
Baris pertama pada dasarnya mengatakan bahwa kita ingin membuat aplikasi yang dapat dijalankan bernama myclass dan kode untuk membangun ini dapat ditemukan file-file yang berikut. Salah satu dari file-file ini harus memiliki main(). Perhatikan bahwa anda don't harus menentukan .hpp file di mana saja di CMakeLists.txt. Juga anda don't harus menentukan Derived.cpp sebagai perpustakaan.
Aku hanya berlari ke lain penyebab kesalahan ini yang bisa anda cek.
Kelas dasar didefinisikan fungsi virtual murni sebagai:
virtual int foo(int x = 0);
Dan subclass memiliki
int foo(int x) override;
Masalahnya adalah typo bahwa "=0"
seharusnya berada di luar tanda kurung:
virtual int foo(int x) = 0;
Jadi, dalam kasus anda're bergulir sejauh ini turun, mungkin kau't menemukan jawaban - ini adalah sesuatu yang lain untuk memeriksa.
GNU C++ compiler untuk membuat keputusan di mana untuk menempatkan vtable
dalam kasus anda memiliki definisi fungsi virtual dari suatu objek yang tersebar di beberapa kompilasi unit (misalnya beberapa objek virtual fungsi definisi yang dalam .cpp file orang lain di tempat lain .cpp file, dan sebagainya).
Compiler memilih untuk menempatkan vtable
di tempat yang sama sebagai mana yang pertama menyatakan fungsi virtual didefinisikan.
Sekarang jika anda untuk beberapa alasan lupa untuk memberikan definisi yang pertama virtual function yang dideklarasikan dalam objek (atau keliru lupa untuk menambahkan menyusun objek dengan menghubungkan fase), anda akan mendapatkan kesalahan ini.
Sebagai efek samping, harap dicatat bahwa ini hanya untuk khusus ini fungsi virtual anda tidak't mendapatkan tradisional linker kesalahan seperti anda kehilangan fungsi foo.
Tidak untuk cross posting tapi. Jika anda berurusan dengan warisan kedua google yang terjadi adalah apa yang saya telah terjawab, yaitu. semua metode virtual harus didefinisikan.
Seperti:
virtual void fooBar() = 0;
Lihat answare https://stackoverflow.com/q/9406580/1592572 untuk rincian. Hanya menyadari itu's yang sudah disebutkan di atas, tapi sih itu mungkin membantu seseorang.
Ok, solusi untuk ini adalah bahwa anda mungkin telah terjawab di pengertian. Lihat contoh di bawah ini untuk menghindari vtable compiler error:
// 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
{
}
CDasherComponent
memiliki tubuh untuk destructor? It's pasti tidak di sini - pertanyaan ini jika di .cc file.CDasherModule
harus secara eksplisit mendefinisikan destructor virtual
.CGameModule
ekstra }
di akhir (setelah }; // untuk kelas
).CGameModule
yang terkait terhadap perpustakaan yang mendefinisikan CDasherModule
dan CDasherComponent
?Ini adalah pertama hasil pencarian untuk saya jadi saya pikir saya'd tambahkan hal lain untuk memeriksa: pastikan definisi fungsi virtual yang benar-benar di kelas. Dalam kasus saya, saya punya ini:
File Header:
class A {
public:
virtual void foo() = 0;
};
class B : public A {
public:
void foo() override;
};
dan di saya .cc file:
void foo() {
...
}
Ini harus baca
void B::foo() {
}
vtable
? statis
data anggota kelas. Setiap objek yang polimorfik kelas dikaitkan dengan vtable yang kebanyakan berasal dari kelas. Dengan memeriksa hubungan ini, program ini dapat bekerja polimorfik sihir. Peringatan penting: sebuah vtable merupakan implementasi detail. Hal ini tidak diamanatkan oleh C++ standar, meskipun sebagian besar (semua?) C++ compiler menggunakan vtables untuk melaksanakan polimorfik perilaku. Rincian saya menyajikan yang baik khas wajar atau pendekatan. Penyusun diperbolehkan untuk menyimpang dari ini!
Masing-masing polimorfik objek memiliki (tersembunyi) pointer ke vtable untuk objek's kebanyakan berasal dari kelas (mungkin beberapa pointer, dalam kasus yang lebih kompleks). Dengan melihat pointer, program ini dapat memberitahu apa "nyata" jenis objek (kecuali selama konstruksi, tapi let's melewatkan bahwa kasus khusus). Misalnya, jika sebuah objek dari tipe A
tidak menunjuk ke vtable dari A
, maka objek itu sebenarnya adalah sebuah sub-objek dari sesuatu yang berasal dari A
.
Nama "vtable" berasal dari "virtual fungsi meja". Ini adalah tabel yang menyimpan pointer ke (virtual) fungsi. Penyusun memilih konvensi untuk berapa meja yang ditata; pendekatan sederhana adalah untuk pergi melalui fungsi virtual dalam urutan mereka dinyatakan dalam definisi class. Ketika fungsi virtual yang disebut, program ini mengikuti objek's pointer ke vtable, pergi ke entri yang berhubungan dengan fungsi yang diinginkan, kemudian menggunakan stored function pointer untuk memanggil fungsi yang benar. Ada berbagai trik untuk membuat ini bekerja, tapi aku tidak't pergi ke orang-orang di sini.
Di mana/kapan vtable
yang dihasilkan? Sebuah vtable dihasilkan secara otomatis (kadang-kadang disebut "yang dipancarkan") oleh compiler. Compiler bisa memancarkan vtable di setiap unit terjemahan yang melihat polimorfik definisi kelas, tapi itu biasanya akan menjadi tidak perlu berlebihan. Alternatif (digunakan oleh gcc, dan mungkin oleh orang lain) adalah untuk memilih satu terjemahan unit di mana untuk menempatkan vtable, mirip dengan bagaimana anda akan memilih satu sumber file di mana untuk menempatkan kelas' anggota data statis. Jika proses seleksi ini gagal untuk memilih terjemahan unit, maka vtable menjadi sebuah referensi tidak terdefinisi. Oleh karena itu kesalahan, dan pesan ini memang tidak terlalu jelas.
Demikian pula, jika proses seleksi tidak memilih terjemahan unit, tapi yang file objek ini tidak disediakan untuk linker, maka vtable menjadi sebuah referensi tidak terdefinisi. Sayangnya, pesan kesalahan bahkan bisa kurang jelas dalam hal ini daripada dalam kasus di mana proses seleksi gagal. (Terima kasih untuk penjawab yang disebutkan kemungkinan ini. Aku mungkin akan lupa sebaliknya.)
Proses seleksi yang digunakan oleh gcc masuk akal jika kita mulai dengan tradisi mencurahkan (satu) file sumber untuk masing-masing kelas yang satu kebutuhan untuk pelaksanaannya. Itu akan menjadi bagus untuk memancarkan vtable ketika menyusun bahwa file sumber. Let's menyebut bahwa tujuan kami. Namun, proses seleksi harus bekerja bahkan jika tradisi ini tidak diikuti. Jadi, bukannya mencari untuk pelaksanaan seluruh kelas, let's mencari implementasi dari anggota tertentu dari kelas. Jika tradisi ini diikuti – dan jika anggota itu sebenarnya dilaksanakan – maka ini mencapai tujuan.
Anggota dipilih oleh gcc (dan berpotensi dengan penyusun lainnya) adalah yang pertama non-inline fungsi virtual yang tidak murni virtual. Jika anda adalah bagian dari orang-orang yang menyatakan konstruktor dan destruktor sebelum anggota lain fungsi, maka destructor memiliki kesempatan yang baik untuk dipilih. (Anda tidak ingat untuk membuat virtual destructor, kan?) Ada pengecualian; aku'd mengharapkan yang paling umum pengecualian adalah ketika inline definisi disediakan untuk destructor dan ketika default destructor adalah meminta (menggunakan "= default
").
Cerdik mungkin melihat bahwa polimorfik kelas diperbolehkan untuk memberikan inline definisi untuk semua fungsi virtual. Doesn't yang menyebabkan proses seleksi gagal? Itu tidak lama compiler. I've baca yang terbaru penyusun telah membahas situasi ini, tapi saya tidak tahu relevan nomor versi. Aku bisa mencoba mencarinya, tapi itu's mudah untuk kode di sekitar itu atau menunggu compiler untuk mengeluh.
Secara ringkas, ada tiga penyebab utama "undefined referensi untuk vtable" kesalahan:
Selamat datang kembali orang-orang yang melompat-lompat ke depan! :)
= 0
") dan definisi yang anda berikan (bukan "= default
"). A
. Anda destructor menyatakan (dalam definisi kelas anda) sebagai salah virtual ~A() = default;
atau
virtual ~A() {}
? Jika demikian, dua langkah akan mengubah anda destructor ke dalam jenis fungsi yang kita inginkan. Pertama, perubahan jalur itu untuk
virtual ~A();
Kedua, masukan baris berikut dalam file sumber yang merupakan bagian dari proyek anda (sebaiknya file dengan implementasi kelas, jika anda memiliki satu):
A::~A() {}
Yang membuat anda (virtual) destructor non-inline dan tidak dihasilkan oleh compiler. (Merasa bebas untuk mengubah hal-hal yang baik sesuai dengan kode format gaya, seperti menambahkan sebuah header komentar untuk definisi fungsi.)
Begitu banyak jawaban di sini tapi tidak satupun dari mereka tampaknya telah membahas apa masalah saya. Saya punya sebagai berikut:
class I {
virtual void Foo()=0;
};
Dan di file lain (termasuk dalam kompilasi dan linking, tentu saja)
class C : public I{
void Foo() {
//bar
}
};
Nah ini didn't bekerja dan aku punya kesalahan semua orang berbicara tentang. Untuk mengatasinya, saya harus pindah sebenarnya definisi dari Foo keluar dari kelas deklarasi seperti:
class C : public I{
void Foo();
};
C::Foo(){
//bar
}
I'm tidak ada C++ guru sehingga saya bisa't menjelaskan mengapa hal ini lebih tepat tetapi memecahkan masalah bagi saya.
Jadi saya menggunakan Qt dengan Windows XP dan compiler MinGW dan hal ini sudah membuatku gila.
Pada dasarnya moc_xxx.cpp dihasilkan kosong bahkan ketika saya menambahkan
Q_OBJECT
Menghapus segala sesuatu yang membuat fungsi virtual, eksplisit dan apa pun yang anda kira doesn't bekerja. Akhirnya saya mulai menghapus baris demi baris dan ternyata bahwa aku telah
#ifdef something
Seluruh file. Bahkan ketika #ifdef benar moc file tidak dihasilkan.
Jadi menghapus semua #ifdefs tetap masalah.
Hal ini tidak terjadi dengan Windows dan LEBIH baik dari tahun 2013.
Jika semuanya gagal, mencari duplikasi. Aku salah kirim oleh eksplisit awal referensi untuk konstruktor dan destruktor sampai saya membaca sebuah referensi dalam pos lain. It's any yang belum terselesaikan metode. Dalam kasus saya, saya pikir saya telah diganti deklarasi yang digunakan char xml sebagai parameter dengan menggunakan tidak perlu merepotkan const char xml, tapi sebaliknya, aku telah menciptakan yang baru dan meninggalkan yang lain di tempat.