【问题标题】:C++ bi-directional association: object access with smart pointers seems to corrupt the instanceC++ 双向关联:使用智能指针访问对象似乎会破坏实例
【发布时间】:2016-08-11 23:34:56
【问题描述】:

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

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

  • 代码编译时没有错误甚至警告。
  • 我正在使用 qt creator 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;
    }

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

试运行:

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

        itDepartment->show();
        return 0;
    }

【问题讨论】:

  • 包含您提到的崩溃的回溯会很有帮助。
  • 我不知道如何提供。你的意思是堆栈跟踪吗?我知道如何在 eclipse (Java) 中做到这一点,但在 QT 中不知道。但我可以提供以下信息:程序崩溃,试图在 show 方法中访问管理器的名称,这也不再可用。系统声称存在分段错误。
  • 你可以在 gdb 上运行它,但是,如果你想以图形方式运行它,只需在调试模式下运行项目(Qt Creator 上的F5),它会在崩溃时停止。然后,它将显示调用堆栈。更多信息:doc.qt.io/qtcreator/creator-debug-mode.html

标签: qt c++11 shared-ptr bidirectional weak-ptr


【解决方案1】:

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

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

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

有人需要从外部拥有这些对象。他们不能只拥有彼此(循环引用)。

【讨论】:

  • 1.我不明白为什么没有明确的所有者或对象被删除的原因:如果我尝试跟踪共享指针的引用计数器,我总是仍然拥有我在 main.xml 中声明的shared_ptr&lt;Department&gt; itDepartment。那么如何将this 作为参数传递给 Manager 对象来擦除我当前所在的构造函数中的对象呢?因此,实例变量“head”——它是部门对象的一部分——也不应该被删除。
  • 2.我尝试在双方都使用 shared_ptr 来实现整个事情。它仍然没有工作。我也明白,您需要weak_ptr,以便在程序结束时完全擦除对象。如果一侧没有弱指针,则循环引用将阻止删除。
  • 3.无论如何,我尝试了您的建议,但没有奏效。我在 C++ 库的某个地方遇到了编译器错误,因为据说我试图访问某个类的受保护部分?很抱歉 - 当我阅读您的回答时,我非常有希望,但它并没有解决我的问题 - 或者我对它的理解。
  • @schulefant:基本上你不能从this 构造一个shared_ptr。它不与任何其他共享指针共享引用计数,因此它过早地删除了this。
  • 我不得不承认我不明白这背后的原因。 Main 有一个指向同一个对象的共享指针,因此来自 this 的 shared_ptr 应该是第二个共享指针。删除是没有意义的。但我会接受事实就是这样。所以没有 shared_ptr 或weak_ptr 。那么我该如何正确实现与智能指针的双向关联?其次——weak_ptr到底是为了什么——如果不是为了确保在循环(双向)引用的情况下正确删除对象?我认为这是他们的主要目的。
【解决方案2】:

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

我的方法有两个问题:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-24
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多