我一直听说你不应该从没有虚拟析构函数的类继承而且我没注意太多,因为我只是不经常使用继承。即使您不想使用多态,此规则也适用,但您只想要所有类功能,并且还想添加更多功能?具体来说,只要我没有多态地使用它,下面的类是否安全,行为定义明确? (即没有删除派生对象的基本指针)
template<typename T>
class SomewhatSafeVector : public std::vector<T>
{
public:
typedef std::vector<T> base;
T& operator[](unsigned n) {
if (n >= base::size())
{
throw IndexOutOfBounds();
}
return base::operator[](n);
}
};
答案 0 :(得分:7)
我一直听说你不应该从没有虚拟析构函数的类继承
这是给初学者的一个经验法则,因为解释所有错综复杂需要花费太多时间,并且实际上只给他们一些基线工作的所有时间更安全(并且对于练习计划来说并不是那么昂贵) (虽然可能有点矫枉过正)。
您可以在基类中使用没有virtual
析构函数的继承。另一方面,如果基类根本没有virtual
方法,那么继承可能是该作业的错误工具。 例如:如果我使用SafeVector<T> sv; sv[3];
,那么它是安全的,但如果我std::vector<T>& v = sv; v[3];
它不是......这是因为你只是隐藏基类方法,而不是覆盖它(提高警告级别,他们会让你知道)。
这里的正确方法是使用组合,然后为实际成员创建转发方法,以实现您真正使用的方法。在实践中,它很累,因为C ++不支持委托(using attribute.insert;
),所以很多人诉诸继承......
另一种方法是提供更安全的方法作为免费方法,因为您始终可以无限制地添加免费方法。对于具有“OO”思维模式的人来说,它可能会觉得不那么惯用,而且有些运营商不能这么做。
答案 1 :(得分:5)
如果您不打算以多态方式使用该类(不删除派生对象的基本指针),那么它不是未定义的行为。
参考:
C ++ 03标准:5.3.5删除
<强> 5.3.5 / 1:强>
delete-expression运算符会销毁由new-expression创建的派生程度最高的对象(1.8)或数组 删除表达式:
:: opt delete cast-expression
:: opt delete [] cast-expression
<强> 5.3.5 / 3:强>
在第一个备选(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟类型析构函数或行为是未定义的。在第二种方法(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的.73)
答案 2 :(得分:4)
欢迎您以多态方式使用该对象,但您不能delete
多态地使用它。如果您避免通过std::vector<>*
删除指向班级对象的指针,那么您就是安全的。
除了:您可以简化operator[]
因此:
T& operator[](unsigned n) { return this->at(n); }
答案 3 :(得分:2)
是的,如果你从不使用多态(即,从不向上转换引用或指针),则无法执行不安全的破坏。
Mixin类通常以这种方式使用,而CRTP很少暗示虚拟析构函数,以命名几种模式。