Particularly, why do we have
template<typename T, typename A = allocator<T>>
class vector
{
A alloc;
//...
};
instead of
template<typename T>
class vector
{
allocator<T> alloc;
//...
};
I saw this in a C++ manual and it confused me quite a bit. What other kinds of allocators could one possibly want/need?
Because this would work with only one allocator - standard one. But what if you want to allocate memory differently? For example, you might want to use shared memory, or file-backed memory, or anything else.
This is the whole point of having allocators - to allow user to customize the way memory is going to be allocated and freed.
Related
I'm implementing a super simple container for long term memory management, and the container will have inside an array.
I was wondering, what are the actual implications of those two approaches below?
template<class T, size_t C>
class Container
{
public:
T objects[C];
};
And:
template<class T>
class Container
{
public:
Container(size_t cap)
{
this->objects = new T[cap];
}
~Container()
{
delete[] this->objects;
}
T* objects;
};
Keep in mind that those are minimal examples and I'm not taking into account things like storing the capacity, the virtual size, etc.
If the size of the container is known at compile time, like in first example, you should better use std::array. For instance:
template<class T, size_t C>
class Container
{
public:
std::array<T, C> objects;
};
This has important advantages:
You can get access to its element via std::get, which automatically checks that the access is within bounds, at compile time.
You have iterators for Container::objects, so you can use all the routines of the algorithm library.
The second example has some important drawbacks:
You cannot enforce bounds-check when accessing the elements: this can potentially lead to bugs.
What happens if new in the constructor throws? You have to manage this case properly.
You need a suitable copy constructor and assignment operators.
you need a virtual destructor unless you are sure that nobody derives from the class, see here.
You can avoid all these problems by using a std::vector.
In addition to #francesco's answer:
First example
In your first example, your Container holds a C-style array. If an instance of the Container is created on the stack, the array will be on the stack as well. You might want to read heap vs stack (or similar). So, allocating on the stack can have advantages, but you have to be careful with the size you give to the array (size_t C) in order to avoid a stack overflow.
You should consider using std::array<T,C>.
Second example
Here you hold a pointer of type T which points to a C-style array which you allocate on the heap (it doesn't matter whether you allocate an instance of Container on the stack or on the heap). In this case, you don't need to know the size at compile time, which has obvious advantages in many situations. Also, you can use much greater values for size_t C.
You should consider using std::vector<T>.
Further research
For further research, read on stack vs heap allocation/performance, std::vector and std::array.
Lets say I have a function to get data into an std vector:
void getData(std::vector<int> &toBeFilled) {
// Push data into "toBeFilled"
}
Now I want to send this data to another function, that should free the data when finished:
void useData(int* data)
{
// Do something with the data...
delete[] data;
}
Both functions (getData and useData) are fixed and cannot be changed. This works fine when copying the data once:
{
std::vector<int> data;
getData(data);
int *heapData = new int[data.size()];
memcpy(heapData, data.data(), data.size()*sizeof(int));
useData(heapData);
data.clear();
}
However, this memcpy operation is expensive and not really required, since the data is already on the heap. Is it possible to directly extract and use the data allocated by the std vector? Something like (pseudocode):
{
std::vector<int> data;
getData(data);
useData(data.data());
data.clearNoDelete();
}
Edit:
The example maybe doesn't make too much sense, since it is possible to just free the vector after the function call to useData. However, in the real code, useData is not a function but a class that receives the data, and this class lives longer than the vector...
No.
The API you're using has a contract that states it takes ownership of the data you provide it, and that this data is provided through a pointer. This basically rules out using standard vectors.
Vector will always assuredly free the memory it allocated and safely destroy the elements it contains. That is part of its guaranteed contract and you cannot turn that off.
You have to make a copy of the data if you wish to take ownership of them... or move each element out into your own container. Or start with your own new[] in the first place (ugh) though you can at least wrap all this in some class that mimics std::vector and becomes non-owning.
Here's a horrible hack which should allow you to do what you need, but it relies on Undefined Behaviour doing the simplest thing it can. The idea is to create your own allocator which is layout-compatible with std::allocator and type-pun the vector:
template <class T>
struct CheatingAllocator : std::allocator<T>
{
using typename std::allocator<T>::pointer;
using typename std::allocator<T>::size_type;
void deallocate(pointer p, size_type n) { /* no-op */ }
// Do not add ANY data members!!
};
{
std::vector<int, CheatingAllocator<int>> data;
getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
useData(data.data());
// data actually uses your own allocator, so it will not deallocate anything
}
Note that it's as hacky and unsafe as hacks go. It relies on the memory layout not changing and it relies of std::allocator using new[] inside its allocate function. I wouldn't use this in production code myself, but I believe it is a (desperate) solution.
#TonyD correctly pointed out in the comments that std::allocator is quite likely to not use new[] internally. Therefore, the above would most likely fail on the delete[] inside useData(). The same #TonyD also made a good point about using reserve() to (hopefully) prevent reallocation inside getData(). So the updated code would look like this:
template <class T>
struct CheatingAllocator : std::allocator<T>
{
using typename std::allocator<T>::pointer;
using typename std::allocator<T>::size_type;
pointer allocate(size_type n) { return new T[n]; }
void deallocate(pointer p, size_type n) { /* no-op */ }
// Do not add ANY data members!!
};
{
std::vector<int, CheatingAllocator<int>> data;
data.reserve(value_such_that_getData_will_not_need_to_reallocate);
getData(reinterpret_cast<std::vector<int>&>(data)); // type pun, `getData()` will use std::allocator internally
useData(data.data());
// data actually uses your own allocator, so it will not deallocate anything
}
Is it necessary for me to call allocator.construct() for an array of primitive types allocated using an arbitrary allocator, as in the code listing below? The class doesn't require the allocated memory to be initialized to any particular value, so it seems to me that calling allocator.construct() with a newly-allocated chunk of memory would be unnecessary. Is there any danger in not calling this method, given that the array always consists of primitive types?
template <class T, template <class> class Allocator = std::allocator>
class foo
{
public:
typedef Allocator<T> allocator;
typedef typename allocator::pointer pointer;
private:
unsigned size_;
allocator alloc_;
pointer t_;
public:
foo(unsigned n) throw(std::bad_alloc) : size_(n), alloc_(),
t_(alloc_.allocate(n))
{
// Note that I do not call alloc_.construct() here.
}
~foo() { alloc_.deallocate(t_, size_); }
};
Yes. The allocator is free to impose whatever custom book-keeping it wants, including the number of existing objects. There is no guarantee at all that it simply does new (memory) T(...). And in addition, it would be a very nasty surprise for a person to change your code so that it's no longer just primitives and then find it randomly breaks sometime later.
It seems to be the month of C++ templates for me...
I have a SecureString. SecureString looks just like a std::string, except it uses a custom allocator which zeroizes on destruction:
class SecureString
{
public:
typedef std::basic_string< char, std::char_traits<char>, zallocator<char> > SecureStringBase;
typedef zallocator<char>::size_type size_type;
static const size_type npos = static_cast<size_type>(-1);
....
private:
SecureStringBase m_base;
};
The full code for SecureString can be found at http://code.google.com/p/owasp-esapi-cplusplus/source/browse/trunk/esapi/util/SecureString.h; and the code for the allocator can be found at http://code.google.com/p/owasp-esapi-cplusplus/source/browse/trunk/esapi/util/zAllocator.h.
Currently, we have a swap defined that takes a std::string as an argument:
void SecureString::swap(std::string& str)
{
SecureStringBase temp(str.data(), str.size());
m_base.swap(temp);
str = std::string(temp.data(), temp.size());
}
I feel like I'm missing an opportunity in swap because the underlying types only differ by allocators. Can anyone see a way to avoid the temporary? Is it possible to use rebind to make this run faster?
EDIT: SecureString::swap(std::string& str) is now gone. Reference to the function in this thread has been left in place for posterity.
Jeff
Unfortunately... no.
This is not what rebind is for. rebind is used because an allocator is meant to allocate objects of one type, and one type only (std::allocator<T>) in the STL.
However, there is a trick. When you instantiate std::list<T, std::allocator<T>> for example, then the allocator does not have to allocate Ts, it has to allocate some internal structure instead like __list_node<T>, and that is when rebind is put to use, it creates a new allocator, sibling of the precedent (they differ only by the template parameter and likely share the same memory pool under the covers).
In your case however, your allocator and the std::string allocator are different, and thus they cannot exchange memory. So you have to do a copy.
You can optimize the void swap(SecureString&, SecureString&) operation, but not this one.
One question: why not typedef std::string<char, SecureAllocator<char>> SecureString; ?
This is a followup from stl allocator, copy constructor of other type, rebind
I am using std::map and want a custom allocator that can reuse the storage for the internal nodes. The items being stored are pointers, so I'm not talking about reusing them, just the internal allocations for the map.
The main requirements is that different instances of the map cannot share an object pool, it must be unique per instance.
I don't need a complete solution, I'd just like an idea of how to cope with the required copy constructor that takes allocators of a different type. I don't know how to manage the internal memory in that case.
As you point out in the other question, the allocators shouldn't have any state. Use thread-local storage or a pointer in each allocator object to the memory pool: the allocators merely become a type-specific interface to that pool.
struct MemoryPool {
// none of this depends on the type of objects being allocated
};
template<class T>
struct MyAllocator {
template<class U> struct rebind { typedef MyAllocator<U> other; };
MemoryPool *_pool; // copied to any allocator constructed
template<class U>
MyAllocator(MyAllocator const &other) : _pool(other._pool) {}
// allocate, deallocate use _pool
// construct, destruct deal with T
};