⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ec2.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 4 页
字号:
  std::set_new_handler(globalHandler);       // restore                                             // handler  return memory;}If the duplicated calls to std::set_new_handler caught your eye, turn to Item M9 for information on how to eliminate them.Clients of class X use its new-handling capabilities like this: void noMoreMemory();                           // decl. of function to                                               // call if memory allocation                                               // for X objects failsX::set_new_handler(noMoreMemory);                                               // set noMoreMemory as X's                                               // new-handling functionX *px1 = new X;                                // if memory allocation                                               // fails, call noMoreMemorystring *ps = new string;                       // if memory allocation                                               // fails, call the global                                               // new-handling function                                               // (if there is one)X::set_new_handler(0);                         // set the X-specific                                               // new-handling function                                               // to nothing (i.e., null)X *px2 = new X;                                // if memory allocation                                               // fails, throw an exception                                               // immediately. (There is                                               // no new-handling function                                               // for class X.)You may note that the code for implementing this scheme is the same regardless of the class, so a reasonable inclination would be to reuse it in other places. As Item 41 explains, both inheritance and templates can be used to create reusable code. However, in this case, it's a combination of the two that gives you what you need.All you have to do is create a "mixin-style" base class, i.e., a base class that's designed to allow derived classes to inherit a single specific capability in this case, the ability to set a class-specific new-handler. Then you turn the base class into a template. The base class part of the design lets derived classes inherit the set_new_handler and operator new functions they all need, while the template part of the design ensures that each inheriting class gets a different currentHandler data member. The result may sound a little complicated, but you'll find that the code looks reassuringly familiar. In fact, about the only real difference is that it's now reusable by any class that wants it: template<class T>				// "mixin-style" base classclass NewHandlerSupport {			// for class-specificpublic:						// set_new_handler support  static new_handler set_new_handler(new_handler p);  static void * operator new(size_t size);private:  static new_handler currentHandler;};template<class T>new_handler NewHandlerSupport<T>::set_new_handler(new_handler p){  new_handler oldHandler = currentHandler;  currentHandler = p;  return oldHandler;}template<class T>void * NewHandlerSupport<T>::operator new(size_t size){  new_handler globalHandler =    std::set_new_handler(currentHandler);  void *memory;  try {    memory = ::operator new(size);  }  catch (std::bad_alloc&) {    std::set_new_handler(globalHandler);    throw;  }  std::set_new_handler(globalHandler);  return memory;}// this sets each currentHandler to 0template<class T>new_handler NewHandlerSupport<T>::currentHandler;With this class template, adding set_new_handler support to class X is easy: X just inherits from newHandlerSupport<X>: // note inheritance from mixin base class template. (See// my article on counting objects for information on why// private inheritance might be preferable here.)class X: public NewHandlerSupport<X> {  ...                 // as before, but no declarations for};                    // set_new_handler or operator newClients of X remain oblivious to all the behind-the-scenes action; their old code continues to work. This is good, because one thing you can usually rely on your clients being is oblivious.Using set_new_handler is a convenient, easy way to cope with the possibility of out-of-memory conditions. Certainly it's a lot more attractive than wrapping every use of new inside a try block. Furthermore, templates like NewHandlerSupport make it simple to add a class-specific new-handler to any class that wants one. Mixin-style inheritance, however, invariably leads to the topic of multiple inheritance, and before starting down that slippery slope, you'll definitely want to read Item 43.Until 1993, C++ required that operator new return 0 when it was unable to satisfy a memory request. The current behavior is for operator new to throw a std::bad_alloc exception, but a lot of C++ was written before compilers began supporting the revised specification. The C++ standardization committee didn't want to abandon the established test-for-0 code base, so they provided alternative forms of operator new (and operator new[] see Item 8) that continue to offer the traditional failure-yields-0 behavior. These forms are called "nothrow" forms because, well, they never do a throw, and they employ nothrow objects (defined in the standard header <new>) at the point where new is used: class Widget { ... };Widget *pw1 = new Widget;      // throws std::bad_alloc if                               // allocation failsif (pw1 == 0) ...              // this test must failWidget *pw2 =  new (nothrow) Widget;        // returns 0 if allocation                               // failsif (pw2 == 0) ...              // this test may succeedRegardless of whether you use "normal" (i.e., exception-throwing) new or "nothrow" new, it's important that you be prepared to handle memory allocation failures. The easiest way to do that is to take advantage of set_new_handler, because it works with both forms. Back to Item 7: Be prepared for out-of-memory conditions.Continue to Item 9: Avoid hiding the "normal" form of new.Item 8: Adhere to convention when writing operator new and operator delete.When you take it upon yourself to write operator new (Item 10 explains why you might want to), it's important that your function(s) offer behavior that is consistent with the default operator new. In practical terms, this means having the right return value, calling an error-handling function when insufficient memory is available (see Item 7), and being prepared to cope with requests for no memory. You also need to avoid inadvertently hiding the "normal" form of new, but that's a topic for Item 9.The return value part is easy. If you can supply the requested memory, you just return a pointer to it. If you can't, you follow the rule described in Item 7 and throw an exception of type std::bad_alloc.It's not quite that simple, however, because operator new actually tries to allocate memory more than once, calling the error-handling function after each failure, the assumption being that the error-handling function might be able to do something to free up some memory. Only when the pointer to the error-handling function is null does operator new throw an exception.In addition, the C++ standard requires that operator new return a legitimate pointer even when 0 bytes are requested. (Believe it or not, requiring this odd-sounding behavior actually simplifies things elsewhere in the language.)That being the case, pseudocode for a non-member operator new looks like this: void * operator new(size_t size)        // your operator new might{                                       // take additional params  if (size == 0) {                      // handle 0-byte requests    size = 1;                           // by treating them as  }                                     // 1-byte requests  while (1) {    attempt to allocate size bytes;    if (the allocation was successful)      return (a pointer to the memory);    // allocation was unsuccessful; find out what the    // current error-handling function is (see Item 7)    new_handler globalHandler = set_new_handler(0);    set_new_handler(globalHandler);    if (globalHandler) (*globalHandler)();    elsethrow std::bad_alloc();  }}The trick of treating requests for zero bytes as if they were really requests for one byte looks slimy, but it's simple, it's legal, it works, and how often do you expect to be asked for zero bytes, anyway?You may also look askance at the place in the pseudocode where the error-handling function pointer is set to null, then promptly reset to what it was originally. Unfortunately, there is no way to get at the error-handling function pointer directly, so you have to call set_new_handler to find out what it is. Crude, yes, but also effective.Item 7 remarks that operator new contains an infinite loop, and the code above shows that loop explicitly while (1) is about as infinite as it gets. The only way out of the loop is for memory to be successfully allocated or for the new-handling function to do one of the things described in Item 7: make more memory available, install a different new-handler, deinstall the new-handler, throw an exception of or derived from std::bad_alloc, or fail to return. It should now be clear why the new-handler must do one of those things. If it doesn't, the loop inside operator new will never terminate.One of the things many people don't realize about operator new is that it's inherited by subclasses. That can lead to some interesting complications. In the pseudocode for operator new above, notice that the function tries to allocate size bytes (unless size is 0). That makes perfect sense, because that's the argument that was passed to the function. However, most class-specific versions of operator new (including the one you'll find in Item 10) are designed for a specific class, not for a class or any of its subclasses. That is, given an operator new for a class X, the behavior of that function is almost always carefully tuned for objects of size sizeof(X) nothing larger and nothing smaller. Because of inheritance, however, it is possible that the operator new in a base class will be called to allocate memory for an object of a derived class: class Base {public:  static void * operator new(size_t size);  ...};class Derived: public Base       // Derived doesn't declare{ ... };                         // operator newDerived *p = new Derived;        // callsBase::operator new!If Base's class-specific operator new wasn't designed to cope with this and chances are slim that it was the best way for it to handle the situation is to slough off calls requesting the "wrong" amount of memory to the standard operator new, like this: void * Base::operator new(size_t size){  if (size != sizeof(Base))             // if size is "wrong,"    return ::operator new(size);        // have standard operator                                        // new handlethe request

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -