锘??xml version="1.0" encoding="utf-8" standalone="yes"?> You already know how to use placement
new
to allocate a single object on a predetermined memory address. However,
some programming tasks require the allocation of arrays on a
predetermined memory address. Here's how you do it. Mobile devices, embedded systems and custom garbage collectors are
only a few instances of programming environments that may require
placement
new allocation of arrays. Before I discuss the
details of such array allocations, let's remind ourselves briefly how
scalar (i.e. non-array) placement
new works. The scalar version of placement
new takes a user-supplied address on which it constructs a single object. Unlike the ordinary version of the
new operator, placement
new doesn't allocate storage for the object; it merely constructs the object on the memory address you provide:
#include <stdexcept>
using namespace std;
template<typename T>
class Ptr2T {
public:
//鏋勯犲嚱鏁幫紝褰㈠弬涓烘暟緇勮搗濮嬪湴鍧鍜屽ぇ灝?/span>
Ptr2T(T *p, int size)
: m_p(p), m_array(p), m_size(size) { };
Ptr2T& operator++(); //鍓嶇紑++
const Ptr2T operator++(int); //鍚庣紑++
Ptr2T& operator--(); //鍓嶇紑--
const Ptr2T operator--(int); //鍚庣紑--
Ptr2T& operator+=(int n);
Ptr2T& operator -=(int n);
//瀹夊叏鐨勬暟緇勫厓绱犺闂搷浣?/span>
T& operator*() const;
private:
T *m_p; //璁塊棶鏁扮粍鐨勬寚閽?/span>
T *m_array; //淇濆瓨鏁扮粍鐨勮搗濮嬪湴鍧
int m_size; //淇濆瓨鏁扮粍鐨勫ぇ灝?/span>
};
template<typename T>
inline Ptr2T<T>& Ptr2T<T>::operator++()
{
m_p += 1;
return *this;
}
template<typename T>
inline const Ptr2T<T> Ptr2T<T>::operator++(int)
{
Ptr2T current = *this;
++(*this); //鐢ㄩ噸杞界殑鍓嶇紑++鏉ュ疄鐜?/span>
return current;
}
template<typename T>
inline Ptr2T<T>& Ptr2T<T>::operator--()
{
m_p -= 1;
return *this;
}
template<typename T>
inline const Ptr2T<T> Ptr2T<T>::operator--(int)
{
Ptr2T current = *this;
--(*this); //鐢ㄩ噸杞界殑鍓嶇紑--鏉ュ疄鐜?/span>
return current;
}
template<typename T>
inline T& Ptr2T<T>::operator*() const
{
if (m_p < m_array || m_p > m_array + m_size - 1) { //瓚婄晫媯鏌?/span>
throw out_of_range("out of range");
}
return *m_p;
}
template<typename T>
inline Ptr2T<T>& Ptr2T<T>::operator+=(int n)
{
m_p += n;
return *this;
}
template<typename T>
inline Ptr2T<T>& Ptr2T<T>::operator-=(int n)
{
m_p -= n;
return *this;
}
template<typename T>
Ptr2T<T> operator+(const Ptr2T<T> &p, const int n)
{
return Ptr2T<T>(p) += n; //鐢ㄩ噸杞界殑+=鏉ュ疄鐜?/span>
}
template<typename T>
Ptr2T<T> operator+(const int n, const Ptr2T<T> &p)
{
return p + n;
}
template<typename T>
Ptr2T<T> operator-(const Ptr2T<T> &p, const int n)
{
return Ptr2T<T>(p) -= n; //鐢ㄩ噸杞界殑-=鏉ュ疄鐜?/span>
}
//浣跨敤鏂規硶
int main(void)
{
char a[5] = {'a', 'b', 'c', 'd', 'e'};
int b[5] = {1, 2, 3, 4, 5};
Ptr2T<char> pc(a, 5);
Ptr2T<int> pi(b, 5);
cout << *pc++ << endl;
pi--;
pi += 2;
cout << *(pi - 1) << endl;
*++pi = 100;
cout << *pi << endl;
return 0;
}
]]>
int m_y;
public:
//explicit闄愬埗int鍒癥ear鐨勯殣寮忚漿鎹?/span>
explicit Year(int y)
: y(m_y) { }
//Year鍒癷nt鐨勭被鍨嬭漿鎹?nbsp;
operator int() const
{ return m_y; }
//other funtion
}
public :
Date(int d, Month m, Year y);
//
};
Date d1(1987, feb, 21); //error, 21涓嶈兘闅愬紡杞崲涓篩ear
Date d2(21, feb, Year(1987)); //ok
鍦ㄨ繖閲孻ear灝卞彧鏄寘瑁逛綇浜唅nt錛屽int鎻愪緵涓灞備繚鎶よ屽凡銆傜敱浜巓perator int()鐨勫瓨鍦紝鍙闇瑕侊紝Year鍙互闅愬紡鐨勮漿鍖栦負int鍑虹幇榪愮畻琛ㄨ揪寮忎腑鍙傚姞榪愮畻銆傝岄氳繃緇欐瀯閫犲嚱鏁板0鏄庝負explicit錛屽氨鑳藉淇濊瘉錛宨nt鍒癥ear鐨勮漿鍖栧彧鑳藉湪鏄庣‘鏃犺鐨勬儏鍐佃繘琛岋紝閬垮厤浜嗘剰澶栫殑璧嬪箋?br>
鏄劇ず鏋勯犲嚱鏁板拰杞崲榪愮畻絎︾殑鍚堜綔錛岃Year鍙互褰搃nt浣跨敤錛屽悓鏃跺張瀵筜ear榪涜涓瀹氱殑淇濇姢銆傘傘?br>
]]>
Placement
new Overview
class Widget {
public:
Widget();
virtual ~Widget
virtual void Draw();
};
char* buf=new char [sizeof (Widget)];//preallocate
Widget* widget= new(buf) Widget; //construct Widget on buf
widget->Draw(); //use Widget
To destroy widget you first have to invoke its destructor explicitly:
Next, reclaim the raw memory like this:
Array Allocation
Allocating arrays with placement new follows the same steps more or less, but you have to pay attention to additional nuances. Here is a step-by-step guide:
First, allocate a buffer large enough to hold an array of the desired type:
Don't be tempted to calculate the size manually; always use sizeof to ensure that the buffer is properly aligned and has the right size.
Next, construct an array of ARRSIZE objects on the buffer using placement new[] :
You can now use the allocated array as usual:
for (int i=0; i<ARRSIZE; i++)Make sure that your target class -- Widget in this example -- has a public default constructor. Otherwise, it would be impossible to create arrays thereof.
{
widgets[i].Draw();
}
To destroy such an array allocated by placement new you have to call the destructor for each element explicitly:
The while -loop uses a descending order to preserve the canonical destruction order of C++ -- the object that was constructed last must be destroyed first. To comply with this requirement, the element with the highest index is destroyed first.
Finally, you release the raw memory on which the array resided by calling delete[] :
delete[] buf;
The array placement new has a potential performance problem: it initializes every element in the array unconditionally. If your app deals with large arrays, this isn't the most efficient way. In some apps only a portion of the array is actually used, and in other apps the elements are assigned a different value immediately after their construction. In these cases, you want to postpone, or even completely avoid, the automatic initialization of array elements. To avoid the initialization of placement new arrays, follow the following steps:
As before, begin with an allocation of a raw buffer with the appropriate size. This time however, use the global operator new instead of the new operator:
Widget * warr=
static_cast<Widget*> (::operator new ( sizeof(Widget)* ARRSIZE));
The global operator new , very much like C's malloc() , merely allocates raw bytes of memory from the free-store, without initializing them. It returns void * rather than Widget* which is why you need to cast the result explicitly.
At this stage, warr is a pointer to raw memory. You can't access its elements because they haven't been initialized yet. To initialize individual elements, call placement new once more, for each element you want initialized:
void assign(Widget arr[], size_t & sz, const Widget& init)
{
new (&arr[sz++]) Widget (init); //invoke copy ctor
}
assign() passes the address of an individual element to placement new which in turn invokes Widget 's copy constructor. The copy-constructor initializes that element with init . Using this technique, you can initialize elements selectively, leaving the rest of the array uninitialized.
To destroy such an array, invoke the destructor of every initialized object. Then call the global operator delete to reclaim the raw storage:
void destroy(Widget arr[], size_t & sz)
{
while (sz)
{
arr[--sz].~Widget();//destroy all initialized elements
}
::operator delete (arr); //reclaim raw storage
}
The techniques I've presented here are bug prone. Therefore, they should be encapsulated in higher-level classes that hide the implementation details from users. These techniques aren't rarely-used as they might seem. STL allocators use them under the hood to avoid object initialization and minimize reallocations.
NULL is to be used for pointers only since it may be defined as ((void *)0)
, this would cause problems with anything but pointers.
0 can be used anywhere, it is the generic symbol for each type's zero value and the compiler will sort things out.
'\0' should be used only in a character context.
nul is not defined in C or C++, it shouldn't be used unless you define it yourself in a suitable manner, like: