mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-11-16 07:48:44 +01:00
Reformulated the placement new operator section
git-svn-id: https://cppannotations.svn.sourceforge.net/svnroot/cppannotations/trunk@540 f6dd340e-d3f9-0310-b409-bdd246841980
This commit is contained in:
parent
c253f075c8
commit
cd5ca4aa1f
5 changed files with 96 additions and 53 deletions
|
@ -70,8 +70,10 @@ includefile(memory/moving.yo)
|
|||
subsect(Move-only classes (C++0x))
|
||||
includefile(memory/moveonly)
|
||||
|
||||
COMMENT(
|
||||
subsect(Defining move special member functions (C++0x, 4.6))
|
||||
To do
|
||||
END)
|
||||
|
||||
subsect(Moving: implications for class design (C++0x))
|
||||
includefile(memory/moveimplications)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
int main()
|
||||
{
|
||||
//CODE
|
||||
using std::string;
|
||||
|
||||
char buffer[3 * sizeof(string)];
|
||||
string *sp = new(buffer) string [3];
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <string>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Strings
|
||||
{
|
||||
|
@ -8,28 +10,43 @@ class Strings
|
|||
size_t d_capacity;
|
||||
|
||||
public:
|
||||
Strings(Strings &&tmp);
|
||||
Strings();
|
||||
~Strings();
|
||||
void reserve(size_t request);
|
||||
void append(std::string const &next);
|
||||
|
||||
private:
|
||||
void reserve();
|
||||
void destroy();
|
||||
};
|
||||
|
||||
Strings::Strings()
|
||||
:
|
||||
d_memory(static_cast<string *>(operator new(sizeof(string)))),
|
||||
d_size(0),
|
||||
d_capacity(1)
|
||||
{}
|
||||
|
||||
void Strings::reserve(size_t request)
|
||||
{
|
||||
if (request <= d_capacity)
|
||||
return;
|
||||
do
|
||||
d_capacity <<= 1;
|
||||
while (d_capacity < request);
|
||||
reserve();
|
||||
}
|
||||
|
||||
//RESERVE
|
||||
void Strings::reserve()
|
||||
{
|
||||
using std::string;
|
||||
|
||||
string *newMemory =
|
||||
static_cast<string *>(memcpy(
|
||||
operator new(d_capacity),
|
||||
d_memory,
|
||||
d_size * sizeof(string)
|
||||
));
|
||||
|
||||
delete d_memory;
|
||||
string *newMemory = static_cast<string *>( // 1
|
||||
operator new(d_capacity * sizeof(string)));
|
||||
for (size_t idx = 0; idx != d_size; ++idx) // 2
|
||||
new (newMemory + idx) string(d_memory[idx]);
|
||||
destroy(); // 3
|
||||
d_memory = newMemory;
|
||||
}
|
||||
//=
|
||||
|
@ -45,12 +62,15 @@ void Strings::append(std::string const &next)
|
|||
|
||||
Strings::~Strings()
|
||||
{
|
||||
using std::string;
|
||||
destroy();
|
||||
}
|
||||
|
||||
//DESTROY
|
||||
for (string *sp = d_memory + d_size; sp-- != d_memory; )
|
||||
void Strings::destroy()
|
||||
{
|
||||
for (std::string *sp = d_memory + d_size; sp-- != d_memory; )
|
||||
sp->~string();
|
||||
|
||||
operator delete(d_memory);
|
||||
//=
|
||||
}
|
||||
//=
|
||||
|
|
|
@ -6,7 +6,7 @@ em(transfer) of the data pointed to by a temporary object to its destination.
|
|||
|
||||
Moving information is based on the concept of anonymous (temporary)
|
||||
data. Temporary values are returned by functions like tt(operator-()) and
|
||||
tt(opertor+(Type const &lhs, Type const &rhs)), and in general by functions
|
||||
tt(operator+(Type const &lhs, Type const &rhs)), and in general by functions
|
||||
returning their results `by value' instead of returning references or
|
||||
pointers.
|
||||
|
||||
|
|
|
@ -3,16 +3,18 @@ A remarkable form of operator tt(new) is called the emi(placement new)
|
|||
header file must have been included.
|
||||
|
||||
With placement tt(new) operator tt(new) is provided with an existing block of
|
||||
memory in which an object or value is initialized. The block of memory should
|
||||
of course be large enough to contain the object, but apart from that no other
|
||||
requirements exist. It is easy to determine how much memory is used by en
|
||||
entity (object or variable) of type tt(Type): the
|
||||
memory in which tt(new) initializes an object or value. The block of memory
|
||||
should of course be large enough to contain the object, but apart from that
|
||||
there are no other requirements. It is easy to determine how much memory is
|
||||
used by en entity (object or variable) of type tt(Type): the
|
||||
ti(sizeof) operator returns the number of bytes required by an tt(Type)
|
||||
entity. Entities may of course dynamically allocate memory for their own use.
|
||||
entity.
|
||||
|
||||
Entities may of course dynamically allocate memory for their own use.
|
||||
Dynamically allocated memory, however, is not part of the entity's memory
|
||||
`footprint' but it is always made available externally to the entity
|
||||
itself. This is why tt(sizeof) returns the same value when applied to
|
||||
different tt(string) objects returning different length and capacity values.
|
||||
different tt(string) objects that return different length and capacity values.
|
||||
|
||||
The placement tt(new) operator uses the following syntax (using tt(Type) to
|
||||
indicate the used data type):
|
||||
|
@ -25,13 +27,14 @@ and tt(Type(arguments)) is any constructor of the class tt(Type).
|
|||
The placement tt(new) operator is useful in situations where classes set
|
||||
aside memory to be used later. This is used, e.g., by tt(std::string) to
|
||||
change its capacity. Calling tt(string::reserve) may enlarge that capacity
|
||||
without making memory beyond the string's length immediately available. But
|
||||
the object itself may access its additional memory and so when information
|
||||
is added to a tt(string) object it can draw memory from its capacity rather
|
||||
than having to perform a reallocation for each single addition of information.
|
||||
without making memory beyond the string's length immediately available to the
|
||||
tt(string) object's users. But the object itself em(may) use its additional
|
||||
memory. E.g, when information is added to a tt(string) object it can draw
|
||||
memory from its capacity rather performing a reallocation for each single
|
||||
character that is added to its contents.
|
||||
|
||||
Let's apply that philosophy to a class tt(Strings) storing tt(std::string)
|
||||
objects. The class defines a tt(char *d_memory) accessing the memory holding
|
||||
objects. The class defines a tt(string *d_memory) accessing the memory holding
|
||||
its tt(d_size) string objects as well as tt(d_capacity - d_size) reserved
|
||||
memory. Assuming that a default constructor initializes tt(d_capacity) to 1,
|
||||
doubling tt(d_capacity) whenever an additional tt(string) must be stored, the
|
||||
|
@ -43,48 +46,65 @@ by tt(reserve)) has been consumed;
|
|||
it() properly deleting the installed strings and memory when a
|
||||
tt(Strings) object ceases to exist.
|
||||
)
|
||||
To double the capacity new memory is allocated, old memory is copied into
|
||||
the newly allocated memory, and the old memory is deleted. This is implemented
|
||||
by the member tt(void Strings::reserve), assuming tt(d_capacity) has already
|
||||
been given its proper value:
|
||||
The private member tt(void Strings::reserve) is called when the current
|
||||
capacity must be enlarged to tt(d_capacity). It operates as follows:
|
||||
First new, raw, memory is allocated (line 1). This memory is in no way
|
||||
initialized with strings. Then the available strings in the old memory are
|
||||
copied into the newly allocated raw memory using placement new (line 2). Next,
|
||||
the old memory is deleted (line 3).
|
||||
verbinsert(RESERVE)(examples/strings.cc)
|
||||
|
||||
The member tt(append) adds another tt(string) object to a tt(Strings)
|
||||
object. A (public) member tt(reserve(request)) ensures that the tt(String)
|
||||
object's capacity is sufficient. Then the placement tt(new) operator is used
|
||||
to install the next string into the raw memory's appropriate location:
|
||||
object. A (public) member tt(reserve(request)) (enlarging tt(d_capacity) if
|
||||
necessary and if enlarged calling tt(reserve())) ensures that the tt(String)
|
||||
object's capacity is sufficient. Then placement tt(new) is used to install the
|
||||
latest string into the raw memory's appropriate location:
|
||||
verbinsert(APPEND)(examples/strings.cc)
|
||||
|
||||
At the end of the tt(String) object's lifetime all its dynamically
|
||||
allocated memory must be returned. This is the responsibility of the
|
||||
destructor, as explained in link(the next section)(DESTRUCTOR). The
|
||||
destructor's full definition is postponed to that section, but its actions
|
||||
when placement tt(new) is involved can be discussed here.
|
||||
At the end of the tt(String) object's lifetime, and during enlarging
|
||||
operations all currently used dynamically allocated memory must be
|
||||
returned. This is made the responsibility of the member tt(destroy), which is
|
||||
called by the class's destructor and by tt(reserve()). More about the
|
||||
destructor itself in link(the next section)(DESTRUCTOR), but the
|
||||
implementation of the support member tt(destroy) is discussed below.
|
||||
|
||||
With placement tt(new) an interesting situation is encountered. Objects,
|
||||
possibly themselves allocating memory, are installed in memory that may or may
|
||||
not have been allocated dynamically, but that is definitely not
|
||||
completely filled with such objects. So a simple tt(delete[]) can't be used,
|
||||
but a tt(delete) for each of the objects that em(are) available can't be used
|
||||
either, since that would also delete the memory of the objects themselves,
|
||||
which wasn't dynamically allocated.
|
||||
not have been allocated dynamically, but that is usually not completely filled
|
||||
with such objects. So a simple tt(delete[]) can't be used. On the other hand,
|
||||
a tt(delete) for each of the objects that em(are) available can't be used
|
||||
either, since those tt(delete) operations would also try to delete the memory
|
||||
of the objects themselves, which wasn't dynamically allocated.
|
||||
|
||||
This peculiar situation is solved in a peculiar way, only
|
||||
encountered in cases where the placement tt(new) operator has been used:
|
||||
memory allocated by objects initialized using placement tt(new) is returned by
|
||||
hi(destructor: explicit call)em(explicitly) calling the object's destructor.
|
||||
The destructor is declared as a member having the class preceded by a tilde as
|
||||
its name, not using any arguments. So, tt(std::string)'s destructor is named
|
||||
tt(~string). The memory allocated by our class tt(Strings) is therefore
|
||||
properly destroyed as follows (in the example assume that tt(using namespace
|
||||
std) was specified):
|
||||
This peculiar situation is solved in a peculiar way, only encountered in
|
||||
cases where placement tt(new) is used: memory allocated by objects initialized
|
||||
using placement tt(new) is returned by
|
||||
hi(destructor: explicit call)
|
||||
em(explicitly) calling the object's destructor. The destructor is
|
||||
declared as a member having as its name the class name preceded by a tilde,
|
||||
not using any arguments. So, tt(std::string)'s destructor is named
|
||||
tt(~string). An object's destructor will only return memory allocated by the
|
||||
object itself and it will em(not) destroy its object.
|
||||
The memory allocated by the em(strings) stored in
|
||||
our class tt(Strings) is therefore
|
||||
properly destroyed by explicitly calling their destructors. Following this
|
||||
tt(d_memory) is back to its initial status: it points to raw memory
|
||||
again. This raw memory is then returned to the common pool by tt(operator
|
||||
delete):
|
||||
verbinsert(DESTROY)(examples/strings.cc)
|
||||
|
||||
So far, so good. All is well as long as we're using but one object. What
|
||||
about allocating an array of objects? Initialization is performed as usual.
|
||||
But as with tt(delete), tt(delete[]) cannot be called when the buffer was
|
||||
allocated statically. Instead, when multiple objects were initialized using
|
||||
the placement tt(new) operator in combination with a statically allocated
|
||||
placement tt(new) in combination with a statically allocated
|
||||
buffer all the objects' destructors must be called explicitly, as in the
|
||||
following example:
|
||||
verbinsert(CODE)(examples/placement2.cc)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue