如何处理C ++中new引发的异常?

时间:2015-07-20 10:57:10

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

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

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的值恢复为旧值(例外)?

3 个答案:

答案 0 :(得分:2)

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

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;

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

  

如何使用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);
}