1. 程式人生 > >虛擬函式必須定義(純虛擬函式除外)

虛擬函式必須定義(純虛擬函式除外)

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;
        }
    }

}

http://blog.csdn.net/gvfdbdf/article/details/51954819