簡體   English   中英

C ++類擴展

[英]C++ Class Extension

有沒有辦法向類添加新方法,而不修改原始類定義(即包含類和相應的.h文件的編譯.lib),如C#的類擴展方法?

沒有.C ++沒有這樣的能力。

正如其他答案中所提到的,常見的解決方法是:

  • 定義派生類,可能使用工廠來隱藏實際的實現類
  • 定義裝飾器
  • 定義對該類實例進行操作的非成員函數

不,你不能用C ++做到這一點。

如果你想要達到這樣的目的,你有兩個選擇,

  • 您可以繼承自該類(如果這是一個選項,它可能不合法,因為該類可能尚未編寫為允許繼承)
  • 您可以編寫自己的包裝類,它具有相同的接口+新方法,並委托給您要擴展的那個。

我更喜歡代表團的做法。

C#類擴展方法主要是語法糖。 您可以使用自由函數獲得相同的功能(例如,具有對類的引用或常量引用的函數作為其第一個參數)。 既然這對STL很有效,為什么不為你的班級?

在C ++中,您可以使用自由函數,但有時在將多個函數嵌套在一起時,擴展方法的效果會更好。 看看這個C#代碼:

var r = numbers.Where(x => x > 2).Select(x => x * x);

如果我們使用自由函數在C ++中編寫它,它將如下所示:

auto r = select(where(numbers, [](int x) { return x > 2; }), [](int x) { return x * x; });

這不僅難以閱讀,而且難以編寫。 解決這個問題的常用方法是創建所謂的pipable函數。 這些函數是通過重載|來創建的 管道運營商(它實際上是或運營商)。 所以上面的代碼可以寫成這樣:

auto r = numbers | where([](int x) { return x > 2; }) | select([](int x) { return x * x; });

這更容易閱讀和寫作。 許多庫對范圍使用pipable函數,但也可以擴展到其他類。 Boost在它的范圍庫中使用它,pstade 烤箱使用它,而且這個C ++ linq庫也使用它。

如果您想編寫自己的pipable函數,請在此處解釋如何執行此操作 但是,其他庫提供了功能適配器以使其更容易。 Pstade egg有一個pipable適配器 ,linq提供range_extension適配器來為范圍創建一個pipable函數。

使用linq,首先只需將函數創建為函數對象,如下所示:

struct contains_t
{
    template<class Range, class T>
    bool operator()(Range && r, T && x) const
    { return (r | linq::find(x)) != boost::end(r); };
};

然后使用靜態初始化初始化函數,如下所示:

range_extension<contains_t> contains = {};

然后你可以像這樣使用你的pipable函數:

if (numbers | contains(5)) printf("We have a 5");

一般不是。 但是,如果庫沒有創建需要您的擴展的類的實例,並且您能夠修改應用程序中創建類的實例並需要擴展的所有位置,那么您可以使用以下方法:

  • 創建一個在所有需要該類實例的地方調用的工廠函數,並返回指向該實例的指針(google for Design Patterns Factory ,...)。
  • 使用所需的擴展創建派生類。
  • 使工廠函數返回派生類而不是原始類。

例:


    class derivedClass: public originalClass { /* ... */};

    originalClass* createOriginalClassInstance()
    {
         return new derivedClass();
    }
  • 當您需要訪問擴展時,您需要將原始強制轉換轉換為派生類。

這大致是如何實現Glen建議的“繼承”方法。 從理論的角度來看,Glen的“具有相同界面的包裝類”方法也非常好,但是具有稍微不同的屬性,使得在您的情況下工作的可能性降低。

有一種方法可以做到。 這是通過放松你的要求一點。 在C ++中,人們經常說類的接口不僅包括其成員函數,還包括在該類上工作所有函數

也就是說,可以將類作為參數賦予的非成員函數應該被視為其接口的一部分。

例如, std::find()std::sort()std::vector接口的一部分,即使它們不是類的成員。

如果你接受這個定義,那么你總是可以通過添加非成員函數來擴展一個類。

您無法將方法或數據物理地添加到二進制形式的類文件中。 但是,您可以通過編寫擴展類來向該類的對象添加方法和數據(功能和狀態)。 這不是直截了當的,需要基於元對象協議和接口的編程。 你需要做很多事情才能在C ++中實現這一點,因為它不支持反射開箱即用。 在這種實現中,當您通過原始類對象指針查詢由新擴展類實現的接口時,元對象實現通過元類對象返回該接口指針,該元類對象用於在運行時創建的擴展類。 這是多少可定制(基於插件)的軟件應用程序框架的工作原理。 但是,您必須記住,它需要使用其中描述對象關系的字典為所有類編寫實例化元對象,並為原始和擴展類對象提供正確的接口指針。 Dassault Systemes的CATIA V5是用這種稱為CAA V5的架構編寫的,您可以通過編寫具有所需功能的新擴展類來擴展現有組件。

抱歉,沒有。 一旦你的代碼在obj中,你就無法改變它。 如果可以在VC中完成此操作,則已經支持部分類。 但有一個例外,運算符方法可以使用全局函數進行擴展,就像在STL中實現cout <<一樣。

你當然可以:


template <typename Ext>
class Class: public Ext { /* ... */ };

這並不意味着它是最好的方法。

暫無
暫無

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

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