2017-10-10 13 views
3

Quelques fois j'ai trébuché sur le scénario où j'ai un conteneur de pointeurs qui doit être copié.C++ Comment copier correctement le conteneur (vecteur) des pointeurs?

Disons que nous avons la hiérarchie de classe suivante:

  • élèves (classe de base)

    • Freshman (sous-classe)
    • Sophmore (sous-classe)
    • junior (sous-classe)
    • Senior (sous-classe)
  • StudentService

La classe StudentService a un champ std::vector<Student*> students et le constructeur suivant:

StudentService::StudentService(std::vector<Student*> students) { 
    // code 
} 

Il ne sera pas correct d'utiliser simplement l'opérateur std::vector::operator= et écrire this->students = students, parce que copiera uniquement les adresses de pointeur et si quelqu'un de l'extérieur supprime les objets pointés par ces pointeurs, la classe StudentService en souffrira.

La solution est en boucle à travers chaque pointeur dans le paramètre students et créer un nouvel objet dynamique, quelque chose comme ceci:

for(int i = 0; i < students.size(); i++) { 
    this->students.at(i) = new Student(*students.at(i)); 
} 

Mais ce n'est pas bonne en raison du fait qu'il va créer seul étudiant objets. Et nous savons qu'un étudiant peut être un étudiant de première année, Sophmore, Junior ou Senior. Alors voici ma question: quelle est la meilleure solution à ce problème?

Je suppose que d'une façon serait de placer un champ ENUM privé à l'intérieur de chaque classe d'étudiants et ont 4 instructions Sinon si vérifier quel type de l'élève est et puis créer un nouvel objet dynamique en fonction de cette façon:

for(int i = 0; i < students.size(); i++) { 
    if(students.at(i).getType() == FRESHMAN) { 
     this->students.at(i) = new Freshman(*students.at(i)); 
    } else if(students.at(i).getType() == SOPHMORE) { 
     this->students.at(i) = new Sophmore(*students.at(i)); 
    } else if { 
    // and so on... 
    } 
} 

Mais cela semble encore assez lourd, alors que suggérez-vous?

+2

Oubliez le vecteur. Comment créer un 'Freshman' à partir d'un pointeur' Student'? C'est la question que vous devez rechercher sur SO. – juanchopanza

+0

http://www.boost.org/doc/libs/1_65_1/doc/html/poly_collection.html –

+0

Les étudiants sont-ils clonés à l'université parce qu'ils sont «utilisés» à différents endroits? –

Répondre

7

Vous recherchez le motif Clone. Ajoutez une fonction virtuelle clone() à Student, qui est substituée dans chaque descendant et crée la copie appropriée. Ensuite, écrivez une copie profonde des conteneurs comme vous l'avez correctement spécifié. Edit: mon hypothèse de travail est que vos classes Freshman, etc. descendent de Student. Sinon, utilisez une variante <> et appliquez un visiteur de copie.

+0

Voici un bon exemple https://stackoverflow.com/questions/5148706/copying-a-polymorphic-object-in-c –

+1

Merci! C'était exactement ce dont j'avais besoin. –

4

propriété Résoudre les problèmes

Si vous jugez partagé les Student -s entre vos modules, vous êtes face à un problème de propriété, et je recommanderais en utilisant le vecteur de std::shared_ptr<Student> -s pour le résoudre. Si vous avez un std::vector<std::shared_ptr<Student>>, vous pouvez le transmettre à qui vous voulez.Le destinataire peut copier le vector à l'aide d'un opérateur d'assignation et, plus tard, les objets qu'il ajoute/supprime n'affectent pas le conteneur d'origine, tout comme vous semblez le désirer.

Résoudre les problèmes clonage

Si vous êtes intéressé par chaque module ayant sa propre copie d'un Student -s vecteur, vous êtes face à un problème de clonage.

Vous pouvez le résoudre en ajoutant la méthode suivante pour vos classes:

class Student { 
    [..] 
    virtual Student * clone() const = 0; // Assuming Student is abstract, otherwise implement as well 
}; 

class Freshman : public Student { 
    [..] 
    virtual Freshman * clone() const { return new Freshman(*this); } 
}; 

// Same for other derived classes... 

Et puis en utilisant std::transform pour copier le vecteur:

// students is the original std::vector<Student *> 
std::vector<Student *> copy(students.size()); 
std::transform(students.begin(), students.end(), copy.begin(), [](Student * s) -> Student * { return s->clone(); }); 

BTW, il est étudiant en deuxième année, pas sophmore ..

+2

Je ne pense pas que c'est ce que veut le PO. Utiliser 'std :: shared_ptr' vous donnera une copie superficielle quand je crois que l'OP veut une copie profonde. – NathanOliver

+2

@NathanOliver, d'après OP: "car cela ne fera que copier les adresses des pointeurs et donc si quelqu'un de l'extérieur supprime les objets pointés par ces pointeurs, alors la classe StudentService en souffrira". Et cela va certainement protéger contre cela. –

+0

Oui, cela le rend sûr, mais maintenant les deux vecteurs pointent toujours vers le même objet. Je crois que le PO veut que les deux vecteurs aient leurs propres objets vers lesquels ils pointent. – NathanOliver