关于C++析构函数

zha*_*eng 7 c++ destructor

每个人
我都有一些java经验,并且是c ++的初学者.
belw是我的代码,它的输出是

0 1 2 3 4 5 6 7 8 9
destructor ---s1
8791616 8785704 2
destructor ---s1
Run Code Online (Sandbox Code Playgroud)

我预计输出是

0 1 2 3 4 5 6 7 8 9
destructor ---abc
0 1 2
destructor ---s1
Run Code Online (Sandbox Code Playgroud)

我无法理解为什么析构函数释放第一个对象的资源.如何打印我预期的输出?

#include <iostream>
using namespace std;
class Sequence{
    public:
        Sequence(int count=10,string name = "abc");
        void show();
        ~Sequence();

        int* _content;
        int _count;
        string _name;

};

Sequence::Sequence(int count,string name){
    _count = count;
    _content=new int[count];
    _name = name;
    for(int i=0;i<count;i++){
        _content[i]=i;
    }
}

Sequence::~Sequence(){
    cout << "destructor ---"<<_name<<endl;
    delete [] _content;
}

void Sequence::show(){
    for(int i=0;i<_count;i++)
        cout<<_content[i]<<" ";
    cout<<endl;
}

int main(){
    Sequence s1 = Sequence();
    s1.show();
    s1 = Sequence(3,"s1");
    s1.show();
}
Run Code Online (Sandbox Code Playgroud)

Joh*_*web 6

如果你提高编译器的警告级别,你会得到一个提示,你的类包含指针,但你没有定义Sequence(const Sequence&)operator=(const Sequence&)(参见什么是三规则?).

因为您没有提供复制构造函数或赋值运算符,所以编译器会为您提供这些,它们执行成员分配.

当您调用时s1 = Sequence(3,"s1");,您正在执行以下操作(这对Java开发人员来说可能是意外的):

  • Sequence使用"s1"作为名称创建一个新的临时三个
  • 将此分配给s1,其中:
    • 设置si._content为指向ints刚刚创建的三个新数组的指针,泄漏旧的10个数组.
    • 设置si._count3
    • 设置si._name"s1"
  • 然后销毁临时(而不是 s1)(在上面的实际输出中,你看到"s1"被销毁两次),留下_content指向free'd内存(这就是为什么你在第二次调用时看到垃圾s1.show()).

如果您声明这样的赋值运算符,您将获得更接近预期输出的内容:

Sequence& operator =(const Sequence& rhs)
{
    if (this != &rhs)
    {
        delete [] _content;

        _count = rhs._count;
        _content = new int[_count];
        _name = rhs._name + " (copy)";
        for (int i = 0; i < _count ; ++i)
        {
            _content[i] = rhs._content[i];
        }
    }
    return *this;
}
Run Code Online (Sandbox Code Playgroud)

但是,你不会看到:

destructor ---abc
Run Code Online (Sandbox Code Playgroud)

...因为你s1在它_name包含时不会破坏"abc".

s1当它在结束时超出范围时被销毁},这就是你看到第二个析构函数调用的原因.与您的代码,这就要求delete[]s1._content第二次(它是根据临时删除了,你还记得).这可能会导致程序结束时发生崩溃.

我在我的赋值运算符中添加" (copy)"_name帮助来说明这里发生的事情.

还请看一下什么是复制和交换习语?,这是一个处理带有原始指针的类的非常简洁的方法.这也将产生你的愿望作为实例的输出s1_name"abc"swapPED并销毁.我已经在这里实现了这一点,还有一些其他的小改进,以便您可以看到它正常工作.

注意:创建类实例的规范方法是:

Sequence s1; // Default constructor. Do not use parentheses [http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.2]!
Sequence s2(3, "s2") // Constructor with parameters
Run Code Online (Sandbox Code Playgroud)

  • C++中的堆栈分配对象在超出范围时会被销毁.这意味着如果将它们声明为变量,则在声明它们的块结束时将它们销毁,如果它们是匿名/临时的,则它们在内部创建的语句结束时将被销毁. (2认同)
  • 根据我的回答,未命名的临时工作在分配后立即销毁.`s1`在范围的最后被摧毁. (2认同)