File: placement.yo

package info (click to toggle)
c%2B%2B-annotations 8.2.0-1
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 11,804 kB
  • ctags: 2,845
  • sloc: cpp: 15,418; makefile: 2,473; ansic: 165; perl: 90; sh: 29
file content (90 lines) | stat: -rw-r--r-- 5,251 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
A remarkable form of operator tt(new) is called the emi(placement new)
 hi(new: placement) operator. Before using placement tt(new) the tthi(memory)
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
 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.
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.

The placement tt(new) operator uses the following syntax (using tt(Type) to
indicate the used data type):
        verb(
    Type *new(void *memory) Type(arguments);
        )
    Here, tt(memory) is a block of memory of at least tt(sizeof(Type)) bytes
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.

    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
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
class must support the following essential operations:
    itemization(
    it() doubling its capacity when all its spare memory (e.g., made available
by tt(reserve)) has been consumed;
    it() adding another tt(string) object
    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:
    verbinsert(RESERVE)(memory/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:
    verbinsert(APPEND)(memory/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.

    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.

    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):
    verbinsert(DESTROY)(memory/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
buffer all the objects' destructors must be called explicitly, as in the
following example:
        verbinsert(CODE)(memory/examples/placement2.cc)