D'autres ont déjà résolu les autres problèmes, alors je vais juste regarder un point: voulez-vous jamais supprimer manuellement un objet.
La réponse est oui. @DavidSchwartz a donné un exemple, mais c'est un exemple assez inhabituel. Je vais donner un exemple qui est sous le capot de ce que beaucoup de programmeurs C ++ utilisent tout le temps: std::vector
(et std::deque
, bien qu'il ne soit pas autant utilisé).
Comme la plupart des gens le savent, std::vector
allouera un plus grand bloc de mémoire lorsque / si vous ajoutez plus d'éléments que son allocation actuelle ne peut en contenir. Quand il le fait, cependant, il dispose d'un bloc de mémoire capable de contenir plus d' objets que ce qui est actuellement dans le vecteur.
Pour gérer cela, ce qui vector
fait sous couvertures est d'allouer de la mémoire brute via l' Allocator
objet (ce qui, sauf indication contraire, signifie qu'il utilise ::operator new
). Ensuite, lorsque vous utilisez (par exemple) push_back
pour ajouter un élément à vector
, le vecteur utilise en interne a placement new
pour créer un élément dans la partie (précédemment) inutilisée de son espace mémoire.
Maintenant, que se passe-t-il quand / si vous erase
un élément du vecteur? Il ne peut pas simplement utiliser delete
- cela libérerait tout son bloc de mémoire; il doit détruire un objet dans cette mémoire sans en détruire d'autres, ni libérer aucun des blocs de mémoire qu'il contrôle (par exemple, si vous erase
5 éléments d'un vecteur, puis immédiatement push_back
5 éléments supplémentaires, il est garanti que le vecteur ne se réallouera pas mémoire quand vous le faites.
Pour ce faire, le vecteur détruit directement les objets de la mémoire en appelant explicitement le destructeur, et non en utilisant delete
.
Si, par hasard, quelqu'un d'autre devait écrire un conteneur en utilisant un stockage contigu à peu près comme un vector
do (ou une variante de cela, comme le fait std::deque
vraiment), vous voudrez presque certainement utiliser la même technique.
Par exemple, considérons comment vous pourriez écrire du code pour un tampon circulaire circulaire.
#ifndef CBUFFER_H_INC
#define CBUFFER_H_INC
template <class T>
class circular_buffer {
T *data;
unsigned read_pos;
unsigned write_pos;
unsigned in_use;
const unsigned capacity;
public:
circular_buffer(unsigned size) :
data((T *)operator new(size * sizeof(T))),
read_pos(0),
write_pos(0),
in_use(0),
capacity(size)
{}
void push(T const &t) {
// ensure there's room in buffer:
if (in_use == capacity)
pop();
// construct copy of object in-place into buffer
new(&data[write_pos++]) T(t);
// keep pointer in bounds.
write_pos %= capacity;
++in_use;
}
// return oldest object in queue:
T front() {
return data[read_pos];
}
// remove oldest object from queue:
void pop() {
// destroy the object:
data[read_pos++].~T();
// keep pointer in bounds.
read_pos %= capacity;
--in_use;
}
~circular_buffer() {
// first destroy any content
while (in_use != 0)
pop();
// then release the buffer.
operator delete(data);
}
};
#endif
Contrairement aux conteneurs standard, cela utilise operator new
et operator delete
directement. Pour une utilisation réelle, vous souhaitez probablement utiliser une classe d'allocateur, mais pour le moment, cela ferait plus de distraire que de contribuer (IMO, en tout cas).