Pour la construction des récipients de toute évidence vous souhaitez utiliser l'un des conteneurs standard (tel qu'un std::vector). Mais c'est un exemple parfait de choses que vous devez considérer lors de votre objet contient des pointeurs.
Si votre objet est un pointeur BRUT, alors vous devez vous rappeler de la règle de 3 (maintenant la règle de 5 en C++11).
- Constructeur
- Destructeur
- Constructeur De Copie
- Opérateur D'Affectation
- Constructeur De Déplacement (C++11)
- Assignation De Déplacement (C++11)
C'est parce que si pas défini le compilateur va générer sa propre version de ces méthodes (voir ci-dessous). Le compilateur a généré versions ne sont pas toujours utiles lorsque vous traitez avec des pointeurs.
Le constructeur de copie est la plus difficile à obtenir correct (c'est non trivial si vous souhaitez fournir la forte exception de garantie). L'opérateur d'Affectation peut être définie en termes de Constructeur de Copie que vous pouvez utiliser le copier et le swap idiome à l'interne.
Voir ci-dessous pour plus de détails sur le minimum absolu pour une classe contenant un pointeur vers un tableau d'entiers.
Sachant qu'il est non trivial de l'obtenir correcte, vous devriez envisager d'utiliser std::vector plutôt qu'un pointeur vers un tableau d'entiers. Le vecteur est facile à utiliser (et développer) et couvre tous les problèmes associés avec des exceptions. Comparer la classe suivante avec la définition d'Un ci-dessous.
class A
{
std::vector<int> mArray;
public:
A(){}
A(size_t s) :mArray(s) {}
};
En regardant votre problème:
A* arrayOfAs = new A[5];
for (int i = 0; i < 5; ++i)
{
// As you surmised the problem is on this line.
arrayOfAs[i] = A(3);
// What is happening:
// 1) A(3) Build your A object (fine)
// 2) A::operator=(A const&) is called to assign the value
// onto the result of the array access. Because you did
// not define this operator the compiler generated one is
// used.
}
Le compilateur a généré opérateur d'affectation est parfait pour presque toutes les situations, mais lorsque les indicateurs sont dans le jeu, vous devez prêter attention. Dans votre cas, il est à l'origine d'un problème en raison de la copie superficielle problème. Vous avez terminé avec deux objets qui contiennent des pointeurs vers le même élément de la mémoire. Quand l'Un(3) est hors de portée à la fin de la boucle il appelle delete [] sur son pointeur. Donc l'autre objet (dans le tableau) contient un pointeur vers la mémoire qui a été renvoyé dans le système.
Le compilateur a généré constructeur de copie, de copies de chaque membre de la variable à l'aide que les membres du constructeur de copie. Pour les pointeurs cela signifie simplement que la valeur du pointeur est copié à partir de l'objet source à la destination de l'objet (d'où la copie superficielle).
Le compilateur a généré opérateur d'affectation; copies de chaque membre de la variable à l'aide que les membres de l'opérateur d'affectation. Pour les pointeurs cela signifie simplement que la valeur du pointeur est copié à partir de l'objet source à la destination de l'objet (d'où la copie superficielle).
Donc le minimum pour une classe qui contient un pointeur:
class A
{
size_t mSize;
int* mArray;
public:
// Simple constructor/destructor are obvious.
A(size_t s = 0) {mSize=s;mArray = new int[mSize];}
~A() {delete [] mArray;}
// Copy constructor needs more work
A(A const& copy)
{
mSize = copy.mSize;
mArray = new int[copy.mSize];
// Don't need to worry about copying integers.
// But if the object has a copy constructor then
// it would also need to worry about throws from the copy constructor.
std::copy(©.mArray[0],©.mArray[c.mSize],mArray);
}
// Define assignment operator in terms of the copy constructor
// Modified: There is a slight twist to the copy swap idium, that you can
// Remove the manual copy made by passing the rhs by value thus
// providing an implicit copy generated by the compiler.
A& operator=(A rhs) // Pass by value (thus generating a copy)
{
rhs.swap(*this); // Now swap data with the copy.
// The rhs parameter will delete the array when it
// goes out of scope at the end of the function
return *this;
}
void swap(A& s) throws ()
{
std::swap(this.mArray,s.mArray);
std::swap(this.mSize ,s.mSize);
}
// C++11
A(A&& src) noexcept
: mSize(0)
, mArray(NULL)
{
(*this) = std::move(src); // Implements in terms of assignment
}
A& operator=(A&& src) noexcept
{
src.swap(*this); // You are moving the state of the src object
// into this one. The state of the src object
// after the move must be valid but indeterminate.
//
// The easiest way to do this is to swap the states
// states of the two objects.
//
// Note: Doing any operation on src after a move
// is risky (apart from destroy) until you put it
// into a specific state. Your object shoudl have
// appropriate methods for this.
//
// Example: Assignment (operator = should work).
// std::vector() has clear() which sets
// a specific state without needing to
// know the current state.
}
}