首先,我对很长的达这样一个简单的问题道歉。
我在执行其用作空间填充曲线或表示笛卡尔坐标该索引对应于所述n元组上的非常长的1名维的索引的类。
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 ];
}
这就让我们永远不会离开数组的边界,如果有据可查的,我认为这将是罚款; 不过,我是空的这个特别的实施。
这看起来可以接受别人呢? 是否有这样做边界检查,同时仍然满足我的限制任何其他方式?
编辑:之类的东西希尔伯特曲线等计算是非常混乱的,凌乱的,以至于我不不想在这样的STL库的附加接口。
此外,因为我将不得不转换成千上万的这些多维数据库中查询我不想混了STL函数调用的额外费用每一次,如果可能的话。
我比较喜欢断言的想法; 但是,如果我没有记错,在发行休息建立不是吗?
我想我可以使用的例外,这似乎是大家都在全力支持,但我使用Qt库和避免那些出于性能和便携性的例外,我希望这样做。
Answer 1:
最简单的办法是做,因为C ++本身一样。 这限制了你的用户将体验到惊喜的量。
C ++本身是相当一致的。 无论是内置的[]
上的指针和std::vector::operator[]
如果您使用绑定外的数组索引具有不确定的行为。 如果你想要的边界检查,是明确的和使用std::vector::at
因此,如果你为你的类做同样的,你可以记录外的约束行为,“标准”。
Answer 2:
反正在操作执行[]我想知道什么是最好的方法来实现的边界检查。 我想避免如果在所有可能抛出异常,并且值的完整范围为阵列中的每个号码可用这样一个特殊的值,以在一个出界错误的情况下返回是不可能的任一;
然后,剩下的选项:
- 灵活的设计。 你做了什么。 “修复”无效的输入,因此,它试图做一些有意义。 优点:功能不会崩溃。 缺点:谁访问越界元素的懵懵懂懂呼叫者会得到一个谎言结果。 想象一下,在1层至10 10楼的建设:
你:“谁住在3楼?”
我说:“玛丽”。
你:“谁住在9楼?”
我说:“乔”。
你:“家住1,203rd楼?”
我:(等待... 1203%10 = 3 ...)> “玛丽”。
你:“哇,玛丽必须享受美景那里于是,她拥有两个公寓,然后?”
甲布尔输出参数指示成功或失败。 此选项通常不是十分有用的代码结束。 很多用户会忽略返回代码。 你还是留下你在其他的返回值返回的内容。
契约式设计。 断言呼叫者范围内。 (对于C ++一个切实可行的办法,见异常或错误?米罗萨梅克或由佩德罗·格雷罗在C ++合同设计简洁支持 。)
返回System.Nullable<quint16>
哎呀,等一下,这不是C#。 嗯,你可以返回一个指向一个quint16。 这当然有很多含义,我将不在此讨论并可能使该选项不可用的。
我最喜欢的选择是:
- 对于公开发行库的公共接口:输入将被选中并且会抛出异常。 你排除了此选项,所以它不是一个选择。 它仍然是我的一个公开发布库的接口选择。
- 对于内部代码:契约式设计。
Answer 3:
对我来说,这个方案是不可接受的,因为你可以躲在一个很难找到的bug。 抛出越界异常是去,或者至少把一个断言的功能的方式。
Answer 4:
如果你需要的是某种点的“通知”阵列,那么您的解决方案是确定的。 但是,对我来说,它看起来就像躲在一些“安全”的逻辑索引运算符的missusage,这样我就能对你提出的解决方案。
如果不希望允许指数溢出,那么你可以检查并抛出一个异常。
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 ::矢量<quint16>。 它已经有一个基于索引的访问,你可以使用:
quint16 curvePoint::operator[](size_t index)
{
// points is declared as std::vector< quint16> points;
return points[ index ];
}
Answer 5:
有运营商[],从来没有失败听起来不错,但以后可能会在隐藏的错误,如果调用函数使用非法偏移,发现从缓冲区的开始值,然后转到仿佛就是一个有效的值。
Answer 6:
实现边界检查的最好的方法是添加一个断言。
quint16 curvePoint::operator[](size_t index)
{
assert(index < dimensions);
return point[index];
}
如果你的代码已经依赖于Boost库,您可能需要使用BOOST_ASSERT
代替。
Answer 7:
如果我是你,我会效法由STL设置。
在这种情况下std::vector
用品两种方法: at
其界限检查和operator[]
这是不。 这允许客户端与版本使用的决定。 我绝对不会使用% size()
因为这只是隐藏的bug。 然而边界检查将增加大量的开销过大集合迭代时,这就是为什么它应该是可选的。 虽然我与其他海报同意,断言是一个非常好的主意,因为这只会导致性能损失在调试版本。
你也应该考虑返回引用和供应常量,而不是常量的版本。 下面是函数声明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我试图判断或评价它,找到我喜欢的比特和厌恶。
Answer 8:
由于C#的功能在丹尼尔Daranas的职位,我设法找出一个可行的解决方案的评论。 正如我在我的问题说我使用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不需要在被链接。
Answer 9:
也许你可以添加一个例外“出界”的[]操作符(或至少一个assert)。
这应该抓住任何问题,特别是在调试。
Answer 10:
除非我大大误解的东西,
return point[ index % dimensions ];
没有边界检查的。 它是从线的完全不同的部分,这将使它更难被发现的bug返回一个真正的价值。
我要么:
- 抛出一个异常或断言(虽然你说你不想这样做)
- 简单地提领点过去“自然”的方式排列(即只跳过任何内部检查)。 在%的好处是,他们更可能(虽然不确定是未定义)获得“怪异”的价值观和/或访问冲突
最后,调用者侵犯了您的前置条件,你可以做任何你请。 但我认为这些是最合理的选择。
还考虑什么克特林说如何整合内置的STL集合如果这是合理的。
Answer 11:
如果你提供进入一个椭圆形的点您的解决方案将是很好。 但它会导致非常讨厌的错误,如果你使用它的任意几何函数,因为你故意提供虚假的价值。
Answer 12:
模运算符工作得非常好为数组索引-它也实现负索引(即, point[-3] = point[dimensions - 3]
这是很容易的工作,所以我个人推荐的模运算只要它充分证明。
Answer 13:
另一种选择是让来电者选择了界外政策。 考虑:
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;
}
};
文章来源: When implementing operator[] how should I include bounds checking?