【问题标题】:How to handle an exception thrown by new in C++?如何处理 C++ 中 new 抛出的异常?
【发布时间】:2015-07-20 10:57:09
【问题描述】:

我有一个带有赋值运算符的类,如下所示。

char *buff;
myString& operator= ( const myString& other )
{
  cout << "  myString::operator=\n";
  if( this != &other ){      
    cout<<"my string ="<<endl;
    delete [] buff;              
    length = other.length;      
    buff = new char[length];
    my_strncpy( buff, other.buff, length );
  }
  return *this;   
}       

我正在删除buff 的内存并分配新字符串的长度。如何处理new 分配内存期间发生的任何异常?如果出现异常,如何将buff 的值恢复为旧值?

【问题讨论】:

  • 这似乎是一个很容易回答的问题,但它真的是您想要的。您可以在删除缓冲区之前将该值存储在一些全局临时变量中。这段代码你可能会得到一个bad_alloc 异常,同样可以使用try catch 块来处理。
  • 首先,您需要确定在内存不足时这是否是合理的做法。一个对象在赋值后默默地保留它的旧值听起来充其量是不确定的。
  • 更简单的解决方案是使用std::vector&lt;char&gt; 来分配内存。

标签: c++ c++11 exception-handling new-operator assignment-operator


【解决方案1】:

对此有两种解决方案。第一个(最好的)是使用复制和交换:

myString& operator= ( myString other ) {
    swap (*this, other);
    return *this;
}

如果在复制构造函数中分配失败,我们将永远无法到达交换位置,因此无需担心覆盖当前状态。更多内容请见What is copy-and-swap?

另一种方法是确保您只在安全的情况下使用delete。也就是在new之后做:

tmp_buff = new char[other.length];
// either that threw, or we're safe to proceed
length = other.length;
my_strncpy(tmp_buff, other.buff, length);
delete [] buff;              
buff = tmp_buff;

【讨论】:

    【解决方案2】:

    处理内存不足的情况很困难,因为通常没有可以使用的简单故障路径。

    在您的情况下,您可以尝试在删除旧缓冲区之前创建新缓冲区,但这可能会增加内存不足 (OOM) 的可能性。

    理想情况下,如果旧缓冲区足够大,您可能应该使用它,并且如果旧缓冲区太小,则只创建一个新缓冲区。在这种情况下,可能不建议在发生 OOM 时使用旧缓冲区,因为它太小而无法存储字符串。

    【讨论】:

      【解决方案3】:

      如何处理new 分配内存期间发生的任何异常?

      应该使用try{} catch(){} 来捕获分配和对象创建期间抛出的异常。

      如果出现异常,如何将buff 的值恢复为旧值?

      这里的关键不是在抛出异常时真正恢复旧值,而是首先分配和复制内容,交换指针等,然后删除旧对象和内存。这样如果抛出异常,当前内容保持不变。

      首先将内存分配给一个临时指针,复制需要的数据,然后用新指针交换旧指针并删除旧数据;类似于copy-swap idiom。 GotW (#59) 在here 上也有一篇不错的文章。

      myString& operator= ( const myString& other )
      {
        if( this != &other ){
          try {      
            char* temp_buff = other.length ? new char[other.length] : nullptr;
            // I assume my_strncpy handle NULL pointers etc.
            // If not, call it behind an if check for length and pointer validity
            my_strncpy( temp_buff, other.buff, length );
            std::swap(temp_buff, buff);
            delete [] temp_buff;              
            length = other.length;
          }
          catch (std::bad_alloc& e) {
            // deal with the bad_alloc...
          }      
          catch (std::exception& e) {
            // deal with the exception
          }      
        }
        return *this;   
      }
      

      一般来说,内存不足的情况很严重,因此仅捕获异常可能并不总是理想的——它需要由整个应用程序来处理,甚至可能由整个系统的用户来处理。要问自己的一个问题是; 你将如何处理异常,你将如何从中恢复?


      更通用的解决方案(我假设您确实专注于以当前形式实现 operator=)是使用完整的复制交换实现。

      class myString {
        char* buff;
        std::size_t length;
        // ...
      };
      
      myString::myString(myString const& src) :
      buff(src.length ? new char[src.length] : nullptr),
      length(src.length)
      {
        if (length)
          std::copy(src.buff, src.buff + length, buff);
      }
      
      myString::~myString()
      {
        delete [] buff;
        length = 0;
      }
      
      void myString::swap(myString& rhs)
      {
        std::swap(rhs.buff, this->buff);
        std::swap(rhs.length, this->length);
      }
      
      myString& myString::operator=(myString const& rhs)
      {
        if (this != &rhs) {
          myString temp(rhs);
          swap(temp);
        }
        return *this;
      }
      
      // the rest of the class implementation
      
      //... non-member swap for addition utility
      inline void swap(myString& lhs, myString& rhs)
      {
        lhs.swap(rhs);
      }
      

      【讨论】:

      • 代码 std::swap(temp_buff, buff); 是什么意思删除 [] temp_buff;
      • @sasikala。 std::swap 将交换指针的值(不是指向的值),所以旧的现在代替新的,反之亦然。 delete[] 然后删除旧数据。
      • 查看我的评论 myString& operator= ( const myString& other ) { if( this != &other ){ try { delete []buff;长度=其他。长度; //删除buff内存后,再次为buff分配内存时出现异常 try { buff = new char[other.length];//如果这里出现异常,超出此点的语句将不会被执行。 //在这种情况下,我们如何将旧值放入 buff. } catch (std::bad_alloc& e) { // 处理 bad_alloc... } catch (std::exception& e) { // 处理异常 } }
      • @sasikala。分配缓冲区时遇到什么异常?
      • 我们可以说 bad_alloc 异常。当它发生时,我们无法执行 buff = new char[other.length] 之后的语句。我对吗?那么有什么方法可以恢复这些值。
      猜你喜欢
      • 1970-01-01
      • 2021-12-22
      • 2013-03-05
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      相关资源
      最近更新 更多