4

我有一个类似的类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...

我还没有编译这个,所以可能有错别字,但希望你能明白。

4

2 回答 2

3

运算符重载与异常无关,它只是允许通过使用运算符来调用“函数”。

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

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

于 2014-08-22T10:03:19.500 回答
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;

这个例子不会调用重载的赋值运算符!相反,它的意思是“以构造函数的参数构造对象dyn_arr” 。所以这一行实际上调用了. 如果您有兴趣了解原因,请考虑效率。如果该行的语义包括调用赋值运算符,则运行时系统将作为该行的结果做两件事:构造一些默认状态,然后立即通过分配给状态来销毁该状态。相反,只做一件事——调用复制构造——就足够了。(并导致相同的语义,假设复制构造函数和赋值运算符的任何合理实现。)abdyn_arraab

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

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

有关更多详细信息,请参阅如何处理 C++ 中的构造函数失败?

于 2014-08-23T20:23:26.187 回答