如果使用向量删除叠瓦式自己容器的析构函数中的 char* 成员,则崩溃

Posted

技术标签:

【中文标题】如果使用向量删除叠瓦式自己容器的析构函数中的 char* 成员,则崩溃【英文标题】:crash if deleting char* member in destructor of an imbricated own container using vector 【发布时间】:2012-12-19 19:28:47 【问题描述】:

直到那时我都习惯了,在这里我正在尝试使用向量构建我自己的容器类。我的工作需要它。我正在使用 codeblocks 10.05

class myclass

    public :
      vector<myclass> array;
      char * classname;
      ...

问题是我的类的数据如果在析构函数中没有删除它,它会很好地显示在屏幕上。如果我删除类名,我的函数 show() 会显示奇怪的字符。我想它来自我构建对象的方法和当我将它们作为参数传递时的范围问题。

 myclass::~myclass()
 
    //if(classname) delete [] classname;
 

这就是它在构造函数中的初始化方式:

 myclass::myclass(long lvl = 0, const char name[] = "undefined")
    :ID(++ ID_counter)
    
       level = lvl;

       int namelength = strlen(name);
       classname = new char[namelength + 1];
       strcpy(classname, name);
    

add_content(const myclass & c) 应该复制 c.array 的元素并在 this->array 中“push_back”它们 我发现我们可以将一个没有名称的对象作为参数:mycontainer.add_content(myclass(3,5)); 它有效,但我对它应该具有的范围持怀疑态度

> int main()
>     
>        myclass mycontainer(0);
>        mycontainer.add_content(myclass(3,5));
>     ...

这是完整代码:

#include <vector>
#include <iostream>
using namespace std;

class myclass

  public :

   vector<myclass> array;
   static long ID_counter;
   long ID;
   long level;
   char * classname;

   myclass(int n, long lvl, const char name[]); //push_back n elements, lvl = 0, name = "undefined"
   myclass(long lvl, const char name[]); //lvl = 0, name = "undefined"
   myclass::~myclass();

   void set_level(long lvl); //recursive function, go down the tree
   void add(int n); //push_back n elements
   void add(const myclass & c); //push_back and set back the levels
   void add_content(const myclass & c); //push_back all the c.array[i] and set back the levels

   void show();

   template <typename T> myclass & operator[](const T it) return array[it]; 
;

long myclass::ID_counter = 0;

myclass::myclass(long lvl = 0, const char name[] = "undefined")
:ID(++ ID_counter)

   level = lvl;

   int namelength = strlen(name);
   classname = new char[namelength + 1];
   strcpy(classname, name);

myclass::myclass(int n, long lvl, const char name[] = "undefined")
:ID(++ ID_counter)

   level = lvl;

   int namelength = strlen(name);
   classname = new char[namelength + 1];
   strcpy(classname, name);

   for(int i = 0; i < n; i++) array.push_back( myclass(this->level + 1) );

myclass::~myclass()

   //if(classname) delete [] classname; //there is the point !


void myclass::add(int n = 1)

   for(int i = 0; i < n; i++) array.push_back( myclass(level + 1) );


void myclass::add(const myclass & c)

   array.push_back(c);
   array[array.size() - 1].set_level(level + 1);


void myclass::add_content(const myclass & c)

   for(int i = 0; i < c.array.size(); i++)
   
      array.push_back(c.array[i]);
      array[array.size() - 1].set_level(level + 1);
   


void myclass::set_level(long lvl)

   level = lvl;
   for(int i = 0; i < array.size(); i++) array[i].set_level(level + 1);


void myclass::show()

   cout << "ID : " << ID << "\tLvl : " << level << "\t Classname : " << classname << endl;


int main()

   myclass mycontainer(0); //ground level
   mycontainer.add_content(myclass(3,5)); //the 3 elements level 5 should be reset to 0+1

   mycontainer.show();

   for(int i = 0; i < mycontainer.array.size(); i++)
   
      mycontainer[i].show();

      for(int j= 0; j < mycontainer[i].array.size(); j++)
         mycontainer[i][j].show();
   
   cout << "\ncheckpoint\n";
   system("PAUSE"); //press any key to crash!
   return 0;

这里在析构函数中没有删除*classname,程序会这样:

ID : 1  Lvl : 0  Classname : undefined
ID : 3  Lvl : 1  Classname : undefined
ID : 4  Lvl : 1  Classname : undefined
ID : 5  Lvl : 1  Classname : undefined

checkpoint
Appuyez sur une touche pour continuer...

删除和一些坏字符出现崩溃:

ID : 1  Lvl : 0  Classname : undefined
ID : 3  Lvl : 1  Classname : ░(?
ID : 4  Lvl : 1  Classname : ░(?
ID : 5  Lvl : 1  Classname : ░(?

checkpoint
Appuyez sur une touche pour continuer...

Process returned -1073741819 (0xC0000005)   execution time : 29.651 s
Press any key to continue.

我试图回溯一些关于参数和指针的原则,但这种语言过于经验主义,毫无意义。感谢您的帮助。

【问题讨论】:

也许对 The Rule of Three 的评论可能会阐明这一点,特别是对于 myclass(如果这不是很明显的话)。 【参考方案1】:

您有一个原始指针,但没有定义复制构造函数或赋值运算符。这意味着您将获得自动生成的版本,这是您不想要的。复习三法则。

解决方案 #1:实现适当的复制构造函数和赋值运算符。

解决方案 #2:将 char * classname 更改为 std::string classname。现在自动生成的复制构造函数和赋值运算符就可以工作了。附加好处:你不需要使用new,你不需要使用delete

【讨论】:

+1 我选择解决方案 #2,并至少将其中一个构造函数设为受保护或私有。【参考方案2】:

你应该定义复制构造函数。

我的调试器在这里报错:

for(int i = 0; i < n; i++) array.push_back( myclass(this->level + 1) );

我们在这里看到,myclass 的新对象被创建,然后复制(克隆)到array,然后像任何局部变量一样销毁原始对象。

因此,我们有两个具有相同指针的实例:一个已销毁,另一个在 array 内,由默认复制构造函数创建,它仅复制原始内存。所以,array 中的实例是无效的,因为析构函数已经在上面运行了。

代码

下面的附加构造函数将解决这种情况:

myclass::myclass(const myclass& other) 
:ID(++ ID_counter) 

   level = other.level;

   int namelength = strlen(other.classname);
   classname = new char[namelength + 1];
   strcpy(classname, other.classname);

(上面是所谓的“复制构造函数”;是的,当然你应该遵守“三规则”以避免所有潜在的错误)

【讨论】:

以上是关于如果使用向量删除叠瓦式自己容器的析构函数中的 char* 成员,则崩溃的主要内容,如果未能解决你的问题,请参考以下文章

cpp中的析构函数会自动调用吗?即使析构函数没有提及非动态变量,它们是不是也会被删除?

如何在另一个类的向量中调用派生类的析构函数

为多态基类声明一个虚析构函数

C++ 中的析构函数(与 java 相比)

类中的析构函数是否是虚函数

受保护的与私有的析构函数