C ++运算符重载错误检查,无异常

时间:2014-08-22 09:58:00

标签: c++ embedded operator-overloading safety-critical

我有一个类似于vector的类,它主要是一个动态大小的数组。我正在为资源有限的平台编写它,所以我不需要使用异常。

很明显,要使用运算符重载来简化此类的接口,必须在某些运算符重载函数中执行动态分配。赋值运算符(=)就是一个例子。

虽然没有例外,但是在保持强大的错误安全性的同时以合理的方式通知呼叫者错误的分配错误变得相当具有挑战性。我可以有一个类的错误属性,调用者必须在每次涉及动态分配的调用后检查,但这似乎是一个不太理想的解决方案。

编辑:

这是我目前最好的想法(在上一段中突出显示为不太理想的解决方案),我们将非常感谢任何改进:

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...

我没有编译这个,所以可能会有拼写错误,但希望你能得到这个想法。

2 个答案:

答案 0 :(得分:3)

运算符重载与异常无关,它只是允许&#34;函数&#34;通过使用运营商来调用。

e.g。如果您正在编写自己的向量,则可以实现+以连接两个向量或将单个项添加到向量(作为push_back()的别名)

当然,任何需要分配更多内存的操作都可能耗尽(如果你不能通过设置某种错误状态,你将得到bad_alloc并且必须管理它。)

答案 1 :(得分:3)

这里有两个不同的问题。

第一个与运营商重载有关。正如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;

此示例将 NOT 调用重载的分配运算符!相反,它意味着“构造dyn_arr对象ab作为构造函数的参数”。所以这一行实际上调用了dyn_arr的复制构造函数。如果您有兴趣了解原因,请考虑效率。如果该行的语义包括调用赋值运算符,则运行时系统将执行以下两行操作:使用某个默认状态构造a,然后通过赋值{{1}立即销毁该状态} a的状态。相反,只做一件事 - 调用复制结构 - 就足够了。 (并且假设任何理智的复制构造函数和赋值运算符的实现,导致相同的语义。)

不幸的是,你认识到这个问题很难处理是正确的。除了抛出异常之外,似乎没有一种非常优雅的方法来处理构造函数中的失败。如果你不能这样做,可以:

  • 在构造函数中设置一个标志,并要求/建议用户事后检查它,或
  • 要求指向已分配的内存区域的指针 作为参数传递给构造函数。

有关详细信息,请参阅How to handle failure in constructor in C++?