【问题标题】:Memory management for 2 "interconnected" C++ classes2 个“互连”C++ 类的内存管理
【发布时间】:2017-04-19 14:59:58
【问题描述】:

我有一个班级Branch 和一个班级Tree。类的声明如下:

class Branch{

private:

  map<string, double> properties;

  vector<Branch *> parent;
  vector<Branch *> children;

  int hierarchy;

  public: 
  ...

}

class Tree{

private:

  /* Tree is a vector of pointers to branches. */
  vector<Branch*> tree_branches;

  map<int,int> Hierarchy_distribution;

public:
...

}

1) 如果我理解得很好,Tree 类的唯一属性是vectormap,因此无需声明析构函数、复制赋值运算符和复制构造函数,因为内存是在矢量和地图模板“内部”管理的。

2) 我使用 Python 代码中的这些类(我使用 cython 在 C++ 和 Python 之间进行接口),并且我所做的所有操作都是通过树对象执行的。我认为因为我“使用”的分支包含在树对象中(具有良好的内存管理),所以我不需要为分支类声明析构函数、复制构造函数和复制赋值运算符。但是我遇到了一些问题,我认为我有内存泄漏。

如果这可能导致内存泄漏,有人可以确认我吗?如果是在vector&lt;int&gt; 中存放int hierarchy 避免声明析构函数和公司?

编辑

存放在树中的分支是在Tree 类的方法中创建的。它看起来像:

Tree::addBranch(){
  Branch* branch2insert=new Branch();
  tree_branches.push_back(branch2insert);
}

作为局部变量,branch2insert 在作用域的末尾被破坏了吗?我需要写delete branch2insert;吗?有人知道我指向的Branch 对象在哪里生活吗?

除了Tree 类方法之外,我从不分配资源时,我仍然不明白为什么我需要确保内存管理......

这整件事在我的脑海里变得非常混乱

编辑 2:示例

branch.h

#ifndef BRANCH_H_
#define BRANCH_H_

#include <memory>
#include <string>
#include <vector>
#include <map>
using namespace std;

class Branch{

private:

      vector<Branch*> parent;
      vector<Branch*> children;

public:

      Branch();

      bool hasParent();
      bool hasParent(Branch* test);
      void addParent(Branch* p);
      void removeParent();
      Branch* getParent();

      bool hasChildren();
      bool hasChild(Branch*test);
      void addChild(Branch*ch);
      void removeChild(Branch*ch);
      void removeChildren();
      void removeDescendants();
      vector<Branch*> getBrothers();


};

#endif

tree.h

#ifndef TREE_H_
#define TREE_H_

#include <vector>
#include <map>
#include"branch.h"

using namespace std;

class Tree{

private:

      vector<Branch*> tree_branches;

public:

      Tree();

      int getNumberOfBranches();

      Branch* getBranch(int branch_index); /* Returns branch at index. */
      int getIndex(Branch* branch); /* Returns the index of branch. */

      int getLastDescendantIndex(int ancestor_index); /* Returns index of the last descendant of branch at ancestor index. */
      int getParentIndex(int child_index); /* Returns index of the parent of branch at child_index. */
      vector<int> getBrothersIndex(int branch_index); /* Returns indexes of the brothers of branch at branch_index. */

      void addBranch(int parent_index); /* Adds branch without initializing its properties. */
      void removeBranch(int branch_index); /* Removes branch at branch_index and all its descendants. */

};

#endif

branch.cpp

#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <sstream>
#include <map>
#include <stdio.h>

using namespace std;

#include "branch.h"

Branch::Branch():parent(vector<Branch*>()),children(vector<Branch*>())
        {
        }

bool Branch::hasParent(){
  if(parent.size()==0)
        return false;
    else
    return true;
}

bool Branch::hasParent(Branch* test){
  bool ret = false;
  for(vector<Branch*>::iterator it=parent.begin();it!=parent.end();it++){//traversing parent vector
    if((*it)==test){//if test belong to parent vector
      ret = true;
      break;
    }
  }
  return ret;
}

void Branch::addParent(Branch* mom){
  if(parent.size()==0){//if a branch hasn't a parent, multiple parents aren't allowed in a tree-network
    if(hasParent(mom)==false){//double checking if mom isn't already a parent
            parent.push_back(mom);//adding mom to parent vector
      }
    else{
      cout << "Branch::addParent Error: trying to add a parent twice.\n";
    }
  }
  else{
    cout << "Branch::addParent Error: trying to add a parent to a branch that already has one.\n";
  }
}

void Branch::removeParent(){
    if(this->hasParent()==true){//if this branch has a parent
      vector<Branch*>::iterator it=parent.begin();
      parent.erase(it);//erase it (it is the first and only element of the vector)

    }
    else{
      cout << "Removing the trunk.\n";
    }
}

Branch* Branch::getParent(){
  return parent[0];
}

bool Branch::hasChildren(){
  if(children.size()==0)
        return false;
    else
    return true;
}

bool Branch::hasChild(Branch* test){
  bool ret = false;
  for(vector<Branch*>::iterator it=children.begin();it!=children.end();it++){
    if((*it)==test){
      ret = true;
      break;
    }
  }
  return ret;
}

void Branch::addChild(Branch* ch){
  if(hasChild(ch)==false){
        children.push_back(ch);
        ch->addParent(this); // PARENTHOOD LINK ESTABLISHED IN ADD CHILD. ONLY
                         // NEEDED ONCE.
    }
  else{
        cout << "Branch::addChild Error: trying to add a child but the child has been already added.\n";
  }
}

void Branch::removeChild(Branch* ch){
  if(hasChild(ch)==true){
        for(vector<Branch*>::iterator it=children.begin();it!=children.end();it++){
            if((*it)==ch){
                children.erase(it);
                break;
            }
        }
    }
  else{
      cout << "Branch::removeChild Error: trying to remove a child that doesn't exist.\n";
  }
}

void Branch::removeChildren(){
  if(this->hasChildren()==true){
    children.erase(children.begin(),children.end());
  }
  else{
    cout << "Branch::removeChildren Error: trying to remove all the children of a branch but tha branch hasn't any.\n";
  }
}

void Branch::removeDescendants(){
    if (this!=NULL){
      if(this->hasChildren()==true){
        for(vector<Branch*>::iterator it=children.begin();it!=children.end();it++){
          (*it)->removeDescendants();
        }
      removeChildren();
      }
      for(vector<Branch*>::iterator it=children.begin();it!=children.end();it++){
        (*it)->removeParent();
      }
    }
}

vector<Branch*> Branch::getBrothers(){
  vector<Branch*> brothers;
  vector<Branch*> potential_brothers;
  if (parent.size()!=0){
    potential_brothers=parent[0]->children;
    for (vector<Branch*>::iterator it=potential_brothers.begin();it!=potential_brothers.end();it++){
      if ((*it)!=this){
        brothers.push_back((*it));
      }
    }
  }
  return brothers;
}

tree.cpp

#include <iostream>
#include <sstream>
#include <stdio.h>
#include <string>
#include <vector>
#include <map>
#include <algorithm>

using namespace std;

#include "tree.h"
Tree::Tree():tree_branches(vector<Branch*>()){
    Branch* trunk=new Branch();
    tree_branches.push_back(trunk);
}

int Tree::getNumberOfBranches(){
    int number_of_branches=tree_branches.size(); //retrieving size of vector
    return number_of_branches;
}

Branch* Tree::getBranch(int index){

      unsigned int u_index=index;
      return tree_branches.at(u_index); // returning branch at index of the tree vector

}


int Tree::getIndex(Branch* branch){
    int index=0;
    for(vector<Branch*>::iterator it=tree_branches.begin();it!=tree_branches.end();it++){
      if((*it)==branch){
        return index;
      }
      index++;
    }
    cout << "Tree::getIndex Error: trying to get the index of a branch we can't find. Returning 0.\n";
    return 0;
}


int Tree::getLastDescendantIndex(int ancestor_index){

      if(tree_branches.at(ancestor_index)->hasChildren()==false){// if it is a leaf
        return ancestor_index;
      }

      if(ancestor_index==0){// if it is the trunk
        int N=tree_branches.size();
        int last_descendant_index=N-1;
        return last_descendant_index;
      }

      vector<int> brothers_indexes=Tree::getBrothersIndex(ancestor_index);

      for(vector<int>::iterator it=brothers_indexes.begin();it!=brothers_indexes.end();it++){

        int brother_index=(*it);
        if(brother_index>ancestor_index){

          int last_descendant_index=brother_index-1;
          cout << "The last descendant of" << ancestor_index << " is "<<last_descendant_index<<"\n";
          return last_descendant_index;

        }
      }
      int parent_index=Tree::getParentIndex(ancestor_index);
      Tree::getLastDescendantIndex(parent_index);
}

int Tree::getParentIndex(int child_index){

      if(child_index==0){ //if considered branch is the trunk
        cout << "Tree::getParentIndex: the trunk hasn't a parent. Returning -1.\n";
        return -1;
      }

      unsigned int u_child_index=child_index;
      Branch* parent=tree_branches.at(u_child_index)->getParent(); //retrieving the parent of the considered branch
      int parent_index=Tree::getIndex(parent);
      return parent_index; //returning parent index
}


vector<int> Tree::getBrothersIndex(int branch_index){
    vector<int> brothers_index;

    Branch* this_branch=Tree::getBranch(branch_index);//retrieving the branch from the index
    vector<Branch*> brothers=this_branch->getBrothers();//retrieving branch's brothers

    for(vector<Branch*>::iterator it=brothers.begin();it!=brothers.end();it++){ //traversing a vector containing the brothers of the consideered branch
      int this_index=Tree::getIndex(*it); //retrieving index of a brother
      brothers_index.push_back(this_index); //stocking the index in a vector
    }

    return brothers_index; //returning the vector containing the index of all brothers
  }


void Tree::addBranch(int parent_index){

      unsigned int u_parent_index=parent_index;
      Branch* mom=tree_branches.at(u_parent_index);//getting futur parent
      Branch* branch2insert=new Branch();//creation of branch to insert
      mom->addChild(branch2insert);//setting family relationship

      vector<Branch*>::iterator begin=tree_branches.begin();//creating iterators to manipulate vector elements
      unsigned int inserting_position=u_parent_index+1;//initializing inserting_position
      tree_branches.insert(begin+inserting_position,branch2insert);//inserting new branch

}


void Tree::removeBranch(int branch_index){

      int N=tree_branches.size();

      unsigned int u_branch_index=branch_index;
      Branch* branch2remove=tree_branches.at(u_branch_index);
      branch2remove->removeParent(); //removing parenthood link, if branch2remove is the trunk nothing will be done

      if(branch_index!=0){//removing childhood link between parent and branch_index
        Branch* branch2removeParent=branch2remove->getParent();
        branch2removeParent->removeChild(branch2remove);
      }

      int last_descendant_index=Tree::getLastDescendantIndex(branch_index);
      cout<<"The branch to remove is "<<branch_index<<", its last descendant index is "<<last_descendant_index<<", the size of the tree is "<<N<<".\n";

      branch2remove->removeDescendants();//removing family links for all descendents

      if(last_descendant_index==N-1){
        vector<Branch*>::iterator begin=tree_branches.begin();
        tree_branches.erase(tree_branches.begin()+u_branch_index,tree_branches.end());
      }

      else{
        vector<Branch*>::iterator begin=tree_branches.begin();
        unsigned int u_last_descendant_index=last_descendant_index;
        tree_branches.erase(tree_branches.begin()+u_branch_index,tree_branches.begin()+u_last_descendant_index+1);//removing the considered branch and its descendents from the tree vector
      }
}

test.cpp

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <typeinfo>
#include <vector>
#include <map>
#include <string>
#include "tree.h"



using namespace std;

int main(){

  Tree tree;
  tree=Tree();

  for(int i=0;i<5;i++){
    int N=tree.getNumberOfBranches();
    for(int j=0;j<N;j++){
      tree.addBranch(j);
      tree.addBranch(j);
    }
  }

  tree.removeBranch(21);
  tree.removeBranch(30);
  tree.removeBranch(50);

  return 0;
}

编译:

g++ -Wall -pedantic -c

【问题讨论】:

  • vector&lt;Branch *&gt; 将正确管理它所拥有的Branch *,但它不会管理这些指针指向的Branches。创建指针指向的Branches 的人负责摆脱它们,而不是向量。您可以改用vector&lt;Branch&gt; 来使向量负责分支。
  • 使用vector 不会神奇地免除您管理内存的麻烦。如果parentchildren中的任何一个指针指向堆上的对象,最后还是需要释放它们。
  • 请同时显示您的Tree/Branch 初始化代码。
  • @BenSteffan 我不能把整个初始化,因为它太大了。现在我没有时间研究一个最小的例子。
  • 哪个类拥有您拥有所有这些指针的Branch 对象?您的代码如何知道它们的生命周期何时结束?如果Tree 拥有它的分支,那么它必须delete 它们。

标签: c++ class


【解决方案1】:

1) 如果我理解得很好,Tree 类的唯一属性是向量和映射这一事实使得不必声明析构函数、复制赋值运算符和复制构造函数,因为内存是托管的“内”矢量和地图模板。

你错了,因为在内部向量管理的内存只是分支地址的存储(Branch *)。当你插入一些分支时,你会调用类似tree_branches.push_back(new Branch) 的东西,不是吗?因此,您将需要以某种方式调用相应的delete,否则树的默认析构函数不会处理调用Branches 的析构函数。

您的代码的快速修复可能是从Branch * 切换到shared_ptr&lt;Branch&gt;。这样,当tree_branches 向量被破坏时,shared_ptr 也将被破坏,当任何Branch 的最新shared_ptr 被破坏时,指向的Branch 也将被破坏。

将 int 层次结构存放在向量中会避免声明析构函数和公司吗?

它会改变jack squat,因为Branches默认析构函数仍然不会被调用,所以Branches成员的析构函数永远不会被调用。

【讨论】:

  • 谢谢 Davide,我觉得你的回答很清楚。我确实使用push_back(new Branch())insert。我会尝试共享指针并告诉你。我还需要为Tree 类声明析构函数吗?您还会在分支类中将Branch* 更改为shared_ptr&lt;Branch&gt;(我的意思是对于vector children)吗?
  • @bengo 如果你使用shared_ptr&lt;Branch&gt;,你不需要你自己的析构函数。容器的默认析构函数会销毁共享指针,如果你正确分配了对象(使用make_shared!),那么该对象将在其最后一个共享指针被销毁时被销毁。
  • 在这种情况下切换到shared_ptr 时应小心。很有可能创建一个循环引用,导致没有任何内容被删除。如果您同时持有parents 和children,则会发生这种情况shared_ptr
  • @SeanCline 我不这么认为。最终,代码似乎暗示所有分支都由树持有。然后当 Tree 向量成员被销毁时,所有分支将迟早(从树)或稍后(从另一个“超越”它们的分支)被销毁;前提是分支不是其自身的父级或子级,在想到的任何有意义的应用程序中都应检查和避免。
  • 确实,一个分支不能是它自己的父或子。所有现有的分支都包含在树中,可以通过遍历向量条目或使用分支之间的家庭关系来遍历。
【解决方案2】:

终于没有内存泄漏了。感谢@Yunnosch 提到 Valgrind,我不知道存在这种工具。

@Davide 我接受了您的回答,因为它提供了我所做的替代方案,即使我的代码中的内存管理显然没有问题。

我发布的代码在函数 Tree::getLastDescendantIndex 中有问题。

【讨论】:

  • 您的测试将永远不会遇到泄漏(如果使用 Branch* 并且永远不会删除)只是因为 1)您在示例中仅使用一棵树 2)您的树的范围与主要( ) 体,因此泄漏实际上只存在于测试的最后一条指令中。
  • “泄漏仅存在于测试的最后一条指令中”是什么意思?为什么使用更多的树会有问题?
猜你喜欢
  • 1970-01-01
  • 2011-06-10
  • 2012-03-19
  • 2010-09-06
  • 2018-12-17
  • 1970-01-01
  • 2014-10-24
相关资源
最近更新 更多