[英]C++ Class Extension
有沒有辦法向類添加新方法,而不修改原始類定義(即包含類和相應的.h文件的編譯.lib),如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");
一般不是。 但是,如果庫沒有創建需要您的擴展的類的實例,並且您能夠修改應用程序中創建類的實例並需要擴展的所有位置,那么您可以使用以下方法:
例:
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.