在实施运营商[]如何我应该包括边界检查?
-
06-07-2019 - |
题
首先,我道歉,长期导致达到这样一个简单的问题。
我在执行一级作为一个很长的1维索引的空间曲线或填写的n元组代表笛卡尔坐标这一指标相对应。
class curvePoint
{
public:
friend class curveCalculate;
//Construction and Destruction
curvePoint(): point(NULL), dimensions(0) {}
virtual ~curvePoint(){if(point!=NULL) delete[] point;}
//Mutators
void convertToIndex(){ if(isTuple()) calc(this); }
void convertToTuple(){ if(isIndex()) calc(this); }
void setTuple(quint16 *tuple, int size);
void setIndex(quint16 *index, int size);
void setAlgorithm(curveType alg){algorithm = alg;}
//Inspectors
bool isIndex(){return current==Index;}
bool isTuple(){return current==Tuple;}
size_t size(){return dimensions;}
quint16 operator[](size_t index);
enum curveType{HilbertCurve, ZCurve, GrayCodeCurve};
enum status{Index, Tuple};
private:
curveCalculate calc;
curveType algorithm;
quint16 *point;
size_t dimensions;
status current;
};
(阵列的长度所指向的 点 是 尺寸)
反正在执行的操作者[]我不知道什么是最好的方法来实现边界检查。我想要避免抛例外情况,如果可能的话,以及完整的数值范围是可用于各个数字阵列所以一个特殊的价值回报的情况下,超出边界,错误是不可能的;
我认为像这样的东西虽然实施这类定义:
quint16 curvePoint::operator[](size_t index)
{
return point[ index % dimensions ];
}
这使得它如此,我们从来没有离开边界的阵列,如果记录在案,我认为它将被罚款;尽管如此,我leary这个特殊的执行。
这看起来可接受到其他人?是否有任何其他方式的做边界检查,同时仍然满足我的约束呢?
编辑:计算的事情就是希尔伯特的曲线等都是高度混乱,混乱不够,我不想要的额外口为stl图书馆的方式。
此外,因为我将不得不转换成千上万的这些每次多层面的数据库查询,我不想要的额外费用的补功能的电话在混合,如果在所有可能的。
我不喜欢这个想法的断言;但是,如果我没有记错的话,打破在发布版本不是吗
我想我可以使用例外,这似乎是有什么每个人都是支持的,但是,我使用的脱库和避免例外的两个性和可移植性以及我希望这样做。
解决方案
最简单的解决方案就是像C ++本身那样做。这会限制用户体验的惊喜数量。
C ++本身相当一致。如果使用超出范围的数组索引,内置的[]
on指针和std::vector::operator[]
都有未定义的行为。如果要进行边界检查,请明确并使用std::vector::at
因此,如果您为您的班级做同样的事情,您可以将出境行为记录为<!> quot; standard <!> quot;。
其他提示
不管怎样执行的 算[]我不知道什么 最好的方法来实现边界检查 是。我想要避免抛 例外情况,如果可能的话, 全范围的数值是有用的 每个数字在该阵列所以一个特殊的 值返回情况下的一个出来的 边界的错误是不可能的;
然后剩下的选择是:
- 灵活设计。 你做了什么。"固定"无效的输入,以便它试图做一些有意义的。优势:功能不会崩溃。缺点:懵懵懂懂的呼叫者访问某个出界件会得到一个 谎言 作为一个结果。想象一下一个10层楼的建筑用地板1至10:
你: "谁住在3楼?"
我说: "玛丽"。
你: "谁住在9楼?"
我说: "乔".
你: "谁生活在1 203名楼?"
我说:(等等...1 203名%10=3...) >"玛丽".
你: "哇,玛丽必须享有伟大的美景 在那里.所以她拥有两套公寓呢?"
一个 bool输出参数 表明成功或失败。这种选择通常结束了在不非常有用的代码。许多用户将会忽略返回代码。你还剩下什么你在返回的其他返回的价值。
设计合同。 断言,呼叫紧边界。(为一个实用的方法在C++、见 一个异常或一个错误?通过米罗Samek 或 简单的支持设计合同在C++由佩德罗Guerreiro.)
返回
System.Nullable<quint16>
.哎呀,等一等,这不是C#。好吧,你可以回返的一个指quint16.这当然有很多影响,我将不在这里讨论和可能做出这个选择不能使用。
我最喜欢的选择是:
- 为的公共接口公布的图书馆:输入将进行检查和异常会被抛出。你排除了这个选项,因此它不是一个选择。它仍然是 我 选择用于接口的一个公开发布的图书馆。
- 内部编码:设计合同。
对我来说,这个解决方案是不可接受的,因为你可以隐藏一个很难找到的bug。 抛出超出范围的异常是要走的路,或者至少在函数中放置一个断言。
如果您需要的是某种<!>“圆形<!>”;点数组,然后你的解决方案是好的。但是,对我而言,它看起来就像隐藏了一些<!>“safe <!>”背后的索引操作符的错误。逻辑,所以我会反对你提出的解决方案。
如果不想允许索引溢出,那么你可以检查并抛出异常。
quint16 curvePoint::operator[](size_t index)
{
if( index >= dimensions)
{
throw std::overflow_error();
}
return point[ index ];
}
如果您希望减少开销,可以通过使用调试时间断言来避免异常(假设提供的索引始终有效):
quint16 curvePoint::operator[](size_t index)
{
assert( index < dimensions);
return point[ index ];
}
但是,我建议使用std :: vector <!> lt;而不是使用point和dimension成员。 quint16 GT <!>;用于点存储。它已经具有您可以使用的基于索引的访问权限:
quint16 curvePoint::operator[](size_t index)
{
// points is declared as std::vector< quint16> points;
return points[ index ];
}
让一个永不失败的运算符[]听起来不错,但是稍后可能会隐藏错误,如果调用函数使用非法偏移,则从缓冲区的开头找到一个值,并且好像这是一个有效值。
实现边界检查的最佳方法是添加断言。
quint16 curvePoint::operator[](size_t index)
{
assert(index < dimensions);
return point[index];
}
如果您的代码已经依赖于Boost库,则可能需要使用BOOST_ASSERT
。
如果我是你,我会遵循例组通过stl。
在这种情况下 std::vector
供应两个方法: at
这是边界检查 operator[]
这是没有的。这允许客户决定,版本使用。我绝对不会使用的 % size()
, ,因为这只是隐藏的错误。然而,边界检查将会增加很多的开销用于当迭代过一个大型集合,这就是为什么它应该是可选择的。虽然我同意与其他的海报,声称是一个非常好的想法,因为这将只会导致性能打在调试版本。
你也应该考虑返回的引用和提供const和不const版本。这里是功能的声明 std::vector
:
reference at(size_type _Pos);
const_reference at(size_type _Pos) const;
reference operator[](size_type _Pos);
const_reference operator[](size_type _Pos) const;
作为一个好的经验法则,如果我不知道该如何指定一个API我看的例子怎么其他人指定的类似Api。还当过我使用一个API我尝试判断或评,找到位我喜欢和不喜欢。
感谢Daniel Daranas在帖子中对C#功能的评论,我设法找到了一个可能的解决方案。正如我在我的问题中所述,我使用的是Qt库。因为我可以使用QVariant。 QVariant可以设置为无效状态,可以通过接收它的函数进行检查。所以代码会变成:
QVariant curvePoint::operator[](size_t index){
QVariant temp;
if(index > dimensions){
temp = QVariant(QVariant::Invalid);
}
else{
temp = QVariant(point[index]);
}
return temp;
}
当然这有可能在函数中插入一些粗略的开销,所以另一种可能性就是使用一对模板。
std::pair<quint16, bool> curvePoint::operator[](size_t index){
std::pair<quint16, bool> temp;
if(index > dimensions){
temp.second = false;
}
else{
temp.second = true;
temp.first = point[index];
}
return temp;
}
或者我可以使用QPair,它具有完全相同的功能,并且可以使STL不需要链接。
你可以添加一个<!> quot; out of bounds <!> quot; []运算符的异常(或至少是一个断言)。
这应该可以解决任何问题,特别是在调试时。
除非我对某些事情产生了极大的误解,否则
return point[ index % dimensions ];
根本没有检查边界。它从行的完全不同的部分返回一个真正的值,这将使得检测错误变得更加困难。
我要么:
- 抛出异常或断言(虽然你说你不想这样做)
- 简单地取消引用指向<!>“自然<!>”中的数组。方式(即只是跳过任何内部检查)。优于%的优点是它们更有可能(虽然未定义未定义)得到<!>“奇怪的<!>”;值和/或访问冲突 醇>
最后,来电者违反了您的前提条件,您可以随意做任何事情。但我认为这些是最合理的选择。
还要考虑一下C <!>#259; t <!>#259; lin说如果合理的话就加入内置的STL集合。
如果您提供对椭圆形状的点的访问,那么您的解决方案会很好。但如果你将它用于任意几何函数,它将导致非常讨厌的错误,因为你故意提供错误的值。
模运算符对数组索引的效果非常好 - 它还实现了负索引(即。point[-3] = point[dimensions - 3]
)。这很容易使用,所以我个人推荐模数运算符,只要它有详细记录。
另一种选择是让呼叫者选择越界策略。考虑:
template <class OutOfBoundsPolicy>
quint16 curvePoint::operator[](size_t index)
{
index = OutOfBoundsPolicy(index, dimensions);
return point[index];
}
然后,您可以定义调用者可以选择的多个策略。例如:
struct NoBoundsCheck {
size_t operator()(size_t index, size_t /* max */) {
return index;
}
};
struct WrapAroundIfOutOfBounds {
size_t operator()(size_t index, size_t max) {
return index % max;
}
};
struct AssertIfOutOfBounds {
size_t operator()(size_t index, size_t max) {
assert(index < max);
return index % max;
}
};
struct ThrowIfOutOfBounds {
size_t operator()(size_t index, size_t max) {
if (index >= max) throw std::domain_error;
return index;
}
};
struct ClampIfOutOfBounds {
size_t operator()(size_t index, size_t max) {
if (index >= max) index = max - 1;
return index;
}
};