头文件
class student
{
public:
student(char*);
~student();
student(const student &);
char* name;
static int num;
};
main.cpp文件
int student::num=0;
student::student(char* myname)
{
num++;
int len=strlen(myname);
name=new char[len+1];
strcpy(name,myname);
cout<<name<<":创建,剩余个数:"<<num<<endl;
}
student::~student()
{
num--;
cout<<name<<":销毁,剩余个数:"<<num<<endl;
delete [] name;
}
void fun(student funs)
{
cout<<funs.name<<"被调用"<<endl;
}
void main()
{
student stu1("student1");//--------------1
student stu2("student2");//--------------2
student stu3("student3");//--------------3
fun(stu2);//--------------4
student stu4=stu3;//--------------5
student stu5("student5");//--------------6
stu5=stu1;//--------------7
}
1,2,3都很正常,到4的时候,实参为类值对象,传递过程是一个拷贝构造函数,funs在fun方法结束后会调用析构函数,而运行到5的时候,这是一个通过拷贝构造函数实例化类,这个过程不会调用构造方法和析构函数的,因为我们要了解拷贝构造函数是如何写的,如下
student(const student & c)
{
name=c.name;
}
所以5就相当于 student stu4(stu3)
到7的时候只是赋值,不是通过拷贝构造函数实例化类,因为stu5已经在6的时候实例化了
这个代码在VC 6.0中运行的时候,神奇般的出错了,第一个错误出在main函数结束后,一个个析构,堆栈的析构顺序是先进后出,先析构stu5,再析构stu4,再析构stu3,就在析构stu3的时候出错了。截图如下
为什么会出错,此时研究后发现是因为在5这个位置调用了拷贝构造函数来构造stu4类对象,根据拷贝构造函数的原型(下是原型)
student(const student & c)
{
name=c.name;
}
我发现name=c.name,对于5来说就是stu4.name=stu3.name;name是一个指针,这是一个指针赋值,即stu4的name指针指向了stu3的那么指针指向的位置。那么问题迎刃而解了,即在析构stu4的时候(因为堆栈析构顺序先析构晚压入栈的),delete [] name了,就是告诉计算机,name这块内存被释放了,不被任何东西指向(不和任何东西有关系),于是再去析构stu3的时候,出问题了。。你已经和这块地址没有关系了,凭什么让你delete。而且也没必要delete了,但是如果你知道这块空间地址,并通过地址直接访问这块空间还是能访问到name的值的,因为值并没有被删除,修改代码后,你会更加明白问题在哪里,在5和6之间加上一句:cout<<"stu3的name指向地址:"<<(int *)stu3.name<<"\t"<<"stu4的name指向地址:"<<(int *)stu4.name<<endl;
输出(您可以不要直接cout<<"stu3的name指向地址:"<<&stu3.name<<"\t"<<"stu4的name指向地址:"<<&stu4.name<<endl;这样来访问,这是获取该指针在堆栈中的地址,而不是内容在堆中存储的地址)
要解决这个问题。。看样子只有重写拷贝构造函数了(下面是重构拷贝构造函数)
student::student(const student& tempstu)
{
int len=strlen(tempstu.name);
name=new char[len+1];
strcpy(name,tempstu.name);
}
以为一切问题解决,可是运行的时候
很明显这是在析构1的时候,即析构stu1的时候,发现第7句stu5=stu1;这只是一个赋值过程,因为stu5对象在第6句就构造了,赋值的过程的话,即依然是stu5.name=stu1.name,然后问题原因道理同上。这就是所谓的浅复制的过程,这也是浅复制容易导致的问题(大家可以去研究浅复制和深复制)
至于结构中的最后 的剩余个数为什么编程-2了,(其实这个例子模仿c++ primer),主要是因为位置4调用了fun(stu2);-这个调用,传递了一个实参为stu2的类对象,在传递过程类似
student funs=stu2;这个funs对象在fun方法返回时会销毁,销毁就会调用析构函数num--,而且在第5位置处,是通过student stu4=stu3这种拷贝构造函数来实例化stu4,在重写的拷贝构造函数中,我们并没有让num++,而析构他的时候却有num--了,这就是为什么最后num不会回归至初始0,而是-2