【C++】在C++中擴充套件容器功能(如std::vector)而不從其繼承的正確方法?

2020-11-19 C++

我已經反覆閱讀過從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技術等。但是,總的來說,對於新手來說,其淨收益是巨大的。同樣,我們建議新手不要使用原始陣列,而要直接使用newdelete,這對於新手來說是一個巨大的淨優勢,但是仍然,有些人(有經驗的人)必須這樣做,必須實現新手是(理想情況下)限制使用,以免任何C++程式中都沒有動態分配。

因此,像std::stack這樣的類清楚地表明瞭絕對的規則是胡說八道,有經驗的程式設計師必須權衡利弊,並且在決定繼承時,必須在publicprotectedprivate繼承之間進行選擇。

公開繼承:如果新手動態分配auto_vector,然後嘗試通過在指向delete的指標上使用std::vector來銷燬它,則該設計將無法指導新手正確使用。因此,如果目標是強烈指導新手正確使用,那麼要麼不這樣做,要麼新增使新手難以動態分配的功能。例如。新增一個operator new以及其他型別不可訪問的引數,或者其自身不可訪問。

對於當前情況,其他多型訪問作為std::vector並不是問題,因為索引std::vector超出其大小的程式碼已經具有未定義行為。

對於其他情況,人們可以想象的情況與OP的情況不同,必須考慮其他情況的特殊性:通用絕對規則在程式設計中不佔優勢(至少在C++程式設計中不是如此)。

在這種情況下,是專業的公共繼承:好吧,這是“我想要所有基類的功能”的最簡單,最直接,最明確的表達。

出處

Have any Question?

Let us answer it!