2016-04-19 44 views
0

Ich hatte eine bidirektionale Objektverknüpfung mit rohen Zeigern implementiert und es funktionierte ohne Fehler. Dann entschied ich mich zu meinen Code mit Smart-Pointer Refactoring und ganz plötzlich ein String Mitglied (depName) von einer der Klassen (Abteilung) ist nicht mehr accesssible nach Objektinitialisierung. Ich habe überprüft, ob der Parameter korrekt übergeben wird und im Konstruktor ist alles in Ordnung. Das Mitglied erhält den beabsichtigten Wert. Aber danach ist es nicht mehr zugänglich und zusätzlich zu diesem der Code, der zuvor reibungslos lief, stürzt jetzt ab. Ich habe keine Ahnung warum.C++ bidirektionale Zuordnung: Objektzugriff mit intelligenten Zeigern scheint die Instanz zu beschädigen

EDIT: Das gleiche scheint zu dem String-Variablen-Namen für die Manager-Klasse verwendet wird. ENDE BEARBEITEN

  • Der Code kompiliert ohne Fehler oder sogar Warnungen.
  • Ich arbeite mit Qt Creator 3.3.0 und Mingw Compiler 5.4.0
  • Das System behauptet, es gibt einen "Segmentierung Fehler".

Dies ist mein Code (sorry, es ist viel - ich habe es reduziert so viel wie ich es gewagt):

in den Header-Dateien:

class Manager; 
    class Department 
    { 
    private: 
     string depName; 
     shared_ptr<Manager> head; 
     vector <shared_ptr<Manager>> depMembers; 
    public: 
     Department(string depName, shared_ptr<Manager> head); 
     virtual ~Department(); 
     string getDepName() const; 
     void setDepName(const string &value); 
     void addMember(shared_ptr<Manager> newMember); 
     void removeMember(shared_ptr<Manager> who); 
     const shared_ptr<Manager>getHead() const; 
     void setHead(shared_ptr<Manager>value); 
     double sumOfIncome(); 
     void show(); 
    }; 
    //-------------------------------- 
    class Department; 
    class Manager 
    { 
     private: 
      string name; 
      float salary; 
      float bonus;//Bonus ist ein Prozentsatz 
      weak_ptr<Department> myDepartment; 
    //  Department * myDepartment; //With this raw pointer the code still worked* 
     public: 
      Manager(string, float, float); 
      virtual ~Manager(); 
      float income()const ; 
      string toString()const ; 
      double calcBonus() const; 
      shared_ptr<Department> getMyDepartment() const; 
      void setMyDepartment(shared_ptr<Department> abt); 
      float getSalary() const; 
      string getName() const; 
    }; 

in den cpp Dateien : department.cpp

//--------------------------------------------------- 
    Department::Department(string depName, shared_ptr<Manager>head) 
     :depName(depName),head(nullptr) 
    { 
     this->setHead(head); 
     cout << "\nIn constructor parameter depName: " + depName; 
     cout << "\n instancevariable " + this->depName << endl; 
    } 
    //-------------------------------- 
    Department::~Department() 
    {} 
    //-------------------------------- 
    string Department::getDepName() const 
    { 
     return this->depName; 
    } 
    //-------------------------------- 
    void Department::setDepName(const string &value) 
    { 
     depName = value; 
    } 
    //-------------------------------- 
    void Department::addMember(shared_ptr<Manager> newMember) 
    { 
     depMembers.push_back(newMember); 
    } 
    //-------------------------------- 
    void Department::removeMember(shared_ptr<Manager> who) 
    { 
     vector<shared_ptr<Manager>>::iterator itMember = depMembers.begin(); 
     //Iterator must be dereferenced to access data 
     while(*itMember != who){ 
      itMember++; 
     } 
     if(*itMember == who) 
      depMembers.erase(itMember); 
    } 
    //-------------------------------- 
    const shared_ptr<Manager> Department::getHead() const 
    { 
     return head; 
    } 
    //-------------------------------- 
    void Department::setHead(shared_ptr<Manager>value) 
    { 
     if(head != nullptr && head->getMyDepartment()!= nullptr) 
      head->setMyDepartment(nullptr);//department of old head is deleted 

     //new head of department assigned 
     head = value; 
     //bidirektionaler access 
     if(head !=nullptr) 
      head->setMyDepartment(shared_ptr<Department>(this)); 
    } 
    //-------------------------------- 
    double Department::sumOfIncome() 
    { 
     double sum = 0; 
     for(unsigned int i=0; i < depMembers.size(); i++){ 
      sum += depMembers[i]->getSalary() ; 
     } 
     return sum; 
    } 
    //-------------------------------- 
    void Department::show() 
    { 
     cout <<"----------------" << endl; 
     cout << "Department: " << this->depName << " run by " << head->getName()<<endl; 
     cout <<"----------------" << endl; 
     cout << "Members: " << endl; 
     cout <<"----------------" << endl; 
     cout << head->toString() << endl; 
     for(unsigned int i=0; i < depMembers.size() ; i++){ 
      cout <<"----------------" << endl; 
      cout << depMembers[i]->toString()<< endl; 
     } 
     cout <<"----------------" << endl; 
    } 

manager.cpp

//--------------------- 
    float Manager::getSalary() const 
    { 
     return salary; 
    } 
    //---------------------------------- 
    string Manager::getName() const 
    { 
     return name; 
    } 
    //---------------------------------- 
    Manager::Manager(string n, float s, float bon) 
     :name(n),salary(s), bonus(bon) 
    {} 
    //---------------------------------- 
    Manager::~Manager(){} 
    //---------------------------------- 
    float Manager::income()const 
    { 
     return (salary + calcBonus()); 
    } 
    //---------------------------------- 
    string Manager::toString() const 
    { 
     stringstream ss; 
     ss << name << "\n heads the department "; 
     shared_ptr<Department> dep = myDepartment.lock(); 
     if(dep !=nullptr) 
      ss<< dep->getDepName(); 
     else ss << " NONE "; 
     ss << "\nBonus: " << calcBonus(); 
     ss << "\nIncome: " << income(); 
     return ss.str(); 
    } 
    //---------------------------------- 
    double Manager::calcBonus()const 
    { 
     shared_ptr<Department> dep = myDepartment.lock(); 
     if(dep != nullptr) 
      return dep->sumOfIncome()* bonus; 
     else 
      return 0; 
    } 

    //---------------------------------- 
    shared_ptr<Department> Manager::getMyDepartment() const 
    { 
    // if(!meineAbteilung->expired()) 
     return myDepartment.lock(); 
    } 
    //---------------------------------- 
    void Manager::setMyDepartment(shared_ptr<Department> dep) 
    { 
     myDepartment = dep; 
    } 
    //---------------------------------- 

Testlauf:

int main(){ 
     shared_ptr<Department> itDepartment 
      = make_shared<Department>("IT",make_shared<Manager>("Julia", 66066, 0.15)); 

     itDepartment->show(); 
     return 0; 
    } 
+1

Es wäre hilfreich, ein Backtrace des Absturzes, den Sie erwähnt haben, einzuschließen. –

+0

Ich bin mir nicht sicher, wie ich das liefern soll. Bedeutet das eine Stapelverfolgung? Ich weiß, wie man das in Eclipse (Java) macht, aber nicht in QT. Aber ich kann diese Informationen liefern: Das Programm stürzt ab, um den Namen des Managers in der Show-Methode zugreifen, die auch nicht mehr verfügbar ist. Das System gibt an, dass ein Segmentierungsfehler vorliegt. – schulefant

+0

Sie könnten es auf gdb ausführen, aber, wenn Sie es grafisch machen wollen, führen Sie einfach das Projekt im Debug-Modus ('F5' auf Qt Creator) und es wird aufhören, wenn es abstürzt. Dann zeigt es Ihnen den Callstack. Mehr Infos hier: http://doc.qt.io/qtcreator/creator-debug-mode.html –

Antwort

1

Ich könnte John Zwinck 's Antwort als richtig - (danke John!), Aber ich selbst benötigte weitere Informationen, um das Problem zu verstehen, so habe ich beschlossen, meine eigene Antwort zu posten.

Es gibt zwei Probleme mit meinem Ansatz:

  1. shared_ptr sollte nie von Rohzeiger-Variablen gemacht werden, weil jeder Initialisierung eines shared_ptr aus einem rohen Zeiger einen neuen Manager-Objekt erstellen, mit seinen eigene Referenzzahl Ein vorhandenes Manager-Objekt wird nur verwendet, wenn ein neues shared_ptr aus einem der shared_ptr s oder weak_ptr s des vorhandenen Manager-Objekts erstellt wird. (Irgendwie gelang es dem Buch, das ich gelesen habe, dieses wichtige Bit an Information wegzulassen.) this ist ein roher Zeiger, so dass in meinem Konstruktor der Abteilung ein zweites Manager-Objekt erstellt wird. Der Verweiszähler des zweiten Managerobjekts wird auf 0 zurückgesetzt, sobald der Konstruktor verlassen wird - das gerade beendete Objekt wird gelöscht. A shared_ptr sollte daher niemals von this selbst erstellt werden. Es ist ein fataler Fehler.
  2. Da in bidirektionalen Assoziationen muss das Objekt auf einem Zeiger auf sich selbst zu übergeben, die C++ - Bibliothek enthält eine Klasse enable_shared_from_this, die einige Vorlage Magie liefert eine shared_ptr von diesem im Hintergrund zu machen. Leider ist shared_from_this() im Konstruktor nicht verfügbar, da der Objektaufbau nicht abgeschlossen ist. Die Schlussfolgerung ist, dass ein Objekt keine Referenz auf sich selbst ein anderes Objekt im Konstruktor übergeben kann. Es gibt einfach keine Möglichkeit, das zum Laufen zu bringen. Ein Workaround, setHead() direkt nach dem Konstruktor aufrufen, ist der Ausweg. aus Java, wo this aus dem Konstruktor die Norm ist, das ist etwas, das man nur mit ... leben müssen

Neben John Zwinck mich in die richtige Richtung schubsen, fand ich ein Papier aus David Kieras sehr hilfreich: http://www.umich.edu/~eecs381/handouts/C++11_smart_ptrs.pdf

2

Es abstürzt, weil niemand einige der Objekte besitzt. Dies ist eine große rote Fahne:

head->setMyDepartment(shared_ptr<Department>(this)); 

Es gibt so etwas wie enable_shared_from_this aber Sie verwenden sie nicht, so konstruieren ein shared_ptr von this Unsinn ist, und zwar, weil Sie es erlauben, direkt außerhalb des Gültigkeitsbereichs zu gehen weg . Das wird delete this anrufen, die Sie nicht wollen.

Jemand muss diese Objekte von außen besitzen. Sie können sich nicht nur gegenseitig besitzen (Zirkelbezug).

+0

Sorry, ich verstehe, was Sie über Zirkelverweise sagen, aber ... – schulefant

+0

1. Ich verstehe nicht warum Es gibt keinen eindeutigen Besitzer oder warum das Objekt gelöscht wird: Wenn ich versuche, den Referenzzählern für geteilte Zeiger zu folgen, habe ich immer noch die 'shared_ptr itDepartment', die ich in main deklariert habe. Wie kann also die Übergabe von "this" als Argument an das Manager-Objekt das Objekt löschen, in dessen Konstruktor ich mich gerade befinde? Daher sollte auch die Instanzvariable "head" - die Teil des Abteilungsobjekts ist - nicht gelöscht werden. – schulefant

+0

2. Ich habe versucht, das Ganze mit shared_ptr auf beiden Seiten zu implementieren. Es hat immer noch nicht funktioniert. Auch ich habe verstanden, dass Sie weak_ptr brauchen, damit die Objekte am Ende des Programms vollständig gelöscht werden. Ohne einen schwachen Zeiger auf einer Seite würde die Zirkelreferenz dann das Löschen verhindern. – schulefant