1. 程式人生 > >codeblocks:編譯問題 undefined reference to vtable for...

codeblocks:編譯問題 undefined reference to vtable for...

一個嚴重的問題,在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。