C++虛函數聲明和定義以及g++編譯遇到的一些問題

遇到了一些麻煩的,記錄下來作爲教訓…..


1. 虛函數的聲明和定義

具體關於虛函數的知識不做多講,我在定義一個抽象類時,忘了將一個虛函數聲明爲 純虛函數,又沒有對其定義, 導致編譯報錯時報錯如下:

undefined reference to `vtable for Fibonacci'

錯誤提示的很明顯,就是無法生成虛函數表。

我們知道,虛函數表(地址)在定義了虛函數的類所實例化的對象內存中的第一個位置,也就是在實例化過程中生成了虛表。這個錯誤提示在stackflow中最常見的解答就是類中聲明瞭虛函數,卻沒有定義。

總結一下虛函數聲明和定義的規則如下:

  • 類中的virtual函數,要麼設爲純虛函數,要麼有定義,否則無法生成虛函數表。

    • 虛函數的可以類外定義,但是必須加上類名,類外定義不需要加virtual
    • 聲明爲純虛函數,則類爲抽象類,無法實例化,進一步強調,想要實例化有虛函數的類,必須對虛函數進行定義
  • 基類定義爲虛函數,則子類同名函數也爲虛函數,無論是否有virtual關鍵字修飾(一般聲明時加virtual,便於閱讀)

  • 凡是基類定義有虛函數,則基類需要定義虛析構函數(根據上一條法則,虛析構函數要麼有定義,要麼純虛,一般不設爲純虛,可以定義空白)

  • 虛函數通過虛表實現,虛表是類實例化時生成在對象中的(虛表地址),所以如果一個類能夠實例化,則其虛函數必須有定義,如果不想定義虛函數,只能聲明爲純虛函數,留給子類定義。

舉例如下:

基類爲一個抽象類

// num_sequence.h
#ifndef _NUM_SEQUENCE

#define _NUM_SEQUENCE

#include <string>
#include <iostream>

using namespace std;

class num_sequence {

    public:
        num_sequence() {
            cout << " create a num sequence" << endl;
        }

        virtual ~num_sequence() {
            cout << "~num_sequence has been called" << endl;
        }  

        virtual int elem(int pos) const= 0;
        virtual const string what_am_i() const = 0;

        static int max_elems(){
            return  _max_elems;
        }

        virtual ostream& print(ostream& = cout) const = 0;

    protected:
        virtual void gen_elems(int pos) const = 0;      // const指針指向自認爲const的一個對象, 這個對象不能通過const指針進行修改, 但可以通過其他方式進行修改

        bool check_integrity(int pos) const{
            if(pos <=0 || pos >_max_elems) {
                cerr << "Invalid position: " << pos << endl;
                return false;
            }
            return true;
        }

        const static int _max_elems = 1024;         // 最大元素個數
};

#endif

子類不是抽象類,則必須實現基類的純虛函數

子類聲明:

// Fibonacci.h

#ifndef _FIBONACCI

#define _FIBONACCI

#include "num_sequence.h"
#include <vector>

using std::vector;

class Fibonacci : public num_sequence {

    public:
        Fibonacci(int len = 1, int beg_pos = 1): _length(len), _beg_pos(beg_pos){
            cout << "create a Fibonacci sequence" << endl;
        }

        virtual int elem(int pos) const ;    // 子類類中聲明virtual時, 應當和父類保持精確一致性

        virtual ostream& print(ostream &os=cout) const;

        virtual const string what_am_i() const{
            cout << " I am Fibonacci sequence" << endl;
        }


        int length() const {
            return _length;
        }

        int beg_pos() const{
            return _beg_pos;
        }

        ~Fibonacci() {
            cout << "~Fibonacci has been called" << endl;
        }

    protected:
        virtual void gen_elems(int pos) const;

        int                 _length;    // 長度
        int                 _beg_pos;   // 起始位置
        static vector<unsigned int>  _elems;     // 元素容器

};


#endif

子類虛函數定義:

// Fibonacci.cpp

#include "Fibonacci.h"

vector<unsigned int> Fibonacci::_elems;

// 類外定義virtual函數時, 不需要virtual關鍵字
int Fibonacci::elem(int pos) const {

    if(!check_integrity(pos))           // 檢查pos可用性
        return 0;

    if(pos > _elems.size() )            // 檢查當前元素個數是夠足夠, 當不足時自動生成
        Fibonacci::gen_elems(pos);      // 利用類作用域, 在編譯時指明調用函數  

    return _elems[pos - 1];
}

ostream& Fibonacci::print(ostream &os) const {

    int elem_pos = _beg_pos - 1;
    int end_pos = elem_pos + _length;

    if(end_pos > _elems.size()) {
        Fibonacci::gen_elems(end_pos);
    }

    while(elem_pos < end_pos) {
        os << _elems[elem_pos] << "\t";
        ++elem_pos;
    }
    return os;
}

void Fibonacci::gen_elems(int pos) const {

    if(_elems.empty()) {
        _elems.push_back(1);
        _elems.push_back(1);
    }

    if(_elems.size() <= pos) {
        int ix = _elems.size();
        int n_2 = _elems[ix-2];
        int n_1 = _elems[ix-1];

        for(; ix<=pos; ++ix){
            int elem = n_2 + n_1;
            _elems.push_back(elem);
            n_2 = n_1;
            n_1 = elem;
        }
    }
}


2. g++編譯多個cpp文件

這個主要是由於我對g++用的不多,不熟悉規則。有兩個cpp文件,Fibonacci.cpp(實現Fibonacci類), test.cpp(測試Fibonacci類)。

一開始沒注意,編譯命令如下:

g++ -O0 -std=c++11 -o run test_sequence.cpp Fibonacci.cpp

總是報錯提示找不到Fibonacci中的數據成員。

原因很簡單,編譯的順序不是隨意的,則Fibonacci.cpp必須在test_sequence.cpp前面

g++ -O0 -std=c++11 -o run Fibonacci.cpp test_sequence.cpp 

順利通過。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章