为什么对象 "a" 中的字符串在多次调用后没有在以下代码中被销毁?

Why isn't the string in object "a" getting destroyed in the following code after being called more than once?

所以,我这里有这段代码,看起来像这样...

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std ;

class A
{
 public:
 char *str ;
 A(char *s)
 {
    cout<< "Construction" << endl ;
    str = new char[strlen(s)+1] ;
    strcpy(str,s) ;
 }
 ~A()
 {
    cout << "Destruction" << endl ;
    delete str ;
 }
};

 void showVal(A a )
 {
   cout << a.str << endl ;

 }
 int main()
 {
   A a("Hello") ;
   showVal(a) ;
   showVal(a) ;
   showVal(a) ;

 }

产生以下输出:

Construction
Hello
Destruction
Hello
Destruction
Hello
Destruction
Destruction

Process returned 0 (0x0)   execution time : 0.015 s
Press any key to continue.

我的问题是,对象 "a" 中 "str" 的值是否应该在首次使用 "showVal" 函数调用后被销毁?为什么在那之后它仍然打印 "Hello" ?任何答案将不胜感激。

showVal 不会破坏对象 a。它有自己的 a 副本,它将被销毁。您看到析构函数是为内部 a 调用的,而不是您在 main().

中初始化的析构函数

无论如何,由于copy elision.

,内部a甚至可能不会被创建和销毁

如果您想进一步了解它,请尝试以下代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std ;

class A
{
 public:
 char *str ;
 A(char *s)
 {
    cout<< "Construction" << endl ;
    str = new char[strlen(s)+1] ;
    strcpy(str,s) ;
 }
 ~A()
 {
    cout << "Destruction " << str << endl ;
    delete[] str ; //Thanks for @Vlad from Moscow
 }
};

 int i=0;
 void showVal(A a ){
   std::string temp="inner a:" + std::to_string(i);
   a.str=new char[temp.size()+1]; //let us forget about memory leaks
   strcpy(a.str,temp.c_str()) ;
   cout << a.str << endl ;
   ++i;
 }
 int main()
 {
   A a("Hello") ;
   showVal(a) ;
   showVal(a) ;
   showVal(a) ;

 }

在这种情况下,showVal 正在创建自己的临时对象 a,因此析构函数调用只是表明它超出了范围,但是如果您想查看构造函数调用,那么添加一个 copy constructor 它将显示调用

 A(const A&){
     cout<<"copy constructor called\n";
 }

程序有未定义的行为。

class 的析构函数不正确

 ~A()
 {
    cout << "Destruction" << endl ;
    delete str ;
    ^^^^^^^^^^^
 }

您必须使用 delete [] 运算符。

 ~A()
 {
    cout << "Destruction" << endl ;
    delete []str ;
    ^^^^^^^^^^^
 }

你还必须定义复制构造函数。否则,隐式定义的复制构造函数使用 member-wise 复制源对象。在这种情况下,两个或多个对象将具有指向同一动态分配内存的指针,这会导致多次尝试删除同一动态分配内存..

如果要定义class至少要用下面的方法包括复制构造函数。

class A
{
 public:
 char *str ;
 A(char *s)
 {
    cout<< "Construction" << endl ;
    str = new char[strlen(s)+1] ;
    strcpy(str,s) ;
 }
 A( const A & a )
 {
    str = new char[strlen(a.str)+1] ;
    strcpy( str, a.str );

 }
 ~A()
 {
    cout << "Destruction" << endl ;
    delete []str ;
 }
};

然后在这些语句中

showVal(a) ;
showVal(a) ;
showVal(a) ;

将创建 class 的临时对象,它们创建自己的数据成员副本 str,分配单独的内存范围。所以这些对象的销毁不会影响原始对象本身。

此处对象 a 通过值传递给函数 showVal。对象 a 不是临时无名对象,因此不会调用 move copy constructor。但是,是的,隐式的 default copy constructor 会被调用。现在,由于您尚未定义 default copy constructor,因此只能看到一个 "Construction"

A a("Hello") ;
showVal(a) ;
showVal(a) ;
showVal(a) ;

如果你现在定义一个default copy constructor

A(const A &obj)
{
   cout<< "Copy Construction" << endl ;
   str = new char[strlen(obj.str) + 1];
   strcpy(str, obj.str);
} 

然后你会看到:

Construction       //due to A a("Hello")
Copy Construction  //due to 1st showVal(a)
Hello              
Destruction        //due to 1st showVal(a)
Copy Construction  //due to 2nd showVal(a)
Hello
Destruction        //due to 1st showVal(a)
Copy Construction  //due to 3rd showVal(a)
Hello
Destruction        //due to 3rd showVal(a)
Destruction        //due to A a("Hello")

现在打印 "Hello" 的原因是 delete str; 会导致未定义的行为(我遇到运行时错误)。正确的做法是 delete [] str;