Chad said:
What do you do when you need to map the data structure to a specific
space in memory for a memory-mapped sensor array e.g. the temperature
sensors for levels -2 to 8 in a building?
I am not sure I understood what you mean by this, but in C++ you can do
use a specific numeric address in memory in the style:
int *p= reinterpret_cast<int *>(0x5556);
Of course this is system-specific and if nothing exists there will be a
problem.
You can also do:
// Complete safe and portable
#include <new>
class SomeClass {};
int main()
{
// 1000 bytes in the stack
unsigned char array[1000];
// Create SomeClass object in the beginning of the array
SomeClass *p= new(array)SomeClass;
// Create a second SomeClass object after the first
SomeClass *r= new(array+sizeof(*r))SomeClass;
}
With placement new you can create objects wherever you want. Also you
can define your own versions of placement new, new, delete, new[],
delete[] etc both globals and as parts of a class:
#include <cstddef>
#include <new>
#include <iostream>
class SomeClass
{
static std::size_t occupied;
static unsigned char *buffer;
static const std::size_t MAX_SIZE=5*1024;
public:
~SomeClass() { std::cout<<"Destructor called!\n"; }
void *operator new(std::size_t size)
{
using namespace std;
cout<<"Class member operator new was called!\n";
if(occupied+size>MAX_SIZE)
throw bad_alloc();
occupied+=size;
return buffer+occupied-size;
}
void operator delete(void *p)
{
std::cout<<"Class member operator delete was called!\n";
occupied-=sizeof(SomeClass);
}
};
std::size_t SomeClass:
ccupied=0;
unsigned char *SomeClass::buffer=new unsigned char[MAX_SIZE];
int main()
{
// The member operator new is called implicitly
SomeClass *p=new SomeClass;
delete p;
}
So objects can undertake their memory management. C++ standard library
containers use allocators, with the default one using the default new,
and gives the ability to define your own ones, as also provides
facilities to manage raw memory manually, but I do not know these yet.
Some pieces of Chapter 19 of TC++PL3 on the later:
"Implementers of containers often allocate() and deallocate() objects
one at a time. For a naive implementation of allocate(), this implies
lots of calls of operator new , and not all implementations of operator
new are efficient when used like that. As an example of a user-defined
allocator, I present a scheme for using pools of fixed-sized pieces of
memory from which the allocator can allocate() more efficiently than can
a conventional and more general operator new().
I happen to have a pool allocator that does approximately the right
thing, but it has the wrong interface (because it was designed years
before allocators were invented). This Pool class implements the notion
of a pool of fixed-sized elements from which a user can do fast
allocations and deallocations. It is a low-level type that deals with
memory directly and worries about alignment:"
Some other facilities:
"19.4.4 Uninitialized Memory
In addition to the standard allocator , the <memory > header provides a
few functions for dealing with uninitialized memory. They share the
dangerous and occasionally essential property of using a type name T to
refer to space sufficient to hold an object of type T rather than to a
properly constructed object of type T .
The library provides three ways to copy values into uninitialized space:"
and it talks about uninitialized_copy, uninitialized_fill and uninitial
ized_fill_n.
Then, other facilities are mentioned like
"Algorithms often require temporary space to perform acceptably. Often,
such temporary space is best allocated in one operation but not
initialized until a particular location is actually needed.
Consequently, the library provides a pair of functions for allocating
and deallocating uninitialized space:
template <class T> pair <T *,ptrdiff_t > get_temporary_buffer(ptrdiff_t
); // allocate, don’t initialize
template <class T> void return_temporary_buffer(T *); // deallocate,
don’t destroy
A get_temporary_buffer<X >(n) operation tries to allocate space for n or
more objects of type X .
If it succeeds in allocating some memory, it returns a pointer to the
first uninitialized space and the number of objects of type X that will
fit into that space; otherwise, the second value of the pair is zero.
The idea is that a system may keep a number of fixed-sized buffers ready
for fast allocation so that requesting space for n objects may yield
space for more than n . It may also yield less, however, so one way of
using get_temporary_buffer() is to optimistically ask for a lot and then
use what happens to be available."
And other stuff, it is an entire chapter.
C++ is very customizable, you can define handling functions for cases
that uncaught exceptions occur, define your own behaviour when memory
allocation error occurs instead of the default throwing of bad_alloc etc.
One of C++ design ideals has been
"Leave no room for a lower level language except assembly".