C++双向关联:使用智能指针访问对象似乎会损坏实例



我有一个用原始指针实现的双向对象关联,它工作起来没有缺陷。然后我决定用智能指针重构我的代码,突然其中一个类(Department)的字符串成员depName。我检查了参数是否正确传递,并且在构造函数中一切正常。成员获得了预期值。但后来它就不再可访问了,除了之前运行顺利的代码,现在也崩溃了。我不知道为什么。

EDIT:在Manager类中使用的字符串变量名似乎也发生了同样的情况。结束编辑

  • 代码编译时没有错误,甚至没有警告
  • 我正在使用qt创建者3.3.0和mingw编译器5.4.0
  • 系统声称存在"分段故障"

这是我的代码(很抱歉,它太多了——我尽我所能减少了它):

头文件中:

    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;
    };

cpp文件中:部门.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;
    }

管理员.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;
    }
    //----------------------------------

试运行:

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

它崩溃了,因为没有人拥有某些对象。这是一个巨大的危险信号:

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

enable_shared_from_this这样的东西,但你没有使用它,所以从this构建shared_ptr是无稽之谈,特别是因为你允许它立即超出范围。这将调用你不想要的delete this

需要有人从外部拥有这些物品。他们不能仅仅拥有彼此(循环引用)。

我可以将John Zwinck的答案标记为正确-(谢谢John!),但我自己需要更多信息来理解这个问题,所以我决定发布自己的答案。

我的方法有两个问题:

  1. shared_ptr永远不应该由原始指针变量生成,因为每次从原始指针初始化shared_ptr都会创建一个具有自己引用计数的新管理器对象。只有根据现有管理器对象的shared_ptrweak_ptr之一创建新的shared_ptr时,才会使用现有管理器。(不知怎么的,我读的书设法省略了这一重要信息。)this是一个原始指针,所以在我的Department构造函数中创建了第二个manager对象。构造函数一离开,第二个管理器对象的引用计数就减少到0——删除了我刚刚创建完的对象。因此,CCD_ 11不应该从CCD_ 12本身创建。这是一个致命的缺陷
  2. 由于在双向关联中,对象需要传递一个指向自身的指针,C++库包含一个类enable_shared_from_this,它提供了一些模板魔法,可以在后台根据该类生成shared_ptr。遗憾的是shared_from_this()在构造函数中不可用,因为对象构建尚未完成。结论是,一个对象不能将引用传递给构造函数中的另一个对象。根本没有办法让它发挥作用解决方法是在构造函数之后直接调用setHead()来自Java,在Java中,从构造函数传递this是常态,这是你必须接受的

除了John Zwinck把我推向正确的方向之外,我发现David Kieras的一篇论文非常有用:http://www.umich.edu/~eecs381/讲义/C++11_smart_ptrs.pdf

相关内容

  • 没有找到相关文章

最新更新