首页 > 解决方案 > 如果我真的非常想从 STL 容器继承,并且我继承了构造函数并删除了 new 运算符,会发生什么?

问题描述

假设我违背了我在本网站和其他在线网站上找到的所有建议,并执行以下操作:

#include <vector>
#include <array>
#include <iostream>

using PointType = std::array<double, 3>;
class Point
:
    public PointType 
{
    public: 

        using PointType::PointType;

        Point(const Point& copy)
            :
                PointType(copy)
        {
            std::cout << "Point(const Point&)" << std::endl;
        }

        Point(Point&& rval)
            :
                PointType(rval)
        {
            std::cout << "Point(Point&&)" << std::endl;
        }

        // Prevent PolygonType* p = new Polygon; delete p;
        template<typename ...Args>
        void* operator new (size_t, Args...) = delete;
};

using PolygonType = std::vector<PointType>; 
class Polygon
:
    public PolygonType
{
    public: 

        using PolygonType::PolygonType;

        Polygon(const Polygon& copy)
            :
                PolygonType(copy)
        {
            std::cout << "Polygon(const Polygon&)" << std::endl;
        }

        Polygon(Polygon&& rval)
            :
                PolygonType(rval)
        {
            std::cout << "Polygon(Polygon&&)" << std::endl;
        }

        // Prevent PolygonType* p = new Polygon; delete p;
        template<typename ...Args>
        void* operator new (size_t, Args...) = delete;
};

如果我对从不使用new withPointPolygon或类似类型感到满意,则删除new运算符会解决未定义行为的问题:

std::array<double, 3> a = new Point({1., 2., 3.})
delete a; 

对其起作用的算法所施加的条件std::vector<PointType>是相同的:算法检查公共接口是否符合算法中所做的事情。如果我想要一个算法(函数模板)将这个点向量视为一个开放的线段链或一个封闭的多边形,这是一个问题。这排除了在解析函数模板候选时依赖隐式接口。此外,概念到达时对我没有帮助,因为容器上的条件相同,我希望算法对它们做的事情不同。因此,另一方面,如果我进行标签调度,那么使用像这样的具体类型并标记它们会使标签调度变得微不足道使用 SFINAE 使用新的元函数 fromtype_traits来检查模板参数是否已被标记为特定标签。

C++11 构造函数继承也解决了重新键入构造函数的老问题。

那么,当从 STL 中继承构造函数并new删除操作符时,仍然会发生什么?一定有什么我没有看到。

标签: c++inheritancestl

解决方案


如果您的对象将始终静态或自动分配(否new),那么它将以相同的方式被破坏,因此您不需要虚拟析构函数,因此这将按预期工作。

从标准容器派生并不是完全禁止的,它只是危险的。通过消除危险的用例,您似乎在很大程度上减轻了危险。

请注意,如果您确实允许动态分配然后通过指向基址的指针删除,那么即使派生类没有自己的状态(即没有成员),您仍然拥有 UB。对象生命周期比仅仅计算数据成员要复杂得多。

您仍然可以允许动态分配并且永远不会通过指向 base 的指针进行删除,但是这在逻辑上是否适合您,更不用说足够的保护性,仅取决于上下文。


轶事:我偶尔会从“库代码”中的矢量/地图继承,不打算由其他任何人进一步扩展。它不是开源软件,它是我控制的专有代码库,因此可以自由使用注释。如果你想通过容器的整个界面,组合是有点痛苦的。


推荐阅读