Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 子类/继承标准容器?_C++_Standard Library_Inheritance - Fatal编程技术网

C++ 子类/继承标准容器?

C++ 子类/继承标准容器?,c++,standard-library,inheritance,C++,Standard Library,Inheritance,我经常读到关于堆栈溢出的语句。就个人而言,我没有发现任何问题,除非我以多态的方式使用它;i、 e.我必须使用虚拟析构函数的地方 如果我想扩展/添加标准容器的功能,那么有什么比继承更好的方法呢?将这些容器包装到自定义类中需要付出更多的努力,而且仍然不干净。您应该避免公开从标准容器派生。您可以在私有继承和组合之间进行选择,在我看来,所有的一般准则都表明,这里的组合更好,因为您不重写任何函数不要从STL容器公开派生-确实不需要它 顺便说一下,如果您想在容器中添加一堆算法,请考虑将它们作为独立函数添加迭

我经常读到关于堆栈溢出的语句。就个人而言,我没有发现任何问题,除非我以多态的方式使用它;i、 e.我必须使用
虚拟
析构函数的地方


如果我想扩展/添加标准容器的功能,那么有什么比继承更好的方法呢?将这些容器包装到自定义类中需要付出更多的努力,而且仍然不干净。

您应该避免公开从标准容器派生。您可以在私有继承组合之间进行选择,在我看来,所有的一般准则都表明,这里的组合更好,因为您不重写任何函数不要从STL容器公开派生-确实不需要它


顺便说一下,如果您想在容器中添加一堆算法,请考虑将它们作为独立函数添加迭代器范围。 因为你永远不能保证你没有以多态的方式使用它们。你在乞求麻烦。花点力气写几个函数并没有什么大不了的,而且,好吧,即使想这样做,充其量也令人怀疑。封装发生了什么?

问题是您或其他人可能会意外地将扩展类传递给一个需要引用基类的函数。这将有效地(悄悄地)切断扩展,并创建一些难以发现的bug


相比之下,编写一些转发函数似乎是一个很小的代价。

这是一个坏主意的原因有很多

首先,这是一个坏主意,因为标准容器没有虚拟析构函数。永远不要使用没有虚拟析构函数的多态性,因为无法保证在派生类中进行清理

第二,它的设计真的很糟糕。事实上,这是一个糟糕的设计,有几个原因。首先,您应该始终通过一般操作的算法来扩展标准容器的功能。这是一个简单的复杂性原因-如果您必须为它应用的每个容器编写一个算法,并且您有M个容器和N个算法,那么您必须编写M x N个方法。如果您一般地编写算法,则只有N个算法。所以你得到了更多的重用

它的设计也非常糟糕,因为您正在通过继承容器来打破一个好的封装。一个很好的经验法则是:如果可以使用类型的公共接口执行所需的操作,则将该新行为设置为该类型的外部行为。这改进了封装。如果这是您想要实现的新行为,请将其设置为命名空间范围函数(如算法)。如果要施加新的不变量,请在类中使用包含

最后,一般来说,您不应该将继承看作是扩展类行为的一种手段。这是早期OOP理论中的一个很大的、不好的谎言,它是由于对重用的不清楚的思考而产生的,并且它一直被教授和推广到今天,尽管有一个明确的理论解释它为什么不好。当您使用继承来扩展行为时,您将扩展的行为绑定到您的接口契约,从而将用户与未来的更改联系起来。例如,假设您有一个Socket类型的类,它使用TCP协议进行通信,您通过从Socket派生一个SSLSocket类并在Socket之上实现更高SSL堆栈协议的行为来扩展它的行为。现在,假设您有一个新的要求,即使用相同的通信协议,但通过USB线或电话。您需要将所有工作剪切并粘贴到从USB类或电话类派生的新类。现在,如果你发现一个bug,你必须在所有三个地方修复它,这并不总是发生,这意味着bug需要更长的时间,而且不总是得到修复


这对于任何继承层次结构A->B->C->都是通用的。。。当你想使用你在派生类中扩展的行为时,比如B,C。。对于不属于基类A的对象,必须重新设计,否则将复制实现。这导致了非常单一的设计,很难改变(想想微软的MFC,或者他们的.NET,或者-嗯,他们经常犯这个错误)。相反,只要有可能,您几乎应该始终考虑通过组合进行扩展。当您考虑“打开/关闭原则”时,应该使用继承。您应该通过继承类拥有抽象基类和动态多态性运行时,每个都将实现完整的实现。层次结构不应该很深——几乎总是两个层次。只有当您有不同的动态类别,这些类别涉及到需要区分类型安全性的各种函数时,才可以使用两个以上的类别。在这种情况下,使用抽象基直到叶类实现为止。

IMHO,如果将STL容器用作功能扩展,我没有发现继承STL容器有什么害处。(这就是我问这个问题的原因。:)

当您尝试将自定义容器的指针/引用传递给标准容器时,可能会出现潜在问题

template<typename T>
struct MyVector : std::vector<T> {};

std::vector<int>* p = new MyVector<int>;
//....
delete p; // oops "Undefined Behavior"; as vector::~vector() is not 'virtual'
模板
struct MyVector:std::vector{};
std::vector*p=新的MyVector;
//....
删除p;//oops“未定义的行为”;作为向量::~vector()不是“虚拟的”
只要遵循良好的编程实践,就可以有意识地避免此类问题

如果我想格外小心,我可以这样做:

#include<vector>
template<typename T>
struct MyVector : std::vector<T> {};
#define vector DONT_USE
#包括
模板
struct MyVector:std::vector{};
#定义不使用的向量

这将不允许完全使用
vector

希望从容器继承的最常见原因是希望向
void f(std::vector<int> &v) { ... }
class GizmoList : public std::vector<CGizmo>
{
    /* No Body & no changes.  Just a more descriptive name */
};
GizmoList aList = GetGizmos();
#include <vector>
#include <iostream>

// private inheritance, nobody else knows about the inheritance, so nobody is upcasting my
// container to a std::vector
template <class T> class MyVector : private std::vector<T>
{
private:
    // in case I changed to boost or something later, I don't have to update everything below
    typedef std::vector<T> base_vector;

public:
    typedef typename base_vector::size_type       size_type;
    typedef typename base_vector::iterator        iterator;
    typedef typename base_vector::const_iterator  const_iterator;

    using base_vector::operator[];

    using base_vector::begin;
    using base_vector::clear;
    using base_vector::end;
    using base_vector::erase;
    using base_vector::push_back;
    using base_vector::reserve;
    using base_vector::resize;
    using base_vector::size;

    // custom extension
    void reverse()
    {
        std::reverse(this->begin(), this->end());
    }
    void print_to_console()
    {
        for (auto it = this->begin(); it != this->end(); ++it)
        {
            std::cout << *it << '\n';
        }
    }
};


int main(int argc, char** argv)
{
    MyVector<int> intArray;
    intArray.resize(10);
    for (int i = 0; i < 10; ++i)
    {
        intArray[i] = i + 1;
    }
    intArray.print_to_console();
    intArray.reverse();
    intArray.print_to_console();

    for (auto it = intArray.begin(); it != intArray.end();)
    {
        it = intArray.erase(it);
    }
    intArray.print_to_console();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
10
9
8
7
6
5
4
3
2
1
std::vector<int>* stdVector = &intArray;
error C2243: 'type cast': conversion from 'MyVector<int> *' to 'std::vector<T,std::allocator<_Ty>> *' exists, but is inaccessible