codeblocks:編譯問題 undefined reference to vtable for...
阿新 • • 發佈:2019-01-13
一個嚴重的問題,在codeblocks用C++程式設計中出現 undefined reference to vtable for…的問題,昨天就遇到了,今天通過查資料才解決,主要涉及到類中的虛擬函式實現的問題。
相信大家都有習慣,程式設計時,寫幾個函式就編譯一下,否則全寫好再編譯的話會,會導致一堆報錯讓人頭疼。但正是這個習慣讓我碰到了 undefined reference to ‘vtable for …’ 的問題。
先丟擲結論:寫類函式實現的時候一定要先寫虛擬函式的實現,不然就會出現上面的問題。
下面寫下問題發現過程:
我在練習類繼承的時候,先寫好了基類和派生類框架,然後在另一個檔案中寫函式實現
//brass.h //brass account 基類 class Brass { std::string fullname; long acctNum; double balance; public: Brass(const std::string &s = "nobody", long an = -1, double bal = 0.0); void Deposit(double amt); virtual void Withdraw(double amt);//虛擬函式 double Balance()const; virtual void ViewAccount()const;//虛擬函式 virtual ~Brass(){}//虛擬函式,行內函數 }; //brass account plus 派生類 class BrassPlus : public Brass//對於解決這個問題不重要,可以不看 { double maxloan; double rate; double owesBank; public: BrassPlus(const std::string &s = "nobody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125); BrassPlus(const Brass &ba, double ml = 500, double r = 0.11125); virtual void ViewAccount()const; virtual void Withdraw(double amt); void ResetMax(double m){maxloan = m;} void ResetRate(double r){rate = r;} void ResetOwes(){owesBank = 0;} };
下面是類實現的一部分
Brass::Brass(const std::string &s, long an, double bal) { fullname = s; acctNum = an; balance = bal; } void Brass::Deposit(double amt) { if (amt < 0) std::cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; else balance += amt; }//只實現了基類的建構函式和一個存錢函式
如果只寫到這裡進行編譯會出上面的錯誤,問題在於**寫繼承類的時候,必須將基類中的虛擬函式實現寫好才可以寫繼承類。**我將基類的虛擬函式全部寫好如下(並沒有寫派生類中的實現):
#include <iostream>
#include <string>
#include "brass.h"
// std::string fullname;
// long acctNum;
// double balance;//類中的三個內容
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
format setFormat();
void restore(format f, precis p);//為了更改輸出格式方便
Brass::Brass(const std::string &s, long an, double bal)
{
fullname = s;
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
if (amt < 0)
std::cout << "Negative deposit not allowed; " << "deposit is cancelled.\n";
else
balance += amt;
}
void Brass::Withdraw(double amt)//基類虛擬函式
{
format initialState = setFormat();
precis prec = std::cout.precision(2);
if (amt < 0)
std::cout << "Withdrawal amount must be positive; "
<< "Withdrawal canceled.\n";
else if (amt <= balance)
balance -= amt;
else
std::cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
restore(initialState, prec);
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAccount()const//基類虛擬函式
{
format initialState = setFormat();
precis prec = std::cout.precision(2);
std::cout << "Client: " << fullname << std::endl;
std::cout << "Account Number: " << acctNum << std::endl;
std::cout << "Balacce: $" << balance << std::endl;
restore(initialState, prec); //restore original format
}
format setFormat()//重點在於兩個虛擬函式寫好了,這些不懂的話沒關係,以後就懂了
{
return std::cout.setf(std::ios_base::fixed,
std::ios_base::floatfield);
}
void restore(format f, precis p)
{
std::cout.setf(f, std::ios_base::floatfield);
std::cout.precision(p);
}
在此時進行編譯就已經通過了,問題得到解決。但是深層次的原因還是不很理解。
我的簡單理解是問題出在最開始的.h檔案,也就是寫類框架的時候。一般正常思路是寫派生類的時候,基類的一切都已經寫好了,派生類總不能繼承於一個半吊子基類。然後發現我錯了,關鍵在於類中的虛擬函式。
類中的虛擬函式是個複雜的東西。不僅僅是基類,寫派生類的時候也要寫完派生類的虛擬函式實現再編譯才會通過,應該涉及到動態聯編的原理,可查閱相關資料。
所以以後練習的時候要注意,先寫虛擬函式,以上程式碼出自c++primer plus 程式清單13.7、8。