每个人
我有一些 java经验,是c的初学者.
belw是我的代码,它的输出是
我有一些 java经验,是c的初学者.
belw是我的代码,它的输出是
0 1 2 3 4 5 6 7 8 9 destructor ---s1 8791616 8785704 2 destructor ---s1
我预计输出是
0 1 2 3 4 5 6 7 8 9 destructor ---abc 0 1 2 destructor ---s1
我无法理解为什么析构函数释放第一个对象的资源.
如何打印我预期的输出?
#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(); }
解决方法
如果你提高编译器的警告级别,你会得到一个提示,你的类包含指针,但你没有定义Sequence(const Sequence&)或operator =(const Sequence&)(见
What is The Rule of Three?).
因为您没有提供复制构造函数或赋值运算符,所以编译器会为您提供这些,它们执行成员分配.
当您调用s1 = Sequence(3,“s1”);时,您正在执行以下操作(这对Java开发人员来说可能是意外的):
>创建一个新的,临时的,三个序列,其名称为“s1”
>将其分配给s1,其中:
>将si._content设置为指向刚刚创建的三个整数的新数组的指针,泄漏旧的10个整数.
>将si._count设置为3
>将si._name设置为“s1”
>然后销毁临时(而不是s1)(在上面的实际输出中,你看到“s1”被销毁两次),留下_content指向free’d内存(这就是你在第二次调用s1时看到垃圾的原因.节目()).
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; }
但是,你不会看到:
destructor ---abc
…因为你的_name包含“abc”时不会销毁s1.
s1在结束时超出范围时被销毁,这就是你看到第二个析构函数调用的原因.使用您的代码,这会再次调用s1._content上的delete [](它会在临时删除,您会记得).这可能会导致程序结束时发生崩溃.
我在我的赋值运算符中为_name添加了“(copy)”来帮助说明这里发生了什么.
还请看一下What is the copy-and-swap idiom?,这是处理带有原始指针的类的非常简洁的方法.这也会产生你想要的输出,因为_c为“abc”的s1实例被换出并销毁.我已经实现了这个here,以及其他一些小改进,以便您可以看到它正常工作.
N.B:创建类实例的规范方法是:
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