假设我违反了我在本网站和在线其他地方找到的所有建议,并且这样做:
#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;
};
如果我很满意从未与Point
或Polygon
或类似类型的 new 一起使用,则删除new
运算符可以解决以下问题:未定义的行为:
std::array<double, 3> a = new Point({1., 2., 3.})
delete a;
适用于该算法的算法在std::vector<PointType>
上施加的条件是相同的:算法检查公共接口是否适合算法中的操作。如果我希望算法(功能模板)将这些点矢量作为线段的开放链或闭合的多边形来查看,则会出现问题。解决功能模板候选时,这排除了对隐式接口的依赖。另外,概念到达时对我没有帮助,因为同样,容器上的条件相同,我希望算法对它们执行的操作不同。因此,如果我执行标签分发with SFINAE using the new metafunctions from type_traits
来检查template参数是否已使用特定标签进行标签,则使用像这样的具体类型并对它们进行标记会使标签分发变得微不足道。
C ++ 11构造函数继承也消除了重新键入构造函数的旧问题。
那么,从STL继承以继承构造函数并删除new
运算符的方式时,仍然会爆炸吗?一定有我没看到的东西。
答案 0 :(得分:6)
如果您的对象将始终是静态或自动分配的(无new
),则将以相同的方式对其进行销毁,因此您不需要虚拟销毁器,因此可以按预期工作
并不是绝对禁止从标准容器中派生,这很危险。您似乎通过消除危险的用例在很大程度上减轻了危险。
请注意,如果您 did 允许动态分配,然后通过指向基址的指针删除,则即使派生类不拥有自己的状态(即,没有成员),您仍然会拥有UB。 )。对象生存期比仅计算数据成员要复杂得多。
您仍然可以允许动态分配,并且永远不要通过指向基础的指针删除,但是,从逻辑上讲,这是否适合您,更不用说具有足够的保护性,仅取决于上下文。
>轶事:我曾经偶尔会从“库代码”中的矢量/地图继承而来,并不打算被其他人进一步扩展。它不是开源软件,而是我控制下的专有代码库,因此可以自由使用注释。如果您想遍历容器的整个界面,则组合会有些痛苦。