首先,我为这么简单的问题长期致以道歉。
我正在实现一个类,它在空间填充曲线上作为非常长的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;
};
( point 指向的数组长度为维度)
无论如何在operator []的实现中,我想知道实现边界检查的最佳方法是什么。我想尽可能避免抛出异常,并且数组中的每个数字都可以使用全范围的值,因此在出现越界错误时也不能返回特殊值;
虽然在类定义中实现了,但我正在考虑这样的事情:
quint16 curvePoint::operator[](size_t index)
{
return point[ index % dimensions ];
}
这使得我们永远不会离开数组的界限,如果有详细记录,我认为它会没事;尽管如此,我对这个特定的实施方式很有兴趣。
这对其他人来说是否可以接受? 在满足我的约束条件的同时还有其他方法来进行边界检查吗?
编辑: Hilbert曲线等事物的计算非常混乱,非常混乱,我不希望stl库的附加接口在路上。
另外,因为每次查询多维数据库时我都必须转换成千上万的这些数据,所以如果可能的话,我不希望混合中的stl函数调用的额外成本。
我更喜欢断言的想法;但是,如果我没记错的话,发布版本中断了吗?
我想我可以使用异常,这似乎是每个人都支持的异常,但我使用的是Qt库,避免了性能和可移植性的异常,我希望也能这样做。
答案 0 :(得分:12)
最简单的解决方案就是像C ++本身那样做。这会限制用户体验的惊喜数量。
C ++本身相当一致。如果使用超出范围的数组索引,则内置的[]
指针和std::vector::operator[]
都有未定义的行为。如果您想要边界检查,请明确并使用std::vector::at
因此,如果您为您的班级做同样的事情,您可以将越界行为记录为“标准”。
答案 1 :(得分:9)
无论如何执行 operator []我想知道是什么 实现边界检查的最佳方法 是。我想避免投掷 如果可能的例外,和 可以使用全范围的值 数组中的每个数字都是特殊的 退出的价值 边界错误也是不可能的;
然后剩下的选项是:
你:“谁住在三楼?”
我:“玛丽”。
你:“谁住在9楼?”
我:“乔”。
你:“谁住在1,203楼?”
我:(等等...... 1,203%10 = 3 ...) > “玛丽”
你:“哇,玛丽必须从up there欣赏美景。那么她拥有两套公寓呢?”
bool输出参数表示成功或失败。此选项通常以不太可用的代码结束。许多用户将忽略返回码。你仍然留下你在其他回报值中返回的内容。
按合同设计。断言调用方在边界内。 (有关C ++中的实用方法,请参阅An exception or a bug? by Miro Samek或Simple Support for Design by Contract in C++ by Pedro Guerreiro。)
返回System.Nullable<quint16>
。糟糕,等等,这不是C#。好吧,你可以返回一个指向quint16的指针。这当然有很多含义,我不会在这里讨论,这可能使这个选项无法使用。
我最喜欢的选择是:
答案 2 :(得分:7)
对我来说,这个解决方案是不可接受的,因为你可以隐藏一个很难找到的bug。 抛出超出范围的异常是要走的路,或者至少在函数中放置一个断言。
答案 3 :(得分:3)
如果您需要的是某种“循环”点数,那么您的解决方案就可以了。但是,对我而言,它看起来就像隐藏了一些“安全”逻辑背后的索引操作符的错误,所以我反对你提出的解决方案。
如果不想允许索引溢出,那么你可以检查并抛出异常。
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 ];
}
答案 4 :(得分:2)
让一个永不失败的运算符[]听起来不错,但是稍后可能会隐藏错误,如果调用函数使用非法偏移,则从缓冲区的开头找到一个值,并且好像这是一个有效值。
答案 5 :(得分:1)
实现边界检查的最佳方法是添加断言。
quint16 curvePoint::operator[](size_t index)
{
assert(index < dimensions);
return point[index];
}
如果您的代码已经依赖于Boost库,则可能需要使用BOOST_ASSERT
。
答案 6 :(得分:1)
感谢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不需要链接。
答案 7 :(得分:1)
如果我是你,我会按照stl。
设置的例子在这种情况下,std::vector
提供了两种方法:at
边界检查,operator[]
不是。% size()
。这允许客户端决定使用版本。我肯定不会使用reference at(size_type _Pos);
const_reference at(size_type _Pos) const;
reference operator[](size_type _Pos);
const_reference operator[](size_type _Pos) const;
,因为这只是隐藏了错误。但是,在迭代大型集合时,边界检查会增加很多开销,这就是为什么它应该是可选的。虽然我同意其他海报认为断言是一个非常好的主意,因为这只会导致调试版本中的性能损失。
您还应该考虑返回引用并提供const而不是const版本。以下是std::vector
的函数声明:
{{1}}
作为一个很好的经验法则,如果我不确定如何指定API,我会查找其他人如何指定类似API的示例。此外,当我使用API时,我会尝试判断或评价它,找到我喜欢和不喜欢的位。
答案 8 :(得分:0)
您可以在[]运算符(或至少是一个断言)中添加“越界”异常。
这应该可以解决任何问题,特别是在调试时。
答案 9 :(得分:0)
除非我对某些事情产生了极大的误解,否则
return point[ index % dimensions ];
根本没有检查边界。它从行的完全不同的部分返回一个真正的值,这将使得检测错误变得更加困难。
我要么:
最后,来电者违反了您的前提条件,您可以随意做任何事情。但我认为这些是最合理的选择。
如果合理的话,还要考虑Cătălin关于合并内置STL集合的内容。
答案 10 :(得分:0)
如果您提供对椭圆形状的点的访问,那么您的解决方案会很好。但如果你将它用于任意几何函数,它将导致非常讨厌的错误,因为你故意提供错误的值。
答案 11 :(得分:0)
模运算符对数组索引的效果非常好 - 它还实现了负索引(即point[-3] = point[dimensions - 3]
)。这很容易使用,所以我个人推荐模数运算符,只要它有详细记录。