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

📄 ec2.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 4 页
字号:
  ...                                   // otherwise handle                                        // the request here}"Hold on!" I hear you cry, "You forgot to check for the pathological-but-nevertheless-possible case where size is zero!" Actually, I didn't, and please stop using hyphens when you cry out. The test is still there, it's just been incorporated into the test of size against sizeof(Base). The C++ standard works in mysterious ways, and one of those ways is to decree that all freestanding classes have nonzero size. By definition, sizeof(Base) can never be zero (even if it has no members), so if size is zero, the request will be forwarded to ::operator new, and it will become that function's responsibility to treat the request in a reasonable fashion. (Interestingly, sizeof(Base) may be zero if Base is not a freestanding class. For details, consult my article on counting objects.)If you'd like to control memory allocation for arrays on a per-class basis, you need to implement operator new's array-specific cousin, operator new[]. (This function is usually called "array new," because it's hard to figure out how to pronounce "operator new[]".) If you decide to write operator new[], remember that all you're doing is allocating raw memory you can't do anything to the as-yet-nonexistent objects in the array. In fact, you can't even figure out how many objects will be in the array, because you don't know how big each object is. After all, a base class's operator new[] might, through inheritance, be called to allocate memory for an array of derived class objects, and derived class objects are usually bigger than base class objects. Hence, you can't assume inside Base::operator new[] that the size of each object going into the array is sizeof(Base), and that means you can't assume that the number of objects in the array is (bytes requested)/sizeof(Base). For more information on operator new[], see Item M8.So much for the conventions you need to follow when writing operator new (and operator new[]). For operator delete (and its array counterpart, operator delete[]), things are simpler. About all you need to remember is that C++ guarantees it's always safe to delete the null pointer, so you need to honor that guarantee. Here's pseudocode for a non-member operator delete: void operator delete(void *rawMemory){  if (rawMemory == 0) return;    // do nothing if the null                                 // pointer is being deleted  deallocate the memory pointed to by rawMemory;  return;}The member version of this function is simple, too, except you've got to be sure to check the size of what's being deleted. Assuming your class-specific operator new forwards requests of the "wrong" size to ::operator new, you've got to forward "wrongly sized" deletion requests to ::operator delete: class Base {                       // same as before, but nowpublic:                            // op. delete is declared  static void * operator new(size_t size);  static void operator delete(void *rawMemory, size_t size);  ...};void Base::operator delete(void *rawMemory, size_t size){  if (rawMemory == 0) return;      // check for null pointer  if (size != sizeof(Base)) {      // if size is "wrong,"    ::operator delete(rawMemory);  // have standard operator    return;                        // delete handle the request  }  deallocate the memory pointed to by rawMemory;  return;}The conventions, then, for operator new and operator delete (and their array counterparts) are not particularly onerous, but it is important that you obey them. If your allocation routines support new-handler functions and correctly deal with zero-sized requests, you're all but finished, and if your deallocation routines cope with null pointers, there's little more to do. Add support for inheritance in member versions of the functions, and presto! you're done. Back to Item 8: Adhere to convention when writing operator new and operator delete.Continue to Item 10: Write operator delete if you write operator new.Item 9: Avoid hiding the "normal" form of new.A declaration of a name in an inner scope hides the same name in outer scopes, so for a function f at both global and class scope, the member function will hide the global function: void f();                             // global functionclass X {public:  void f();                           // member function};X x;f();                                  // calls global fx.f();                                // calls X::fThis is unsurprising and normally causes no confusion, because global and member functions are usually invoked using different syntactic forms. However, if you add to this class an operator new taking additional parameters, the result is likely to be an eye-opener: class X {public:  void f();  // operator new allowing specification of a  // new-handling function  static void * operator new(size_t size, new_handler p);};void specialErrorHandler();          // definition is elsewhereX *px1 =  new (specialErrorHandler) X;       // calls X::operator newX *px2 = new X;                      // error!By declaring a function called "operator new" inside the class, you inadvertently block access to the "normal" form of new. Why this is so is discussed in Item 50. Here we're more interested in figuring out how to avoid the problem.One solution is to write a class-specific operator new that supports the "normal" invocation form. If it does the same thing as the global version, that can be efficiently and elegantly encapsulated as an inline function: class X {public:  void f();  static void * operator new(size_t size, new_handler p);  static void * operator new(size_t size)  { return ::operator new(size); }};X *px1 =  new (specialErrorHandler) X;      // calls X::operator                                    // new(size_t, new_handler)X* px2 = new X;                     // calls X::operator                                    // new(size_t)An alternative is to provide a default parameter value (see Item 24) for each additional parameter you add to operator new: class X {public:  void f();  static    void * operator new(size_t size,                // note default                        new_handler p = 0);         // value for p};X *px1 = new (specialErrorHandler) X;               // fineX* px2 = new X;                                     // also fineEither way, if you later decide to customize the behavior of the "normal" form of new, all you need to do is rewrite the function; callers will get the customized behavior automatically when they relink. Back to Item 9: Avoid hiding the "normal" form of new.Continue to Constructors, Destructors, and Assignment OperatorsItem 10: Write operator delete if you write operator new.Let's step back for a moment and return to fundamentals. Why would anybody want to write their own version of operator new or operator delete in the first place?More often than not, the answer is efficiency. The default versions of operator new and operator delete are perfectly adequate for general-purpose use, but their flexibility inevitably leaves room for improvements in their performance in a more circumscribed context. This is especially true for applications that dynamically allocate a large number of small objects.As an example, consider a class for representing airplanes, where the Airplane class contains only a pointer to the actual representation for airplane objects (a technique discussed in Item 34): class AirplaneRep { ... };      // representation for an                                // Airplane objectclass Airplane {public:  ...private:  AirplaneRep *rep;             // pointer to representation};An Airplane object is not very big; it contains but a single pointer. (As explained in Items 14 and M24, it may implicitly contain a second pointer if the Airplane class declares virtual functions.) When you allocate an Airplane object by calling operator new, however, you probably get back more memory than is needed to store this pointer (or pair of pointers). The reason for this seemingly wayward behavior has to do with the need for operator new and operator delete to communicate with one another.Because the default version of operator new is a general-purpose allocator, it must be prepared to allocate blocks of any size. Similarly, the default version of operator delete must be prepared to deallocate blocks of whatever size operator new allocated. For operator delete to know how much memory to deallocate, it must have some way of knowing how much memory operator new allocated in the first place. A common way for operator new to tell operator delete how much memory it allocated is by prepending to the memory it returns some additional data that specifies the size of the allocated block. That is, when you say this, Airplane *pa = new Airplane;you don't necessarily get back a block of memory that looks like this:Instead, you often get back a block of memory that looks more like this:For small objects like those of class Airplane, this additional bookkeeping data can more than double the amount of memory needed for each dynamically allocated object (especially if the class contains no virtual functions).If you're developing software for an environment in which memory is precious, you may not be able to afford this kind of spendthrift allocation. By writing your own operator new for the Airplane class, you can take advantage of the fact that all Airplane objects are the same size, so there isn't any need for bookkeeping information to be kept with each allocated block.One way to implement your class-specific operator new is to ask the default operator new for big blocks of raw memory, each block of sufficient size to hold a large number of Airplane objects. The memory chunks for Airplane objects themselves will be taken from these big blocks. Currently unused chunks will be organized into a linked list the free list of chunks that are available for future Airplane use. This may make it sound like you'll have to pay for the overhead of a next field in every object (to support the list), but you won't: the space for the rep field (which is necessary only for memory chunks in use as Airplane objects) will also serve as the place to store the next pointer (because that pointer is needed only for chunks of memory not in use as Airplane objects). You'll arrange for this job-sharing in the usual fashion: you'll use a union.To turn this design into reality, you have to modify the definition of Airplane to support custom memory management. You do it as follows: class Airplane {           // modified class  now supportspublic:                    // custom memory management  static void * operator new(size_t size);  ...private:  union {    AirplaneRep *rep;      // for objects in use    Airplane *next;        // for objects on free list  };  // this class-specific constant (see Item 1) specifies how  // many Airplane objects fit into a big memory block;  // it's initialized below  static const int BLOCK_SIZE;  static Airplane *headOfFreeList;};Here you've added the declarations for operator new, the union that allows the rep and next fields to occupy the same memory, a class-specific constant for specifying how big each allocated block should be, and a static pointer to keep track of the head of the free list. It's important to use a static member for this last task, because there's one free list for the entire class, not one free list for each Airplane object.The next thing to do is to write the new operator new: void * Airplane::operator new(size_t size){  // send requests of the "wrong" size to ::operator new();  // for details, see Item 8  if (size != sizeof(Airplane))    return ::operator new(size);  Airplane *p =           // p is now a pointer to the    headOfFreeList;       // head of the free list

⌨️ 快捷键说明

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