Implementing simple allocator - c++

As a part of vector implementation I have to implement an allocator using functions malloc() and free() given the below interface:
class Allocator manages memory for class vector:
template<class T>
class Allocator {
public:
// function members
T* allocate(int n);
void deallocate(T* p, int n);
void construct(T* p, const T& v);
void destroy(T* p);
};
class Allocator member implementations:
/*
Function: allocate()
Use: allocator_object.allocator(num_of_elements)
Implicit in vector_base constructor.
It wraps malloc(); Throws bad_alloc
if allocation unsuccessful.
*/
template <class T>
T* Allocator<T>::allocate(int n) {
try {
std::auto_ptr<T> mem_ptr = reinterpret_cast<T*>(malloc(n * sizeof(T)));
} catch (std::bad_alloc& e) {
std::cerr <<"bad_alloc caught: "<< e.what() <<'\n';
throw;
}
return mem_ptr.release();
}
/*
Function: deallocate()
Use: allocator_object.deallocate(mem_ptr, redundant_par);
Implicit in base_vector destructor.
It returns memory to free store.
First argument is the pointer returned by malloc().
*/
template <class T>
void Allocator<T>::deallocate(T* mem_ptr, int n) {
free(mem_ptr);
}
/*
Function: construct()
Use: allocator_object.construct(elem_ptr[i], value)
Implicit in vector_base constructor
and all modifying members of vector.
It assigns the value passed as second
argument to the element with address
held by the pointer passed as a first
argument.
*/
template <class T>
void Allocator<T>::construct(T* p, const T& v = T()) {
*p = v;
}
/*
Function: destroy()
Use: allocator_object.destroy(elem_ptr[i]);
Implicitly in vector members: reserve(), resize();
It assigns type default value to the element
with address held by the pointer passed as argument.
*/
template <class T>
void Allocator<T>::destroy(T* p) {
*p = T(); // ? not sure if right
}
How to check for possible bad allocations from malloc() in function allocate()1, is the current method correct?
Edit:
Is the implementation of function destroy() correct2, could you give me an example of correct implementation?
There is an additional argument, n, in function deallocate() which I can't figure out how(what for) to use.
1. I know I'm using a deprecated std::auto_ptr, just following the book recommendations wrongly trying to get rid of the pointer in case of bad allocation.
2. I've read the documentation for function destroy(), but considering the fact that the allocated memory is one contiguous chunk that is free()d by passing the pointer returned by malloc(), the only reasonable object destruction is the assignment of a default value.

std::bad_alloc isn't magically generated. malloc fails by returning nullptr and there's nothing which would convert that into an exception. You call malloc, so you must check the return value manually.
destroy is wrong for the same reason as construct is wrong: They're a pair of functions that turn raw memory into an object and back. The normal way to do so is by placement new and direct invocation of the destructor. You're just calling the assignment operator there, which doesn't affect the objects lifetime at all.

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.

How unique_ptr can use the custom deleter function?

For educational sake I am trying to understand a bit how smart pointers works with custom deleters. So I've written this code on my own that simulates how std::uniqe_ptr works. In fact I also think about changing the deleters template functions into template function classes and specialize one for dynamic arrays. Anyway this is what I've tried:
template <typename T>
void Free(T* p)
{
std::cout << "Free(T*): Freeing memory of a dynamic object...\n";
delete p;
}
template <typename T>
void Del_It(T* p)
{
std::cout << "Del_It(T*): freeing memory of a dynamic object...\n";
delete p;
}
template <typename T, typename Deleter=void(*)(T*)>
class Uniptr
{
public:
Uniptr(T* = nullptr, Deleter = Free<T>);
virtual ~Uniptr();
private:
T* ptr_{nullptr};
Deleter delPtr_{nullptr};
};
template <typename T, typename Deleter>
Uniptr<T, Deleter>::Uniptr(T* p, Deleter d) :
ptr_(p),
delPtr_(d)
{
}
template <typename T, typename Deleter>
Uniptr<T, Deleter>::~Uniptr()
{
delPtr_(ptr_);
delPtr_ = nullptr;
}
int main()
{
Uniptr<int, void(*)(int*)> upi(new int(9), Del_It<int>);
std::cout << "\ndone\n";
}
It looks that it works fine but I need your advice, tips... Thank you
To be honest custom deleter can be everything. Let's make a dirty our hand,
int main()
{
auto f = [](FILE* f){std::fclose(f); std::cout<<"I am closing file\n";};
std::unique_ptr<FILE,decltype(f)>fptr{std::fopen("test.txt","w"), f};
}
What we have done here? We have wrapped the file handling in C via unique_ptr.
What we get it? In case of forget to close the file after the operation, we ensure that it will be closed automatically.
So that, do not think that custom deleter can only use to delete pointer. I can be specified for any application such as a socket, file, etc...
Please check that unique_ptr
Code was taken from File Wrapper
A custom deleter can used to perform some custom clean up operation when the unique pointer is destroyed. In your case you're not doing anything apart from deleting the pointer. Therefore you're not performing any custom clean up. Plus you've got two functions Free and Del_It that do the same thing. If Del_It is meant for dynamically-allocated arrays, it should do delete [] p.
Suppose you're working with legacy code that specifically requires you to call a release() function that frees resources:
class LegacyClass
{
...
void release() {...}
~LegacyClass() {...}
};
You can't change the legacy code (it might even be in shared library), but you have to call the release() function. So you could define a custom deleter to do that:
struct Deleter
{
void operator()(LegacyClass *ptr) const
{
if (ptr)
{
ptr->release();
delete ptr;
}
}
};
With a unique pointer the deleter is actually part of the type:
unique_ptr<T, Deleter>
but for a shared pointer the custom deleter is not part of the type; it's passed into the constructor. The custom deleter can be defined as:
functor
function pointer
lambda
std::function *
*(this one can bloat the size of your unique pointer)

Scoped std::unique_ptr cast

I'm currently working on some code using smart pointers in which it is necessary at a number of points to cast these pointers to their base types and pass them as const arguments to functions. Currently I'm using shared_ptr's and the standard pointer casting functions to achieve this, but this seems inefficient (as each cast costs at least one CAS) and also misleading (as we are not modelling a shared relationship, the parent is the sole owner of the object).
I therefore came up with the following but wanted to check it is indeed safe, or is there some edge case which will break it?
template <typename ToType, typename FromType>
class FTScopedCastWrapper {
public:
explicit FTScopedCastWrapper(std::unique_ptr<FromType>& p) : from_ptr_(&p) {
auto d = static_cast<ToType *>(p.release());
to_ptr_ = std::unique_ptr<ToType>(d);
}
~FTScopedCastWrapper() {
auto d = static_cast<FromType *>(to_ptr_.release());
(*from_ptr_) = std::unique_ptr<FromType>(d);
}
const std::unique_ptr<ToType>& operator()() {
return to_ptr_;
}
// Prevent allocation on the heap
void* operator new(size_t) = delete;
void* operator new(size_t, void*) = delete;
void* operator new[](size_t) = delete;
void* operator new[](size_t, void*) = delete;
private:
std::unique_ptr<FromType>* from_ptr_;
std::unique_ptr<ToType> to_ptr_;
};
template <typename ToType, typename FromType>
FTScopedCastWrapper<ToType, FromType> FTScopedCast(std::unique_ptr<FromType>& p) {
return FTScopedCastWrapper<ToType, FromType>(p);
}
The intended usage is then
void testMethod(const std::unique_ptr<Base>& ptr) {
// Do Stuff
}
auto ptr = std::make_unique<Derived>();
testMethod(FTScopedCast<Base>(ptr)());
The deleter is not carried across as doing so would prevent upcasting. It also doesn't make sense to do so as the deleter will never be invoked on the created smart pointer anyway.
Allocation on the heap is prevented as it could allow the wrapper to outlive the pointer it wraps, copying is prevented by the std::unique_ptr member and standard destruction order will ensure the raw pointer is returned to the original smart pointer before it is destroyed, even if it is declared in the same scope as the wrapper.
I'm aware this is not thread safe but I'd argue sharing a unique_ptr between threads is breaking its contract of a single owner.
If I understand you correctly, the intention is to "steal" the contents of a std::unique_ptr for the duration of the function call, and then return it to its original owner when the function call is complete.
But this just seems needlessly convoluted. For a start, as pointed out by #TheUndeadFish in the comments, you could just take a raw Base* as the function argument and call it with std::unique_ptr::get(). As long as the called function doesn't do something silly like call delete on the passed-in pointer or squirrel it away in a static variable for later use then this will work just fine.
Alternatively, if you find raw pointers completely distasteful, you could use a non-owning pointer wrapper, something like the following (untested, but you get the idea):
template <typename T>
class unowned_ptr {
public:
unowned_ptr() = default;
unowned_ptr(const unowned_ptr&) = default;
unowned_ptr& operator=(const unowned_ptr&) = default;
template <typename U>
unowned_ptr(const U* other) : ptr(other) {}
template <typename U>
unowned_ptr(const std::unique_ptr<U>& other) : ptr(other.get()) {}
T* operator->() { return ptr; }
const T* operator->() const { return ptr; }
private:
T* ptr = nullptr;
};
Something very similar to this, std::observer_ptr ("the world's dumbest smart pointer") was proposed for C++17, but I'm not sure of the status.

How can I define a function that takes as a parameter a pointer value of any kind?

I'm implementing for practice a smart pointer class.
I already defined an assignment operator overload that takes another instance of the same class. Now I want to define an overload of this operator, that takes any pointer. So I should be able to do stuff like smartPointer = &someObject; or smartPointer = NULL;, etc.
How can I go about doing that? Should I pass in a void*? Something else?
As a more general question (and I know this is rarely desired): what kind of parameter tells the compiler that any pointer can be passed in?
You can use a template function to make your object allow any pointer to be assigned to it.
template<typename T>
void operator=(T* obj)
{
//Your code here
}
However, its not a smart pointer if you could assign it any raw pointers as it could be assigned to more than one smart pointer object and then there would be a problem while deleting the pointer.
Following may help:
class MySharedPointer
{
public:
template <typename T>
MySharedPointer& operator = (T* p)
{
ptr.reset(p, [](void* p) { delete static_cast<T*>(p); });
return *this;
}
// nullptr is not a pointer, so it should have its own overload.
MySharedPointer& operator = (std::nullptr_t) {
ptr.reset();
return *this;
}
private:
std::shared_ptr<void> ptr;
};
Live example

C++ Access violation reading location

I am trying to make a basic any style class in C++, called object. It compiles successfully, but before anything happens, I get the error: Unhandled exception at 0x008128C1 in object.exe: 0xC0000005: Access violation reading location 0xCCCCCCCC.:
#include <typeinfo>
struct object
{
public:
template < typename T > struct Data
{
public:
Data(T value) : val_(&value){}
Data() : val_(nullptr){}
T* value()
{
return val_;
}
~Data()
{
delete &val_;
}
template < typename Tn > void Swap(Data<Tn>* D)
{
if (std::is_destructible<T>())
{
val_->~T();
}
Tn n_val = (Tn)val_;
std::swap<Tn>(&n_val, D->value());
}
private:
T* val_;
};
struct Inner : Data<void*>
{
template < typename T > void Cast()
{
Swap<T>(new Data<T>((T)NULL));
}
template < typename T > void Cast(const T& value)
{
Swap<T>(new Data<T>(value));
}
};
private:
Inner* Held;
public:
template < typename T > object(const T& value)
{
Held->Cast<T>(value);
}
template < typename T > void operator=(const T& value)
{
Held->Cast<T>(value);
}
template < typename T > void cast()
{
Held->Cast<T>();
}
template < typename T > void cast(const T& value)
{
Held->Cast<T>(value);
}
~object(){ delete Held; }
const void* operator()() const
{
return *Held->value();
}
};
And then in my test file
#include <iostream>
int main()
{
object MyObject = 5;
std::cout << MyObject();
}
Notice that you are doing delete Held; even though you never usednew. You never actually assign anything to Held, so it is uninitialized when you attempt to do Held->Cast<T>(value);. You're going to have to allocate a Held object in some way before you can do that.
You also have a problem your Data struct. Its constructor takes a copy of its argument, and then you store a pointer to that copy. That copy is local to the constructor though and will be destroyed when the constructor ends. The val_ pointer is left pointing at the destroyed object. Not only that, but you then do delete &val_; which attempts to deallocate the object that had automatic storage duration.
You really shouldn't be using new and delete as much as you are, and you would avoid many of the problems you're having.
Your object class is expecting data that has been allocated on the heap. This can be seen since you are accessing that data via a pointer in your object class, swapping its value, and deleting that pointer.
In your main function you are building an object with a literal value of 5. This value has not been allocated on the heap because no allocation was called for (no call to new exists). 5 may have been allocated on the stack, but as a literal it might also be stored in program ROM by the compiler, or any other location where it is dangerous to access the memory address.
The moment your code attempts to modify the data at the address of the literal value 5, you have committed an access violation because you are accessing memory that you are not allowed by your program to modify directly.
To solve this you probably want your object class to allocate a copy of the data being passed to it on the heap that it can then take ownership of and modify and delete at will.