2009-10-12 8 views
0

Dans mon code, j'ai un vecteur d'objets étudiants.En C++, comment pousser un objet vers un vecteur tout en maintenant un pointeur sur l'objet?

vector<Student> m_students; 

Je veux:

  1. Vérifiez si le vecteur contient tout étudiant d'un certain nom.
  2. Si un tel étudiant n'existe pas, ajoutez-en un nouveau.
  3. Ajoutez des données à l'étudiant de ce nom.

Consultez le code suivant:

// Check to see if the Student already exists. 
Student* targetStudent = NULL; 
for each (Student student in m_students) 
{ 
    if (student.Name() == strName) 
    { 
     targetStudent = &student; 
     break; 
    } 
} 

// If the Student didn't exist, add it. 
if (targetStudent == NULL) 
{ 
    targetStudent = new Student(strName); 
    m_students.push_back(*targetStudent); 
} 

// Add the course info to the Student. 
targetStudent->Add(strQuarter, strCourse, strCredits, strGrade); 

Quand je fais l'appel à m_students.push_back(*targetStudent); il semble que le vecteur « m_students » se termine par une copie de l'objet de l'étudiant que les points « targetStudent » à à ce temps.

La tentative ultérieure d'ajout à targetStudent ne modifie pas l'objet contenu dans le vecteur. Comment puis-je, en commençant par un pointeur vers un objet, ajouter cet objet à un vecteur et accéder ensuite à l'objet qui se trouve dans le vecteur?

Répondre

6

Les conteneurs STL copient les objets qu'ils contiennent. Il n'y a aucun moyen de contourner cela.

Vous pouvez cependant avoir un std::vector<std::shared_ptr<Student> >, ce qui vous permet d'avoir un conteneur de pointeurs intelligents. Pour que cela fonctionne, vos objets doivent tous être attachés au shared_ptr au moment de la construction.

Donc, quelque chose comme:

std::vector<std::shared_ptr<Student> > m_students; 

std::shared_ptr<Student> targetStudent; 
for each (std::shared_ptr<Student> student in m_students) 
{ 
     if (student->Name() == strName) 
     { 
       targetStudent = student; 
       break; 
     } 
} 

// If the Student didn't exist, add it. 
if (!targetStudent) 
{ 
     // creates a new Student and attaches it to smart pointer 
     targetStudent.reset(new Student(strName)); 
     m_students.push_back(targetStudent); 
} 

std::shared_ptr est défini dans l'en-tête <memory> en C++ 11. (Dans TR1, vous pouvez utiliser std::tr1::shared_ptr à la place.) Si vous utilisez C++ 98 sans TR1, ou devez être portable avec lui, vous pouvez utiliser boost::shared_ptr à la place; téléchargement à partir de Boost.

+0

Je recommanderais toujours d'utiliser :: std au lieu de std en faisant référence à l'espace de nom standard. Si quelqu'un crée son propre espace de nom appelé 'std' (pas de niveau supérieur bien sûr, ce ne serait pas légal) alors vous pourriez vous retrouver avec des références ambiguës ou étonnamment résolues à std dans certains cas. – Omnifarious

+0

@Omnifarious Certains peuvent également faire #define std foo'. Vous ne pouvez pas vous préparer à toutes les idioties. –

+0

@quant_dev: Ce n'est pas vraiment parce que quelqu'un peut créer un espace de noms appelé 'std' qui se trouve dans un autre espace de noms. C'est ainsi que vous avez l'habitude de toujours faire référence aux espaces de noms par le nom que vous voulez dire, pas le nom le plus pratique qui semble fonctionner aujourd'hui. – Omnifarious

9

placer le pointeur sur l'objet après qu'il a été inséré dans le vecteur, donc au lieu de

targetStudent = new Student(strName); 
m_students.push_back(*targetStudent);

utilisation

 
m_students.push_back(Student(strName)); 
targetStudent = &m_students.back(); 

Notez également que votre exemple fuites de mémoire, le targetStudent copié dans le vecteur n'est jamais supprimé. En outre, gardez à l'esprit que les pointeurs dans le vecteur deviennent invalides lorsque de nouveaux éléments sont ajoutés (si le vecteur augmente en taille physique et que les éléments doivent être copiés dans le nouveau, tous les pointeurs dans le tampon précédent deviennent invalides) .

+0

Ceci est un changement beaucoup plus simple que la proposition 'shared_ptr', et mieux pour cette seule raison. – MSalters

+0

J'ai relu cette réponse tout à l'heure, et je suis d'accord pour dire que je l'aime aussi. +1 Je suis également d'accord avec MSalters, et même si j'ai écrit la réponse 'shared_ptr', je n'aurais aucun problème si l'OP choisissait cette réponse ou la réponse de Jerry, qui sont toutes deux excellentes. :-) –

+0

+1 C'est en fait très similaire à ce que j'ai fini par faire après que Chris ait fait remarquer que le conteneur fera toujours une copie. Merci également d'avoir signalé la fuite de mémoire (je suis habitué à utiliser les langues collectées pour les ordures et je n'ai pas souvent à penser au tas.) –

5

Vous avez déjà obtenu une réponse raisonnablement directe à votre question. En fonction de ce que vous semblez vouloir accomplir, cependant, il me semble qu'une réponse moins directe pourrait en être une meilleure.

Au moins pendant que je lis votre description, vous avez un nombre d'étudiants uniques, et un certain nombre de cours pour chacun. Quand un étudiant a terminé un cours, vous voulez rechercher l'étudiant.S'ils ne sont pas dans la collection, ajoutez-les. Puis ajoutez les données pour le cours qu'ils ont terminé.

Cela étant, un vecteur me semble être une solution moins qu'idéale. Vous pouvez mettre en œuvre le code d'un couple de différentes manières, mais je serais probablement le faire comme ceci:

struct course { 
    std::string Quarter_, Course_, Credits_, Grade_; 

    using std::string; 
    course(string const &q, string const &c, string const &cr, string const &g) 
     : Quarter_(q), Course_(c), Credits_(cr), Grade_(g) 
    {} 
}; 

std::map<std::string, std::vector<course> > m_students; 

En utilisant, votre séquence entière pour rechercher un étudiant, insérer un nouvel étudiant s'il n'y a pas un par ce nom, puis en ajoutant les travaux de cours à la (nouvelle ou existante) le dossier de l'étudiant travaillerait comme:

m_students[strName].push_back(course(strQuarter, strCourse, strCredits, strGrade)); 

pour en revenir à votre question initiale, les conteneurs standard sont conçus pour fonctionner avec valeurs. Vous leur passez une valeur et ils stockent une copie de cette valeur. Une conséquence de cela est que quelque chose comme push_back(new XXX) est essentiellement toujours une erreur (à peu près une fuite de mémoire garantie). Si vous avez un objet, passez le simplement. Si ce n'est pas le cas, créez simplement un temporaire et transmettez-le. En Java (pour un exemple) voir new XXX partout est routine et presque inévitable. Bien que vous puissiez aussi écrire en C++ de cette façon, ce n'est pas quelque chose que vous devriez attendre en règle générale.

+1

+1 Yay pour avoir une vue d'ensemble! :-D –

+0

+1 Vous avez raison, c'est plus comme ce que je voudrais ... si mon but était de créer une application réelle. Malheureusement, c'était pour une mission d'école et le professeur était très précis sur les structures de données que nous devrions utiliser et il n'y avait pas de place pour ce genre de créativité. –

Questions connexes