C++ undefined reference to vtable for hatası çoğunlukla sanal fonksiyonlu bir sınıfın sanal tablo bilgisi için gereken tanımı bağlama aşamasında bulunamadığında ortaya çıkar. En yaygın nedenler, sanal bir fonksiyonun yalnızca bildirilmesi, sanal yıkıcının tanımlanmaması veya ilgili .cpp dosyasının derleme hedefine eklenmemesidir.
Bu hata sözdizimi derlemesinden sonra görülür; çünkü sınıf bildirimi tek başına geçerli olabilir, ancak ayrı derlenen dosyalar birleştirilirken gerekli sembol bulunamayabilir. Hatanın nedenini anlamak için önce vtable kavramını, ardından bağlayıcının görevini ayırmak gerekir.
vtable nedir ve hata hangi aşamada oluşur?
vtable, sanal fonksiyon çağrılarını nesnenin gerçek türündeki uygulamaya yönlendirmek için derleyici ve ABI uygulamalarında kullanılan tablo modelidir. Örneğin Sekil türünden bir gösterici gerçekte Daire nesnesini gösteriyorsa, sekil->alan() çağrısının Daire::alan() işlevine yönlendirilmesi gerekir.
Polimorfik bir nesnede çoğu uygulama, vtable adresini tutan gizli bir gösterici kullanır. Bu gösterici genellikle vptr olarak adlandırılır. Sanal çağrı sırasında derleyici, nesnenin vptr değerinden ilgili vtable girdisini bulur ve çağrıyı doğru sanal fonksiyona yönlendirir. C++ standardı bu davranışı ve sanal fonksiyon kurallarını tanımlar, ancak adı vtable olan belirli bir veri yapısını zorunlu tutmaz. Vtable ve vptr, kullanılan derleyici ile ABI'nin seçtiği uygulama ayrıntılarıdır.
Bir çeviri birimi, bir kaynak dosyanın ve o dosyada doğrudan ya da dolaylı olarak eklenen başlıkların derleyici tarafından birlikte işlenen hâlidir. Derleyici her çeviri birimini ayrı bir nesne dosyasına dönüştürebilir. Bağlayıcı ise bu nesne dosyalarını ve kütüphaneleri birleştirerek çalıştırılabilir dosyayı oluşturur.
Bu nedenle başlık dosyasındaki sanal fonksiyon bildirimi derleme aşamasında sorun çıkarmayabilir. Vtable'ın veya vtable içindeki bir fonksiyonun gerçek tanımı bağlama sırasında bulunamazsa undefined reference hatası alınır. GCC'nin kullandığı yaygın ABI uygulamasında vtable'ın belirli bir çeviri biriminde üretilmesi, sınıfın ilk uygun sanal fonksiyonuna, yani key function'a bağlı olabilir. Bu fonksiyon tanımlanmadığında bağlayıcı vtable sembolünü de bulamayabilir.
Eksik sanal fonksiyon tanımı vtable hatasını nasıl üretir?

Aşağıdaki örnekte Daire::alan() bildiriliyor, ancak tanımı bırakılıyor. Sekil sınıfındaki alan() saf sanal olduğu için Daire bu işlevi uygulamak zorunda; bildirim yapılması, işlev gövdesinin kendiliğinden oluşturulduğu anlamına gelmez.
// sekil.hpp
class Sekil {
public:
virtual ~Sekil() = default;
virtual double alan() const = 0;
};
// daire.hpp
#include "sekil.hpp"
class Daire : public Sekil {
public:
explicit Daire(double yaricap);
double alan() const override;
private:
double yaricap_;
};
// daire.cpp
#include "daire.hpp"
Daire::Daire(double yaricap) : yaricap_(yaricap) {
}
// Daire::alan() tanımı eksik
// main.cpp
#include <iostream>
#include "daire.hpp"
int main() {
Daire daire(2.0);
Sekil& sekil = daire;
std::cout << sekil.alan() << 'n';
}
GCC ile tüm kaynak dosyaları komuta eklenmiş olsa bile eksik tanım nedeniyle bağlama başarısız olur:
g++ -std=c++17 main.cpp daire.cpp -o alan
GCC ve GNU bağlayıcı kombinasyonunda dosya yolları, geçici nesne dosyalarının adları ve adres satırları değişebilir. Hatanın önemli bölümleri genellikle vtable for Daire ile Daire::alan() const sembollerinin tanımsız olduğunu gösterir. Clang, kullanılan bağlayıcıya göre benzer sembolü undefined reference ya da LLVM bağlayıcısında undefined symbol biçiminde raporlayabilir. Bu fark, sorunun kaynağını değiştirmez: alan() için bir tanım üretilmemiştir.
Tanımı daire.cpp içine eklediğinde vtable için gerekli işlev bağlantılanabilir:
// daire.cpp
#include "daire.hpp"
Daire::Daire(double yaricap) : yaricap_(yaricap) {
}
double Daire::alan() const {
return 3.141592653589793 * yaricap_ * yaricap_;
}
Ardından GCC veya Clang ile bütün kaynak dosyalarını derleyebilirsin:
g++ -std=c++17 main.cpp daire.cpp -o alan
./alan
clang++ -std=c++17 main.cpp daire.cpp -o alan
./alan
Programın beklenen çıktısı 12.5664 değeridir. Buradaki düzeltme vtable'a doğrudan bir tablo eklemek değildir; eksik sanal fonksiyon tanımını sağlayarak derleyicinin ve ABI'nin gerekli sınıf verisini üretmesine ve bağlayıcının sembolleri eşleştirmesine imkân vermektir.
Sanal yıkıcı ve imza uyuşmazlığı nasıl kontrol edilir?

Sanal yıkıcıyı kontrol ederken bildirimin ve tanımın ayrı şeyler olduğunu doğrula. virtual ~Sekil(); satırı yalnızca bir bildirimdir. Yıkıcının gövdesi bir .cpp dosyasında ayrıca tanımlanabilir:
// Sekil.hpp
class Sekil {
public:
virtual ~Sekil();
virtual double alan() const = 0;
};
// Sekil.cpp
#include "Sekil.hpp"
Sekil::~Sekil() = default;
Bu tanım bulunmazsa bağlayıcı, sanal yıkıcı sembolünü ve ilgili vtable verisini oluşturmak için gereken tanımı bulamayabilir. Daire::alan() tanımının eksik olması da benzer biçimde bağlama aşamasında çözülmemiş sanal fonksiyon sembollerine yol açar.
Tanımın bildirime birebir uyduğunu da kontrol et. const, noexcept, referans niteleyicileri, ad alanı ve parametre türleri imzanın parçasıdır. Örneğin bildirim double alan() const; biçimindeyse tanımın double Daire::alan() const olması gerekir. const ifadesini tanımdan kaldırmak, başka bir üye fonksiyon tanımlamaya çalışmak anlamına gelir. Türetilmiş sınıfta override kullanmak, bu tür uyuşmazlıkların önemli bir bölümünü bağlama aşamasına kalmadan yakalar.
“İlk sanal fonksiyon vtable’ı belirler” ifadesi bütün derleyiciler için evrensel bir C++ kuralı değildir. C++ standardı vtable’ın nasıl uygulanacağını zorunlu tutmaz. Örneğin Itanium C++ ABI kullanan uygulamalarda key function, sınıf tanımı sırasında satır içi olmayan ilk saf olmayan sanal fonksiyon olarak tanımlanır ve vtable’ın bu fonksiyonun tanımını içeren çeviri biriminde üretilmesiyle ilişkilendirilir. Bu yüzden eksik bir sanal fonksiyon tanımı, bazı ABI uygulamalarında doğrudan vtable hatası şeklinde görünebilir.
Daire.cpp derleme hedefine eklenmezse hata neden sürer?
Daire.cpp içinde doğru tanımların bulunması tek başına yeterli değildir. Bağlayıcı, derlenmiş nesne dosyalarını birleştirir; kaynak dosyası derleme hedefinin parçası değilse bu dosyadaki tanımlar son çalıştırılabilir dosyaya girmez. C++ derleyicilerinde kaynak dosyasının ismi, bağlama komutuna verilen bir girdi olarak kullanılabilir.
// Sekil.hpp
class Sekil {
public:
virtual ~Sekil() = default;
virtual double alan() const = 0;
};
class Daire : public Sekil {
public:
~Daire() override;
double alan() const override;
};
// Daire.cpp
#include "Sekil.hpp"
Daire::~Daire() = default;
double Daire::alan() const {
return 3.14159;
}
// main.cpp
#include "Sekil.hpp"
int main() {
Daire daire;
return daire.alan() == 0.0;
}
g++ main.cpp -o alan komutunda yalnızca main.cpp derlenir ve bağlanır. GCC ile bu komut, eksik tanımlara bağlı olarak undefined reference to 'Daire::alan() const', undefined reference to 'Daire::~Daire()' veya undefined reference to 'vtable for Daire' türünde hatalar üretebilir. clang++ main.cpp -o alan komutunda da bağlama başarısız olur; Clang sürücüsü genellikle çözülmeyen sembolleri ve ardından bağlayıcının başarısız olduğunu bildiren bir satırı gösterir.
Doğru komut, Daire.cpp dosyasını da hedefe ekler:
g++ main.cpp Daire.cpp -o alan
Bu kez iki kaynak dosyası ayrı çeviri birimlerine dönüştürülür ve elde edilen nesne dosyaları birlikte bağlanır. CMake kullanıyorsan aynı ilişkiyi add_executable(alan main.cpp Daire.cpp) ya da mevcut hedef için target_sources(alan PRIVATE Daire.cpp) ile kurabilirsin. CMake dokümantasyonu, çalıştırılabilir hedefin kaynak listesinin bu komutlarla tanımlandığını belirtir.
- Her sanal fonksiyon ve sanal yıkıcı için tanım bulunuyor mu?
- Tanımın imzası, bildirimle tamamen aynı mı?
- Tanımı taşıyan
.cppdosyası hedefin kaynak listesine eklenmiş mi? - Eski nesne dosyaları temizlenip proje yeniden derlendi mi?
Hata ayıklama sırası ve Qt moc için kısa not
Bu hatayı çözmek için önce sınıf tanımlarını, ardından derleme hedefini ve son olarak kütüphane bağlantılarını kontrol etmek gerekir. Mesajı doğrudan bir kütüphane eksikliği olarak yorumlamak yanıltıcı olabilir; sorun çoğu zaman sanal fonksiyon tanımının veya ilgili nesne dosyasının son bağlama aşamasına ulaşmamasından kaynaklanır.
- Hata mesajındaki sınıf adını belirle. Mesajda geçen sınıf, incelemeye başlayacağın noktadır. Sınıfın bildirimini içeren başlık dosyasını açarak sanal fonksiyonları ve sanal yıkıcıyı listele.
- Her sanal fonksiyonun tanımını ara. Bildirimdeki imzayla tanımdaki imzayı karşılaştır. Parametre türleri,
const, referans kullanımı, ad alanı ve benzeri ayrıntılar aynı olmalıdır. - Sanal yıkıcıyı ayrıca kontrol et.
virtual ~Sekil();gibi bir bildirim varsa, yıkıcının bir kaynak dosyada tanımlandığından emin ol. Sınıf soyut olsa bile bu tanım gerekli olabilir. - Tanımın bulunduğu çeviri birimini hedefe ekle. İlgili
.cppdosyasının CMake içindeadd_executable(),add_library()veyatarget_sources()ile oluşturulan hedefe dahil edildiğini kontrol et. Dosya diskte bulunsa da hedefe eklenmemişse bağlayıcı tanımı göremez. - Temiz derleme yap. Eski nesne dosyaları ve önbellekler hatalı yapılandırmayı koruyabileceği için derleme dizinini temizleyip projeyi yeniden yapılandır ve derle.
Bu kontroller tamamlandıktan sonra kütüphane bağlantılarını incelemek daha anlamlıdır. Çünkü eksik kütüphane, tanımı gerçekten içeren nesne dosyası hedefe hiç eklenmediğinde oluşan vtable hatasını tek başına açıklamaz.
Qt kullanan projelerde, Q_OBJECT içeren sınıflar için moc tarafından üretilen C++ kodu derlenip hedefe bağlanmadığında benzer vtable bağlantı hataları görülebilir.
Sık Sorulan Sorular
undefined reference to vtable for hatası yalnızca eksik kütüphane bağlantısından mı kaynaklanır?
Hayır, bu hata eksik sanal fonksiyon tanımı, tanımlanmamış sanal yıkıcı, ilgili .cpp dosyasının hedefe eklenmemesi veya Qt projelerinde eksik moc çıktısı gibi nedenlerle de oluşabilir. Kütüphaneleri incelemeden önce sınıf tanımlarını ve derleme hedefini kontrol etmek daha doğru bir başlangıçtır.
virtual ~Sekil(); bildirimi varsa sanal yıkıcı neden ayrıca tanımlanmalıdır?
virtual ~Sekil(); bildirimi, yıkıcının varlığını duyurur ancak gövdesini oluşturmaz. Nesne yok edilirken çağrılacak işlevin tanımı bir kaynak dosyada bulunmalı ve bu dosya bağlama aşamasına dahil edilmelidir.
CMake dosyasına yeni bir.cpp eklenmezse vtable bağlantı hatası neden oluşur?
CMake dosyasına eklenmeyen .cpp dosyası derleme hedefinin parçası olmaz. Bu durumda sanal fonksiyonun veya yıkıcının tanımı kaynak kodda bulunsa bile ilgili nesne dosyası son bağlama komutuna katılmaz ve bağlayıcı gerekli sembolü bulamaz.
C++ standardı vtable kullanımını zorunlu tutuyor mu?
C++ standardı vtable adını veya belirli bir vtable düzenini zorunlu tutmaz. Vtable, derleyicilerin ve kullandıkları ABI uygulamalarının sanal çağrıları gerçekleştirmek için tercih ettiği bir uygulama ayrıntısıdır; hata mesajındaki ifade bu nedenle belirli bir derleyici ve bağlayıcı davranışını yansıtır.
Bu teşhis sırası, hatanın kaynağını sınıf tanımından derleme hedefine doğru daraltarak gereksiz kütüphane değişikliklerinden kaçınmana yardımcı olur.