简体   繁体   中英

How to handle an exception thrown by new in C++?

I have a class with assignment operator as below.

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;   
}       

I am deleting memory for buff and allocating with length of new string. How can I handle any exception that happens during the allocation memory with new ? How can I restore the value of buff to old values incase of exception?

There are two solutions to this. The first (best) is to use copy-and-swap:

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

If allocation fails in the copy constructor, we'd never get to the swap, so there's no worry about overwriting our current state. For more, see What is copy-and-swap?

The other approach is to just make sure you only delete if it's safe. That is, do it after the 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;

Dealing with out-of-memory conditions is hard since there often is no easy failure path that can be used.

In your case, you could try creating the new buffer before deleting the old buffer but this may increase the likelyhood of running out of memory (OOM).

Ideally you should probably use the old buffer if it is big enough and only create a new buffer if the old one is too small. In such a case it is probably ill advised to use the old buffer in the event of OOM since it will be too small to store the string.

How can I handle any exception that happens during the allocation memory with new ?

A try{} catch(){} should be used to catch exceptions thrown during allocation and object creation.

How can I restore the value of buff to old values incase of exception?

The key here is not really to restore the old values if an exception is thrown, but to first allocate and copy the contents, swapping pointers etc. and then delete the old object and memory. In this way if an exception is thrown, the current contents are left unchanged.

First allocate the memory to a temporary pointer, copy the data required, then swap the new pointer for the old one and delete the old data; akin to the copy-swap idiom . GotW (#59) also has a nice article on this 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;   
}

In general, out of memory conditions are significant, so just catching the exception may not always be ideal - it needs to be dealt with by the application as a whole, possibly even by the user for the entire system. A question to ask yourself is; what are you going to do with the exception, how are you going to recover from it?


A more general solution (I assume you really are focused on implementing the operator= in the current form) is to use a full blown copy-swap implementation.

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM