【C++】在C++中擴充套件容器功能(如std::vector)而不從其繼承的正確方法?
我已經反覆閱讀過從STL容器is a Bad Thing 繼承的資訊。
(我知道背後的原因是什麼,這不是我的問題。)
牢記以上幾點,擴充套件容器功能的正確方法是什麼?
例如,如果我想要類似vector的容器,當operator[]的引數大於或等於容器的大小時,容器會自動調整大小,我該怎麼辦?
對我來說,C++ 11中最明顯的解決方案是:
template<class T, class A = std::allocator<T> >
class auto_vector : public std::vector<T, A>
{
typedef std::vector<T, A> base_type;
public:
using base_type::vector;
typename base_type::reference operator[](typename base_type::size_type n)
// I don't need const version, so I'll just ignore it here
{
if (n >= this->base_type::size())
{ this->base_type::resize(n + 1); }
return this->base_type::operator[](n);
}
};
在C++ 03中,它將是:template<class T, class A = std::allocator<T> >
class auto_vector : public std::vector<T, A>
{
typedef std::vector<T, A> base_type;
public:
explicit auto_vector(
typename base_type::allocator_type const &alloc =
typename base_type::allocator_type())
: base_type(alloc) { }
explicit auto_vector(
typename base_type::size_type n,
value_type const &val = value_type(),
typename base_type::allocator_type const &alloc =
typename base_type::allocator_type())
: base_type(n, val, alloc) { }
template<class InIt>
auto_vector(InIt f, InIt l, typename base_type::allocator_type const &alloc =
typename base_type::allocator_type())
: base_type(f, l, alloc) { }
auto_vector(auto_vector const &v) : base_type(v) { }
typename base_type::reference operator[](typename base_type::size_type n)
// I don't need const version
{
if (n >= this->base_type::size())
{ this->base_type::resize(n + 1); }
return this->base_type::operator[](n);
}
};
這兩個都是不好的做法,因為它們繼承自std::vector。那麼,實現這種目標的“正確”方法是什麼?
解決辦法
首先,關於...
“我反覆讀過,從STL容器繼承是一件壞事。”
…參考提供了STL容器沒有虛擬解構函式的原因。
對於新手來說,不要從沒有虛擬解構函式的類派生無疑是一個很好的建議。這使他們無法例如使用std::stack訪問受保護的成員,這使他們無法使用Microsoft COM技術等。但是,總的來說,對於新手來說,其淨收益是巨大的。同樣,我們建議新手不要使用原始陣列,而要直接使用new和delete,這對於新手來說是一個巨大的淨優勢,但是仍然,有些人(有經驗的人)必須這樣做,必須實現新手是(理想情況下)限制使用,以免任何C++程式中都沒有動態分配。
因此,像std::stack這樣的類清楚地表明瞭絕對的規則是胡說八道,有經驗的程式設計師必須權衡利弊,並且在決定繼承時,必須在public,protected和private繼承之間進行選擇。
公開繼承:如果新手動態分配auto_vector,然後嘗試通過在指向delete的指標上使用std::vector來銷燬它,則該設計將無法指導新手正確使用。因此,如果目標是強烈指導新手正確使用,那麼要麼不這樣做,要麼新增使新手難以動態分配的功能。例如。新增一個operator new以及其他型別不可訪問的引數,或者其自身不可訪問。
對於當前情況,其他多型訪問作為std::vector並不是問題,因為索引std::vector超出其大小的程式碼已經具有未定義行為。
對於其他情況,人們可以想象的情況與OP的情況不同,必須考慮其他情況的特殊性:通用絕對規則在程式設計中不佔優勢(至少在C++程式設計中不是如此)。
在這種情況下,是專業的公共繼承:好吧,這是“我想要所有基類的功能”的最簡單,最直接,最明確的表達。