Using placement new in a container - c++

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?

AFAIK this will construct/destruct E objects redundantly
It would appear so. The newed 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.

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;
}

Related

confusions about a simple smart pointer implementaion

The following code is abstracted from the book << Hands-On Design Patterns with C++ >> by Fedor G. Pikus published by Packt.
Some confusions have been bugging me for weeks.
(1) How the char array mem_ is initialized?
(2) Is allocate used to allocate memory? How?
(3) Why does mem_ == p ? How was the memory delocated?
// 02_scoped_ptr.C
// Version 01 with deletion policy.
#include <cstdlib>
#include <cassert>
#include <iostream>
template <typename T, typename DeletionPolicy>
class SmartPtr {
public:
explicit SmartPtr(T* p = nullptr,
const DeletionPolicy& deletion_policy = DeletionPolicy() )
: p_(p), deletion_policy_(deletion_policy) {}
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
~SmartPtr() { deletion_policy_(p_); }
void release() { p_ = NULL; }
T* operator->() { return p_; }
const T* operator->() const { return p_; }
T& operator*() { return *p_; }
const T& operator*() const { return *p_; }
private:
T* p_;
DeletionPolicy deletion_policy_;
};
class SmallHeap {
public:
SmallHeap() {}
SmallHeap(const SmallHeap &) = delete;
SmallHeap &operator=(const SmallHeap &) = delete;
~SmallHeap() {}
void * allocate(size_t s) {
assert(s <= size_);
return mem_; // ------------------ is allocate used to allocate memory? how?
}
void deallocate(void *p) {
assert(mem_ == p); // ------------------- why does mem_ == p ? How was the memory delocated?
}
private:
static constexpr size_t size_ = 1024;
char mem_[size_]; // ------------------- how mem_ is initialized?
};
void * operator new(size_t s, SmallHeap *h)
{
return h->allocate(s);
}
template<typename T>
struct DeleteSmallHeap {
explicit DeleteSmallHeap(SmallHeap &heap)
: heap_(heap) {}
void operator()(T *p) const {
p->~T();
heap_.deallocate(p);
}
private:
SmallHeap &heap_;
};
int main() {
SmallHeap a_sh_obj;
SmartPtr<int, DeleteSmallHeap<int>> sh_sp{new(&a_sh_obj) int(42), DeleteSmallHeap<int>(a_sh_obj)};
std::cout << *sh_sp << std::endl;
}
------------------ Update 1 : how is char related to memory? --------------------
Thanks for the helpful explanations, and I need some time to them, especially the custom allocator.
But one thing that is really strange to me is that:
we are talking about memory stuff, but why do we need a char array here?
This code demonstrates a custom allocator which has a static fixed size of size (1024). There is no allocation, but it can be used as an allocator on a STL container on the assumption that you will never need more than 1024 bytes.
If you do need more, boom.
char mem_[size_];
This line initializes the static size and allocate() simply returns that without any call to new.
For the deallocation it uses a simple assert to ensure that the memory that is to be 'deleted' is the same than the one that was 'created'.
All these practises are practically non existant. If you do need a vector of a static size, use a std::array. If you need a vector of an unknown size, use the reserve() vector function to preallocate. If your vector's size is unknown but expected to be small, it's okay to leave it as it is for, in Windows (and I assume in other OSes), it eventually calls HeapAlloc and HeapFree which, for small allocations, are probably cheap, especially if the vector is within a limited scope.
If you need some flexible combination of stack/heap vector, you can use https://github.com/thelink2012/SmallVector.
How the char array mem_ is initialized?
mem_ is not initialized as in filled with values until the use of the custom new operator in new(&a_sh_obj) int(42). This only initializes a small portion of the memory though. Space is allocated on the stack however when you create the local SmallHeap a_sh_obj; variable in main.
Is allocate used to allocate memory? How?
Yes, it is used. The expression new(&a_sh_obj) int(42) uses
void * operator new(size_t s, SmallHeap *h)
{
return h->allocate(s);
}
which gets sizeof(int) passed as first parameter and &a_sh_obj as second parameter.
Why does mem_ == p? How was the memory delocated?
On destruction of sh_sp the DeleteSmallHeap<int> object is used to get rid of the object. The assert is just verification that the memory "freed" is actually the one expected. This doesn't actually deallocate anything, since the memory is still owned by a_sh_obj. It's leaving the main function that in fact releases the memory during when cleaning up a_sh_obj.

The element without default constructor in the custom template container

I need to create a simple template container, which can store any object of any type and could be used everywhere. So, I did something like this:
template <typename Type>
class Container {
public:
Container() : arraySize(10) { valueWrappers = new Type[arraySize];}
Container(const Container& other) { /* --- */}
~Container() { /* --- */}
Container& operator=(const Container& other) { /* --- */}
/* some functions */
private:
int arraySize;
Type* valueWrappers;
};
Now I have the problem - when I'm trying to create my container using as template a class without default constructor, the compilation error appears:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main() {
Container<MyClass> cont;
return 0;
}
C2512 'MyClass': no appropriate default constructor available
The problem is that I need to initialize the array of "Type" values with something, but I don't no what I need to use. I can't use NULL because, in this case, Container will work only with pointers. So, can somebody give an advice, how am I able to do it? Or, maybe, there is another way to solve this task?
Based on your requirements, I think you're going to have to use placement new. Since you haven't provided all the relevant code, I'm going to do what I can.
First, you're going to have to allocate raw memory instead of using new directly.
Container() : arraySize(10) { valueWrappers = reinterpret_cast<Type*>(::operator new(sizeof(Type) * arraySize)); }
Now when you put something in your Container, you'll have to construct it in place, using something like the following:
new (valueWrappers + index) Type(arguments to type);
In your destructor, you'll need to explicitly call the destructors on any object that you used placement new for.
valueWrappers[index]->~Type();
Lastly, release the memory using ::operator delete.
::operator delete(valueWrappers);
Please bear in mind that this is a very quick and dirty answer, and this code can be hard to debug and maintain. You're going to have to keep track of what indexes in valueWrapper have been initialized and which haven't during cleanup. If possible, I highly recommend using something akin to std::vector, which handles all this complexity for you.
One option is to not allocate the array in the default constructor, but initialise valueWrappers to null instead. Another option is to not have a default constructor in your template. Third option is to keep your class as-is and simply document that the template is default constructible only if the type argument is default constructible.
You can use std::optional to defer initialization, which is guaranteed to handle object lifetime correctly. Letting a default constructed container have 10 elements is also a questionable choice — a (count) constructor may be preferable.
template <typename Type>
class Container {
using elem_t = std::optional<Type>;
std::size_t count{};
std::unique_ptr<elem_t[]> elems{};
public:
Container() = default;
Container(std::size_t cnt)
: count{cnt}
, elems{std::make_unique<elem_t[]>(cnt)}
{
}
// for example
template <typename... Args>
void construct_at(std::size_t pos, Args&&... args)
{
assert(pos < count);
assert(!elems[pos]);
elems[pos].emplace(std::forward<Args>(args)...);
}
// ...
};
Note that I used std::unique_ptr to simplify memory management; a pointer will also be OK, though apparently more error-prone. Now you can traverse the container and construct the elements:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main()
{
Container<MyClass> cont(10);
for (std::size_t i = 0; i < 10; ++i) {
cont.construct_at(i, /* argument */);
}
}

C++17 polymorphic memory recources not working

I was trying to understand the c++17 pmr.
So I did this and it is not working as I thought, what could go wrong?
template <typename T>
class Memory : public std::experimental::pmr::memory_resource {
public:
Memory() { this->memory = allocate(sizeof(T), alignof(T)); }
void *getMemory() { return this->memory; }
~Memory() { deallocate(this->memory, sizeof(T), alignof(T)); }
private:
void *do_allocate(std::size_t bytes, std::size_t alignment)
{
memory = ::operator new(bytes);
}
void do_deallocate(void *p, std::size_t bytes, std::size_t alignment)
{
::operator delete(memory);
}
bool do_is_equal(
const std::experimental::pmr::memory_resource& other) const noexcept
{
}
void *memory;
};
what can be going wrong with my implementation?
This is the client..
Memory<std::string> mem;
std::string * st = (std::string*)mem.getMemory();
st->assign("Pius");
std::cout << *st;
The polymorphic resource allocators allocate memory; that's all they do. Unlike container Allocators, they don't create objects. That's why they return void*s.
Memory resources are not meant to be used by themselves. That's why std::polymorphic_allocator<T> exists. You can also do the object creation/destruction yourself, using placement-new and manual destructor calls.
Also, your memory_resource implementation makes no sense. do_allocate should return the allocated memory, not store it internally. Your function provokes undefined behavior by returning nothing (which your compiler should have warned about).

how to do a vector<class objects*> deep copy

I am having trouble with writing the Rule of three when it comes to vector of pointers to class objects. Searched and examples don't seem to apply. I have these three classes:
class Data
{
private:
map<string, double> m_DataVariables;
public:
Data();
Data(const Data &data);
};
class Sample
{
private:
Data *m_pData;
public:
virtual ~Sample()
{
delete m_pData;
}
Sample();
Sample(const Sample &sample);
};
class BuildTree
{
private:
vector<Sample*> BuildSamples;
public:
BuildTree(vector<Sample*> &Samples);
// This does not compile
BuildTree(const BuildTree& other) : BuildSamples(new(other.BuildSamples))
{
}
~TreeBuilding()
{
for (int i = 0; i < BuildSamples.size(); ++i)
delete BuildSamples[i];
}
void BuildTrees(void);
};
1- Not sure if I am correctly deleting BuildSamples.
2- In the constructor want to do a deep copy of the passed parameter into member variable BuildSamples.
BuildTree::BuildTree(vector<Sample*> &samples)
{
BuildSamples = samples; // This just copies the references
}
How do I write the copy constructor to make a deep copy? What am I missing here?
3- Please note: don't have access to smart pointers, share_ptr or unique_ptr, etc. C++98 is what I have.
Please show the steps required to do this. Really appreciate your time and consideration.
I noted you marked your question as c++98; even if C++98 doesn't support smart pointers in std::, you can certainly use the smart pointers defined in Boost, e.g. boost::shared_ptr.
For example, instead of using raw owning pointers like in vector<Sample*>, you can make your code simpler with
vector<boost::shared_ptr<Sample>>.
In this way, the copy and destruction operations will be automatically implemented under the hood.
You probably want something like:
BuildTree(const BuildTree& other)
{
BuildSamples.reserve(other.BuildSamples.size());
for (std::size_t i = 0; i != other.BuildSamples.size(); ++i) {
BuildSamples.push_back(new Sample(*other.BuildSamples[i]));
}
}
BuildTree(const vector<Sample*> &samples)
{
BuildSamples.reserve(samples.size());
for (std::size_t i = 0; i != samples.size(); ++i) {
BuildSamples.push_back(new Sample(*samples[i]));
}
}
You need to initialise the BuildSamples member as a vector of pointers, and ensure every pointer points at a clone of the objects passed. One way is
BuildTree(const BuildTree& other) : BuildSamples(other.BuildSamples)
{
std::vector<Sample *>::iterator i = BuildSamples.begin(), end = BuildSamples.end();
while (i != end)
{
*i = new Sample(**i);
++i;
}
}
The usage of BuildSamples(other.BuildSamples) initialises BuildSamples with the correct number of elements, but those elements are the same pointers as in other.BuildSamples. This ensures the vector is of the correct size, without worrying about setting the size explicitly. That is a shallow copy. The body of the constructor then sets every element of BuildSamples so it points at a clone of itself - thus completes the deep copy.
The constructor BuildTree(const std::vector<Sample *> &) can be implemented in a similar manner.
Note: given that your class is implementing a non-trivial copy constructor (to do the deep copy) and a destructor to cleanup, you also need to implement an assignment operator BuildTree &operator=(const BuildTree &). For an explanation why, look up the "rule of three".
While dealing with pointers and to avoid crashing of a running program due to destructor being called, Deep copy must be used! deep copy allows creating a new pointer pointing to a newly allocated space. But when passing objects as R-value in vector it is better to use the move constructor.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class example{
private:
int *pointer;
public:
//constructor
example(int d){
pointer = new int;
*pointer = d;
cout<<"Constructor Called"<<endl;
}
// deep copy
example(const example &source){
pointer = new int;
*pointer= *source.pointer;
cout<<"deep copy made"<<endl;
}
// Move Constructor
example(example &&source) noexcept :pointer{source.pointer}{
source.pointer = nullptr;
cout << "object moved"<<endl;
}
// Destructor
~example() {
delete pointer;
cout << "Destroyed"<<endl;
}
};
int main()
{
vector <example> vec;
vec.push_back(example{300});
vec.push_back(example{300});
vec.push_back(example{300});
vec.push_back(example{300});
return 0;
}

What is the extra cost of overloading placement new operator?

We want to overload placement new operator just to verify that used memory size is enough for the given class. We know this size. The construction is more or less in this way:
template <size_t MAXSIZE>
class PlacementNewTest {
public:
void* operator new (size_t size, void* where)
{
if (size > MAXSIZE) {
throw bad_alloc();
}
return where;
}
};
Let say used in such simplified context:
char buffer[200];
class A : public PlacementNewTest<sizeof buffer> {
public:
char a[100];
};
class B : public A {
public:
char b[200];
};
int main() {
A* a = new (buffer) A; // OK
a->~A();
B* b = new (buffer) B; // throwed bad_alloc
b->~B();
}
During testing phase I have this PlacementNewTest<> class used, but in the release code I consider to remove it. Do you think, basing on your experience, how much this will cost our performance, not to remove this extra test class? Is this only cost of this verification if (size > MAXSIZE)?
In other words, what is performance penalty for such redefinition:
class PlacementNewNOP {
public:
void* operator new (size_t size, void* where)
{
return where;
}
};
Maybe it is not important in this question - but:
This is, and must be, C++03. We cannot upgrade to C++11. And boost is not an option too, just C++03.
There shouldn't be any overhead apart from the comparison, unless you are using virtual methods, the binding is static.
Of course there is the exception overhead, but since that is something that shouldn't happen, you should be safe to ignore it.