Wide
This is the last part of the type of cache-lightweight and flexible continuous arbitrary type object. It is positioned between basic within and complex std :: vectors, when efficient is important. Types of cache is a very useful structure. More importantly, it can create more complex structures as a convenient basic component - such as String, Vector, Queue, and others. The previous part [1] focuses on the basic implementation of the basic implementation of Buffer operation, such as filling and copying memory. This article you have to read has a wider perspective - we want to discuss copy and mobile objects, rather than raw data.
Low-energy dispenser (allocator) When I want to use discussions based on strategy-based design [2], I often laugh at the STL dispenser is a famous experiment, but it is a tragic failure experiment. . Just as Freud pointed out that middle-aged anxiety is related to the childhood of the childhood, the interesting story of C memory allocation needs to be traced back to C. From the beginning, C provides a functional function of three memory management: Malloc, Free and Realloc
Void * malloc (size_t size); void free (void * p); void * realloc (void * p, size_t newsize);
I may be a magical number, but the three memory management functions are far less enough. If you take into account the annoying repetition --Realloc can be a top three - the situation is the case:
* If you pass the empty pointer, Realloc works in Malloc * If you pass a zero size, Realloc works free.
So, Realloc is a full-power function, which can handle all the needs of all memory management. (Shun, this is a disadvantage) Let's look at a typical Realloc implementation:
Void * Realloc (void * p, size_t news {// exclude extreme case if (p == 0) return malloc (newsize); if (news == 0) Return Free (p), 0;
// Try to expand IF on site (p == _expand (p, newsize) && _msize (p)> = news) {Return P;}
// Need to move memory - get the new memory block Void * pnew = malloc (news); if (! PNEW) return 0;
// Move data to new position Memcpy (PNEW, P, std :: min (_msize (p), newsing); free (p); return pnew;}
I think the realoc implementation above is easy to read, especially because it does not follow See (single entrance, single export Single Entry, Single EXIT) rules [3]. But some details are not very clear - what is the _ Expand and _msize? These two functions are Tools for Realloc:
Void * expand (void * p, sizet news; size_t _ms);
_expand attempts to expand the memory block that is pointed to by its first parameter. It is possible that when you want to redistribute a memory, it is likely to be available. In this case, the memory distributor can quickly adjust its recording memory used to reflect the new memory. This is much faster than copying the entire block to a bigger location. When successful, _expand returns the pointer to it. _MSIZE only returns the size of the memory block pointing to it. (All memory manager must know the size of each memory block) _expand and _msize are non-standard, so you can only use them through Realloc. Go back to Realloc, if the expansion failed on site, Realloc assigns a whole new memory block. Use Memcpy to copy the old memory block to the new memory block, free out of the old memory block. very simple. Maybe too simple. When C is born, it relies very dependent on C library and basic functional functions. In particular, if C develops its own, a separate memory distributor is very difficult - establishing a new, strong type allocation mechanism is more meaningful on the standard C distributor. And this is the problem. Although Memcpy is so loyal to C, it is C good but clumsy servant. For C, moving things with Memcpy works very well, but C needs more. The object with constructor and the destructuring function cannot be moved in memory. They may be packaged in data that points to themselves or compiler (such as pointers that point to base classes when the virtual success). This means that if a C object may no longer be effective from one at Memcpy to the other - fairly, the C standard clearly prohibits Memcpy not a POD (simple and old type). If Memcpy is not good enough, then Realloc is not good - so it does not give redistribution from C from the first day. C has new and delete, but there is no "renew" or other things similar to Realloc. Such std :: allocator (it assigns another thing is the troubles of bringing everyone) nor does it offer any redistribution interfaces. So in the second millennium twilight, most modern C libraries do not have a means to support optimized memory allocation. Because C lacks efficient redistribution, the C program is destined to use more memory and / or the speed that should be reached. C and C are not external support_msize, so a dynamically allocated memory size is usually stored twice in actual application - inside the dispenser and in the program itself, this is more in the wound. Please temporarily close your eyes now. Close your eyes imagine you around a variety of C applications to run (or crash) - all of these desktop applications, commercial applications, servers, multi-storey, embedded systems, and others you can think of. Now open your eyes face reality. They are not optimized: they do not need to copy memory when copying memory, and they manage information that does not require them. All of this is due to the design of C's original dispensers that do not provide access to customers [4]. We now look at how we optimize the buffer's memory allocation. Because the type cache uses the allocator, we need a good solution to a STD :: Allocator interface.
The mallocator is not, this is not a good dock movie name (the authors think it is very elephant? Is it true?) - He is just a mall-based mall-based distributor. We started from a fact that Realloc is not completely useless in C , it is just unable to use a part of C . Therefore, even if you lack enough standard C API, you can safely use Realloc to any POD, including all basic types. This is in our Buffer class, in order to achieve a fast redistribution, we need the following premise: a) Know if an arbitrary type T is a POD. B) Define a malloc-based alligator, which has the same interface and increase the redistribution function. For POD, this new distributor uses Realloc directly, for non-POD, it returns "No", in which case the caller must perform a typical allocation - copy-release series actions. C) Provide a method to ask a dispenser: "Can you redistribute?" Std :: allocator to this question "No." The point is, you can't std :: allocator. D) If possible, try to use Reallocate as possible in Buffer. That is to say, if you instantiate buffe
Namespace Typetraits {Template
By default, only basic types are POD, just as shown in [5], you can know the type of Typetraits :: ISPODB) Herb and Jim that you know to be a POD type, you might write: "Prophet Austern has been detailed How to write a compatible distributor. "In fact, Matt Austern did in [6], this you can find it online in universal interconnects.
Template
C) How do I query a type of function without changing its internal code? Of course, --traits [7]! This doesn't need to spend any strength:
Template
Template
template
Why do you need an OldobjectCount parameter? Very simple: When A :: Realloc returns zero, then you need to assign a new memory block and copy the object, you need to know how many objects you have. This is why two reallocate functions implement the number of objects, not the number of bytes. The Reallocate compares the member function of the standard distributor is a function of a slightly higher level. Now when you call Reallocate, all buffer
Mobile Objects We are now going to another topic. Even if you can quickly redistribute memory by means of any means of expanding the ground, this is still far away from the optimization. Consider the following:
Buffer Now no matter whether you use Mallocator, sometimes you have to experience allocation - copy - destroyed loops. Assume that this action happens, the RESERVE call includes mobile 1,000,000 string objects. In C , the approach of moving objects is similar to those basic transfer methods: at another clone object and destroy source objects. Clone All these strings may overhead huge - if the string is not a reference number. This is much larger than only copying their memory - this is very likely [9] - Su RESERVE brings a lot of unnecessary work, reserve allocates a new memory block, copy each string to new blocks , Then finally destroy the string in the old block. But why did we actually need to replace the object easily, but do all of these copy movements? We only need to tell a string: "Hey, you have a new location in the store, please re-place it yourself." Because the old position of the string is already useless (no matter how it will be discarded), the string can be quickly Copy their internal pointer to the target memory block. We don't need to think that you can understand that you need to make movements as a basic operation, and a copy is copied. Mobile has no replication, the total number of object remains unchanged. In the absence of this concept, we have achieved it by cloning in C and destroying the original object. We need to do more smart. There are many ways to move. The simplest one can use functions Template But this is not that simple. Suppose you are implementing a string class: Class UltimateString {char * end_; char * end /; char * endofStorage_; // Because you can't use _msize, sigh .... public: .... Other functions ....}; now assume you UltimateString Move, as follows template <> void Move (UltimateString * src, void * dest) {UltimateString * typedDest = static_cast If you think this is ok, then you are wrong. This is not possible, because the standard gives the compiler to add their own data to the freedom in your class (unless they are POD). That is to say: The compiler may add an int __coolnessfactor_ member variable in your string class, a member variable you don't know, let you copy all things from SRC to Dest to become impossible. The result of Move is unpredictable. C emphasizes that each constructor must be created by calling one of these constructors. Other ways to create objects are prohibited. So the mobile object must be implemented by some constructor. TEPLATE Takeover wraps a reference in an object and provides access to it. Then, you implement a UltimateString constructor, which accepts a Takeover Class Midget {Widget W_; PUBLIC: Midget (Takeover This problem can be avoided in two ways. One is very simple, replace the widget member with widget * (or you have enough strength, replace it with std :: auto_ptr Midget (TakeOver This is to create a null value of a target object, exchange with a null value and the value of the subject being taken. Take over constructor and exchange are two related and slightly repeated operations, and the relationship between them is as follows: * You can implement a tube constructor with a SWAP that does not thrown, but only you have a constructor that does not thrown. If all your constructor may throw, SWAP is unable to do use to implement the takeover constructor. * If there is no @ #!% ^ & (On this point, you block several text) alignment issues, you can use a tube constructor to implement SWAP. Void Swapviamove (Midget & lhs, Midget & rhs) {char buffer [sizeof (midget)] / / must be aligned to store a midget // copy LHS to Buffer Midget * lhsmoved = new (buffer) Midget (Takeover This function is incorrect (and looks very strange), because there is no 100%-pointed method to ensure that Buffer is correctly aligned to place a Widget. If you can access the constructor that does not thrown, you can remove the alignment problem like this: Void SwapViamove (Midget & lh, Midget & r Hs) {midget temp; // does not throw // moving LHS to TEMP TEMP. ~ MIDGET (); new (& TEMP) Midget (Takeover * If the type is POD, you will use Memcpy, Memmove, or [1], one of the fastest copy mode. Duff may say: I didn't listen to it. :) * Otherwise, if the type supports the tube constructor, use it * Otherwise, the special clone in the cycle - destroying the way. SUMMARY AND Conclusion The memory allocation method generally used in C and C is not optimized. The C library cannot expand a memory in place, and the result C also does not support these. In addition, C and C do not provide a size information of a memory block, but this information distributor can be obtained. These two defects affect the speed and footprint of the application. I don't know what extent, I suspect that the impact on most applications is small or ignored, and it is very troublesome for some procedures. In order to overcome the shortcomings mentioned, you can design a mallocator, which uses Realloc to POD. This allows the portion in your object to benefit from fast redistribution. If your C library implementation provides extension features similar to _msize and _expand, you can use these in your mallocator implementation, so that all objects perform fast redistribution. For C objects, the movement that does not thrown is a basic operation like a copy. Swap objects are conceptually some degree of moving (in a particular environment). A reliable mobile object method is to use a tube constructor. Whether you do it, there is a means of moving objects (fast and non-thrown accidents) to make a feasible way to make a composite container similar to Vector [5] Andrei Alexandrescu. "Generic You can get this source code from the CUJ website or http://merced.go.nease.net/code/buffer.zip.