簡體   English   中英

c ++開關與成員函數指針與虛擬繼承

[英]c++ switch vs. member function pointer vs. virtual inheritance

我試圖分析實現多態的各種方法之間的權衡。 我需要一個對象列表,它們在成員函數中有一些相似之處和一些差異。 我看到的選項如下:

  1. 每個對象都有一個標志,每個函數都有一個switch語句。 標志的值將每個對象定向到每個函數的特定部分。
  2. 在對象中有一個成員函數指針數組,這些指針在構造時分配。 然后,我調用該函數指針以獲取正確的成員函數。
  3. 有一個帶有幾個派生類的虛基類。 這樣做的一個缺點是我的列表現在必須包含指針,而不是對象本身。

我的理解是,由於成員函數的保證接近,選項3中列表中的指針查找將比選項2的成員函數查找花費更長的時間。

這些選項有哪些優點/缺點? 我的首要任務是性能優於可讀性。 有多態的其他方法嗎?

  1. 每個對象都有一個標志,每個函數都有一個switch語句。 標志的值將每個對象定向到每個函數的特定部分

    好吧,如果基於標志的代碼變化非常小,這可能是有意義的。 這最小化了必須適合高速緩存的(重復)代碼量,並避免了任何函數調用間接。 在某些情況下,這些好處可能會超過switch語句的額外成本。

  2. 在對象中有一個成員函數指針數組,這些指針在構造時分配。 然后,我調用該函數指針以獲取正確的成員函數

    您保存一個間接(到vtable),但也使您的對象更大,因此更少適合緩存。 不可能說哪個會占據主導地位,所以你只需要描述一下,但這不是一個明顯的勝利

  3. 有一個帶有幾個派生類的虛基類。 這樣做的一個缺點是我的列表現在必須包含指針,而不是對象本身

    如果您的代碼路徑不同,將它們完全分開是合理的,這是最干凈的解決方案。 如果需要對其進行優化,可以使用專用的分配器來確保它們是順序的(即使容器中沒有順序),也可以使用類似於Boost.Any的聰明包裝器將對象直接移動到容器中。 你仍然會得到vtable間接,但我更喜歡這個#2,除非分析顯示它真的是一個問題。

因此,在您決定之前,您應該回答幾個問題:

  1. 共享多少代碼,有多少變化?
  2. 對象有多大,並且內聯函數指針表會對緩存未命中統計數據產生重大影響嗎?

並且,在你回答了這些之后,你應該只是簡介。

使用switch語句,如果要添加新類,則需要修改打開類的所有位置,這可能位於代碼庫中的不同位置。 您的代碼庫之外可能還有一些地方需要修改,但也許您知道在這種情況下不是這種情況。

使用每個成員中的成員函數指針數組,唯一的缺點是您為每個對象復制該內存。 如果您知道只有一個或兩個“虛擬”功能,那么這是一個不錯的選擇。

至於虛函數,你是正確的,你必須堆分配它們(或手動管理內存),但它是最可擴展的選項。

如果您不是可擴展的,則(1)或(2)可能是您的最佳選擇。 與往常一樣,唯一的方法是衡量。 我知道許多編譯器在某些情況下會通過跳轉表實現switch語句,跳轉表基本上與虛函數表相同。 對於少量的case語句,他們可能只使用二進制搜索分支。

測量!

實現更快的多態性的一種方法是通過CRTP慣用法和靜態多態性

template<typename T>
struct base
{
    void f()
    {
         static_cast<T*>( this )->f_impl();
    }
};

struct foo : public base<foo>
{
    void f_impl()
    {
       std::cout << "foo!" << std::endl;
    }
};

struct bar : public base<bar>
{
    void f_impl()
    {
       std::cout << "bar!" << std::endl;
    }
};

struct quux : public base<quux>
{
    void f_impl()
    {
       std::cout << "quux!" << std::endl;
    }
};


template<typename T>
void call_f( const base<T>& something )
{
    something.f();
}

int main()
{
    foo my_foo;
    bar my_bar;
    quux my_quux;

    call_f( my_foo );
    call_f( my_bar );
    call_f( my_quux );
}

這輸出:

FOO!
酒吧!
QUUX!

靜態多態性比虛擬分派執行得更好 ,因為編譯器知道在編譯時將調用哪個函數,並且它可以內聯所有內容

即使它提供動態綁定,它也不能以通用的異構容器方式執行多態,因為基類的每個實例都是不同的類型。
但是,這可以通過boost::any來實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM