我有一个类似于 vector
的类,它主要是一个动态大小的数组。我正在为资源有限的平台编写它,因此我必须不使用异常。
很明显,要使用运算符重载来简化此类动态分配的接口(interface),必须在某些运算符重载函数中执行。赋值运算符 (=) 就是一个例子。
尽管无一异常(exception),以一种明智的方式将错误的分配错误通知调用者,同时仍然保持强大的错误安全性变得相当具有挑战性。我可以有一个类的错误属性,调用者必须在每次涉及动态分配的调用后检查它,但这似乎不是一个最佳解决方案。
编辑:
这是我目前得到的最好的想法(在上一段中被突出显示为一个不太理想的解决方案),任何改进将不胜感激:
dyn_arr & dyn_arr::operator=(dyn_arr const & rhs) {
if (reallocate(rhs.length)) // this does not destroy data on bad alloc
error |= bad_alloc; // set flag indicating the allocate has failed
else {
size_t i;
for (i = 0; i < rhs.length; ++i) // coppy the array
arr[i] = rhs.arr[i]; // assume this wont throw an exceptions and it wont fail
}
return *this;
}
然后调用:
dyn_arr a = b;
if (a.error)
// handle it...
我还没有编译这个,所以可能会有拼写错误,但希望你明白了。
最佳答案
这里有两个不同的问题。
第一个与运算符重载有关。正如 CashCow 所提到的,C++ 中的重载运算符只是函数调用的语法糖。特别是,运算符不需要 return *this
.这只是一种编程约定,旨在促进运算符链接。
现在,链接赋值 运算符 ( a = b = c = ...
) 是 C++ 应用程序中的一个极端情况。因此,明确禁止 dyn_arr
的用户可能会更好。永远链赋值运算符的类。这将使您可以自由地从运算符返回错误代码,就像从常规函数中一样:
error_t operator = (dyn_arr const & rhs) {
void *mem = realloc(...);
if (mem == NULL) {
return ERR_BAD_ALLOC; // memory allocation failed
}
...
return ERR_SUCCESS; // all ok
}
然后在调用者代码中:
dyn_arr a, b;
if ((a = b) != ERR_SUCCESS) {
// handle error
}
第二个问题与您给出的实际示例有关:
dyn_arr a = b;
这个例子将不调用重载的赋值运算符!相反,它意味着“构造 dyn_arr
对象 a
并将 b
作为构造函数的参数”。所以这一行实际上调用了dyn_arr
的拷贝构造函数.如果您有兴趣了解原因,请从效率的角度考虑。如果该行的语义包括调用赋值运算符,运行时系统将作为该行的结果做两件事:构造a
。具有一些默认状态,然后通过分配给 a
立即销毁该状态b
的状态| .相反,只做一件事——调用复制构造——就足够了。 (并导致相同的语义,假设复制构造函数和赋值运算符的任何合理实现。)
不幸的是,您认识到这个问题很难处理是正确的。除了抛出异常之外,似乎没有真正优雅的方法来处理构造函数中的失败。如果你不能这样做,要么:
- 在构造函数中设置一个标志并要求/建议用户事后检查它,或者
- 要求指向已分配内存区域的指针是 作为参数传递给构造函数。
有关详细信息,请参阅 How to handle failure in constructor in C++?
关于C++ 运算符重载错误检查无异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25444357/