问题
I just came across some container implementation in C++. That class uses an internal buffer to manage its objects. This is a simplified version without safety checks:
template <typename E> class Container
{
public:
Container() : buffer(new E[100]), size(0) {}
~Container() { delete [] buffer; }
void Add() { buffer[size] = E(); size++; }
void Remove() { size--; buffer[size].~E(); }
private:
E* buffer;
int size;
};
AFAIK this will construct/destruct E
objects redundantly in Container()
and ~Container()
if new
/delete
are not customized. This seems dangerous.
Is using placement new
in Add()
the best way to prevent dangerous redundant constructor / destructor calls (apart from binding the class to a fully featured pool)?
When using placement new
, would new char[sizeof(E)*100]
be the correct way for allocating the buffer?
回答1:
AFAIK this will construct/destruct
E
objects redundantly
It would appear so. The new
ed array already applies the default constructor and the delete[]
would call destructor as well for all the elements. In effect, the Add()
and Remove()
methods add little other than maintain the size
counter.
When using placement new, would
new char[sizeof(E)*100]
be the correct way for allocating the buffer?
The best would be to opt for the std::allocator that handles all of the memory issues for you already.
Using a placement new
and managing the memory yourself requires you to be aware of a number of issues (including);
- Alignment
- Allocated and used size
- Destruction
- Construction issues such as emplacement
- Possible aliasing
None of these are impossible to surmount, it has just already been done in the standard library. If you are interested in pursuing a custom allocator, the global allocation functions (void* operator new (std::size_t count);
) would be the appropriate starting point for the memory allocations.
Without further explanation on the original purpose of the code - a std::vector
or a std::array
would be far better options for managing the elements in the container.
回答2:
There's a number of issues with the code.
If you call Remove()
before calling Add()
you will perform assignment to a destructed object.
Otherwise the delete[] buffer
will call the destructor of 100 objects in the array. Which may have been called before.
Here's a valid program:
#include <iostream>
int counter=0;
class Example {
public:
Example():ID(++counter){
std::cout<<"constructing "<<ID<<std::endl;
}
~Example(){
std::cout<<"destructing "<<ID<<std::endl;
ID=-1;
}
private:
int ID;
};
template <typename E> class Container
{
public:
Container() : buffer(new char [100*sizeof(E)]), size(0) {}
~Container() {
for(size_t i=0;i<size;++i){
reinterpret_cast<E*>(buffer)[i].~E();
}
delete [] buffer;
}
void Add() { new (buffer+sizeof(E)*size) E(); size++; }
void Remove() { reinterpret_cast<E*>(buffer)[--size].~E(); }
private:
void* buffer;
size_t size;
};
int main() {
Container<Example> empty;
Container<Example> single;
Container<Example> more;
single.Add();
more.Add();
more.Remove();
more.Add();
more.Add();
more.Remove();
return 0;
}
来源:https://stackoverflow.com/questions/35129485/using-placement-new-in-a-container