现需要为学生类添加一个数据成员用于记录其学习经历和参加社团的情况。这个数据成员可以定义为字符型数据,但其长度是个问题,因此,将其定义为字符型指针也许更合理。程序如下(为简便,本节的学生类仅涉及部分数据成员):
#include <iostream>
#include<iomanip>
#include<cstring>
using namespacestd;
class Student
{
public:
Student(char *pName="NoName",char *pRes=NULL);
void setData(char*pName="NoName", char *pRes=NULL);
void showMessage(){cout << "姓名:" << name <<endl << "简历:" << pResume << endl;}
private:
char name[10];
char *pResume;
};
Student::Student(char*pName, char *pRes)
{
strcpy(name, pName);
if(pRes == NULL)
pResume = NULL;
else
{
pResume = newchar[strlen(pRes)+1];
strcpy(pResume, pRes);
}
}
void Student::setData(char*pName, char *pRes)
{
strcpy(name, pName);
if(pRes != NULL)
{
delete []pResume;
pResume = newchar[strlen(pRes)+1];
strcpy(pResume, pRes);
}
else if(pResume != NULL)
{
delete []pResume;
pResume = NULL;
}
}
int main()
{
Student zhang3("Zhang3","\n2006.9-2012.7 平顶山一小\n2012.9-至今 平顶山一中\n");
zhang3.showMessage();
zhang3.setData("Zhang3","\n2006.9-2012.7 平顶山一小\n2012.9-至今 平顶山一中\n2012年获优秀毕业生\n");
zhang3.showMessage();
return 0;
}
程序的运行结果:
姓名:Zhang3
简历:
2006.9-2012.7 平顶山一小
2012.9-至今 平顶山一中
姓名:Zhang3
简历:
2006.9-2012.7 平顶山一小
2012.9-至今 平顶山一中
2012年获优秀毕业生
类Student存在问题,程序运行过程中其对象“占用”的存放简历的堆空间没有释放,有可能造成内存泄露。
产生对象时通常需申请一块堆空间,但什么时间释放这些堆空间呢?当对象出了作用域后对象就不能使用了,因此,出作用域后对象“占用”的堆空间就应该释放了。对象出作用域时编译系统会自动调用一个称为析构函数的函数。可以在析构函数中释放对象“占用”的堆空间。类Student可定义如下:
class Student
{
public:
Student(char *pName="NoName", char*pRes=NULL);
void setData(char*pName="NoName", char *pRes=NULL);
void showMessage(){cout << "姓名:" << name <<endl << "简历:" << pResume << endl;}
~Student();
private:
char name[10];
char *pResume;
};
Student::Student(char*pName, char *pRes)
{
strcpy(name, pName);
if(pRes == NULL)
pResume = NULL;
else
{
pResume = newchar[strlen(pRes)+1];
strcpy(pResume, pRes);
}
}
voidStudent::setData(char *pName, char *pRes)
{
strcpy(name, pName);
if(pRes != NULL)
{
delete []pResume;
pResume = newchar[strlen(pRes)+1];
strcpy(pResume, pRes);
}
}
Student::~Student()
{
if(pResume != NULL)
delete []pResume;
}
与构造函数相比,析构函数的名字前面有一个~号。当对象的作用域结束时,对象的析构函数会被自动调用执行。
有了析构函数,Student对象不会出现内存泄露的问题,但用一个Student对象初始化另一个同类的对象时(赋值同样),还是会出现问题。测试程序如下(只给出了主函数):
int main()
{
Student zhang3("Zhang3","\n2006.9-2012.7 平顶山一小\n2012.9-至今 平顶山一中\n");
{
Student temp = zhang3;
temp.showMessage();
}
zhang3.showMessage();
return 0;
}
程序运行结果如下:
(程序会出现非法内存访问的错误,图略)
问题出在什么地方呢?
程序中当用zhang3对象初始化temp对象时,temp对象的数据成员pResume也指向了zhang3对象“占用”的堆空间。temp对象正常显示信息后,便出了作用域,析构函数调用执行。temp对象释放了其指向的堆空间。当zhang3对象显示信息时,其原来指向的堆空间已经不能再使用了,即zhang3对象的数据成员pResume变成了野指针。
改正此类错误需用到复制构造函数,当用一个对象初始化同类的另一个对象时,复制构造函数会自动被调用执行。如果类中没有定义复制构造函数,编译系统会帮忙定义一个复制构造函数(执行“逐字节”拷贝的操作)。参数为类引用的构造函数就是所谓的复制构造函数。Student类的复制构造函数可定义为:
Student(Student&stu)
{
strcpy(name, stu.name);
pResume = new char[strlen(stu.pResume) +1];
strcpy(pResume, stu.pResume);
}注意:
复制构造函数名不符实,它的作用是“初始化”,出现下面三种情况时会被自动调用。1.用一个对象初始化另一个同类的对象时2.用实参对象初始化同类的形参对象时,3.用函数返回的对象初始化临时对象时。