confusions about a simple smart pointer implementaion - c++

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.

Related

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).

C++ safety of using `new char[size]` for memory allocation and deallocation for arbitrary data

I have the following functions:
private:
static char* new_data(std::size_t size) { return new char[size]; }
template<typename T> static char* new_data(T& value) { return reinterpret_cast<char*>(new (new_data(sizeof(T))) T(value)); }
template<typename T> static char* new_data(T&& value) { return reinterpret_cast<char*>(new (new_data(sizeof(T))) T(value)); }
static void delete_data(char* data) { return delete[] data; }
(Note: T is any type, except it won't overload any form of new or delete)
These functions are only used as part of a RAII-like struct, so there should not been any memory leaks caused by using them.
I am concerned about the safety of this, specifically with regards to alignment (as an array suitably aligned for char might not be properly aligned for T).
My questions is this:
What can I do to make my code perfectly safe? If it already is, why is it?
EDIT:
I am also assuming here that 'T' has a trivial destructor (well all the types I'm currently using do...)
Also with regards to new_data(std::size_t size)` I am only using it when I am going to manual construct the values (because the corresponding 'type' is to be generated at run time, which will not require allignment)
EDIT: Telling me to use smart pointers is not an answer just because using someone else's code could make my code smaller, that does not make it better (primarily due to efficiency and ease of use).
EDIT: I have decided to use the following modified code:
struct Constant
{
template<typename T, typename... Targs> static Constant&& construct(Targs... args...)
{
return Constant(sizeof(T), new(new char[sizeof(T)]) T(args...));
}
template<typename T> static Constant&& construct_default()
{
return Constant(sizeof(T), new(new char[sizeof(T)]) T);
}
template<typename T, typename... Targs> static Constant&& construct_list(Targs... args...)
{
return Constant(sizeof(T), new(new char[sizeof(T)]) T{args...});
}
Constant()
: Constant(0, nullptr, nullptr) { }
explicit Constant(std::size_t size, Syntax::Expression* creator = nullptr)
: Constant(size, new char[size], creator) { allocate(); }
Constant(const Constant& c)
: Constant(c.size_value, c.data_pointer, c.creator) { allocate(); }
Constant(Constant&& c)
: Constant(c.size_value, c.data_pointer, c.creator) { c.data_pointer = nullptr; }
private:
std::size_t size_value = 0;
char* data_pointer = nullptr;
public:
Syntax::Expression* creator = nullptr;
std::size_t size() const { return size_value; }
template<typename T = char*> T data() { return reinterpret_cast<T>(data_pointer); }
template<typename T = const char*> T data() const { return reinterpret_cast<T>(data_pointer); }
template<typename T> Constant&& copy() const
{
return Constant(size_value, new (new char[sizeof(T)]) T(*reinterpret_cast<T*>(data_pointer)), creator);
}
template<typename T> Constant&& copy(Syntax::Expression* e) const
{
return Constant(size_value, new (new char[sizeof(T)]) T(*reinterpret_cast<T*>(data_pointer)), e);
}
Constant&& raw_copy() const
{
return Constant(size_value, std::memcpy(new char[size_value], data_pointer, size_value), creator);
}
Constant&& raw_copy(Syntax::Expression* e) const
{
return Constant(size_value, std::memcpy(new char[size_value], data_pointer, size_value), e);
}
Constant& operator =(const Constant& c)
{
deallocate();
size_value = c.size_value;
data_pointer = c.data_pointer;
creator = c.creator;
allocate();
return *this;
}
Constant& operator =(Constant&& c)
{
deallocate();
size_value = c.size_value;
data_pointer = c.data_pointer;
creator = c.creator;
c.data_pointer = nullptr;
return *this;
}
~Constant() { deallocate(); }
private:
Constant(std::size_t size, void* data, Syntax::Expression* creator = nullptr)
: size_value(size), data_pointer(reinterpret_cast<char*>(data)), creator(creator) { }
static std::map<void*, std::size_t> allocation_table;
void allocate() const
{
if (data_pointer == nullptr)
return;
if (allocation_table.count(data_pointer) == 0)
allocation_table[data_pointer] = 1;
else allocation_table[data_pointer]++;
}
void deallocate() const
{
if (data_pointer == nullptr)
return;
if ((allocation_table[data_pointer] -= 1) == 0)
{
delete[] data_pointer;
allocation_table.erase(data_pointer);
}
}
};
std::map<void*, std::size_t> Constant::allocation_table = {};
Thank you to everyone who helped me. Though my question was never actually fully answered, based on my reading on http://en.cppreference.com/w/cpp/memory/new/operator_new and http://en.cppreference.com/w/cpp/language/new, I am pretty sure my code is safe (though not certain). Also I may have the ... syntax completely wrong, but I should find out when I actually try and compile it (and use it).
You're basically reinventing:
T* t = new T(value);
T* u = new T(std::move(value));
except it's much less functional, because I can't use any other constructor of T besides copy and move. Not even default! (Note also that your second constructor is incorrect - you should forward from value, not copy it, and having a forwarding-reference constructor makes having an lvalue reference constructor redundant. Also you want to create a decay_t<T>, not a T - because T can be a reference type).
And it's much less safe because... well, what do I do with t and u at the end? You would call delete_data(). That frees the memory, but it doesn't destroy the objects. You're missing a destructor call. delete t does both for you.
Basically, this gives you no advantage and a few important disadvantages over just normal raw new and delete. And modern C++ has tools that have advantages over those: smart pointers. When in doubt, use those instead.
It's also worth pointing out, that these two overloads:
static char* new_data(std::size_t size);
template<typename T> static char* new_data(T&& value);
do wildly different things in a way that could be very confusing. new_data(42) allocates 4 bytes and placement-news 42 into there. new_data((size_t)42) allocates 42 bytes. That's just very error prone.
You could allocate std::max_align_t instead of char, in order to have safely-aligned objects.
However, this doesn't address the issue that neither constructors, nor destructors, are going to get invoked here.
RAII is already a solved issue. There is no reason to reinvent this wheel, in some odd manner. Use containers, and/or std::shared_ptr, and your code will be RAII-safe, by default.

Using placement new in a container

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

Can operator new() initialize PODs before the constructor runs?

I think this question might be a duplicate, but I don't know how to search for it.
I'm trying to overload operator new so that I can allow for a variable-length buffer after my class. Does my current design work as intended, or is it undefined behavior?
If the answer differs in C++98, C++03, and C++11 then please explain the differences.
struct POD { /* ...other POD members here... */ };
struct BufferedPOD : POD
{
size_t n;
BufferedPOD()
// Assume n is already initialized...
{
}
static void *operator new(size_t size)
{
return ::operator new(size);
}
static void *operator new(size_t size, size_t additional_size)
{
void *const p = operator new(size + additional_size);
static_cast<BufferedPOD *>(p)->n = additional_size;
return p;
}
static void operator delete(void *p)
{
return ::operator delete(p);
}
static void operator delete(void *p, size_t)
{
return operator delete(p);
}
};
int main()
{
std::auto_ptr<BufferedPOD> p(new (1000) BufferedPOD());
foo(p.get()); // do something with buffer
return 0;
}
First off, you are relying on undefined behavior, the memory is indeterminate upon calling the constructor.
In debug builds, it will often be filled with some marker-pattern for easier debugging, in release builds this freedom is generally just used to speed up the construction.
In both, reading indeterminate objects gets you UB, however that will play out in detail.
Anyway, you are going at it the wrong way (let's ignore violation of the "rule of three" for the time being:
Just declare matching overloads for ::operator new and ::operator delete, and a factory-function (which should be the only code with access to the only ctor you leave usable) which uses that and passes the extra-space on:
void* operator new(size_t a, size_t b) {
if(a+b< a || a+b < b)
throw new std::bad_alloc("::operator new(size_t, size_t) too big");
return operator new(a+b);
}
void operator delete(void* p, size_t a, size_t b) {return operator delete(p/*, a+b*/);}
struct Buffered : POD { // Not a pod due to inheritance
Buffered* make(size_t extra) {return new(extra) Buffered(extra);}
private:
size_t n;
Buffered(size_t extra) : n(extra) {}
Buffered(Buffered&) = delete;
void operator=(Buffered&) = delete;
};

"pointer being freed was not allocated" in structure

I have defined a structure containing a bytes array and its length. The destructor should only delete the byte array if it was dynamically instanced by the structure's constructor. But sometimes, the delete array; instruction fails with error pointer being freed was not allocated. It seems really strange as the array was instanced in the structure. Can anyone help me figure out what's wrong?
typedef unsigned char byte_t;
struct bytes_t {
bytes_t(varint_t c) : array(new byte_t[c]), count(c), alloc(true) {}
bytes_t(byte_t b[], varint_t c) : array(&b[0]), count(c), alloc(false) {}
~bytes_t() {
if (alloc)
delete array;
}
byte_t* array;
varint_t count;
bool alloc;
};
Edit: Changed a little bit my code, but it still seems to fail. It works when called from the main thread, but not from another thread.
class bytes_t {
private:
byte_t* const array_;
const varint_t count_;
const bool alloc_;
public:
bytes_t(varint_t c) : array_(new byte_t[c]), count_(c), alloc_(true) {}
bytes_t(byte_t* b, varint_t c) : array_(b), count_(c), alloc_(false) {}
~bytes_t() {
if (alloc_)
delete[] array_;
}
byte_t* getArray() {
return array_;
}
varint_t getCount() {
return count_;
}
};
Edit: I followed #Jens' advice and used std::vector instead. Works like a charm!
There are some issues in your class:
When you allocate an array with new[], you must call delete[] instead of delete
Your class needs a copy-constructor and a copy-assignment operator
The class has only public members which provide a very poor abstraction and no information hiding. You should make the members private and provide a public interface for clients.
Have you considered using std::vector instead of writing your own implementation?