class MyString
{
public:
//构造函数
MyString(const char *str=NULL);
//拷贝构造函数
MyString(const MyString &other);
//赋值运算符
MyString &operator=(const MyString &other);
//析构函数
~MyString();
private:
char *mdata;
};
一、构造函数
//构造函数
MyString::MyString(const char *str=NULL)
{
if(str==NULL)
{
mdata=new char[1];
mdata="\0";
}
else
{
int len=strlen(str);
mdata=new char[len];
strcpy(mdata,str);
}
}
1、定义
每个类都分别定义了它的对象初始化的方式,类通过一个或者几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数。构造函数的任务是初始化类的数据成员,无论何时只要类的对象被创建,就会执行构造函数。
2、特征
1)构造函数的名字和类名相同;构造函数没有返回类型;
2)类可以包含多个构造函数,不同的构造函数之间必须在参数数量和参数类型上有所区别。
3)当类没有声明任何构造函数时,编译器会自动生成默认构造函数。
3、注意
1)合成的默认构造函数只适合非常简单的类,对于一个普通的类来说,必须定义它自己的默认构造函数。
2)注意构造函数初始值列表的使用
二、拷贝构造函数
//拷贝构造函数
MyString::MyString(const MyString &other)
{
int len=strlen(other.mdata);
mdata=new char[len];
strcpy(mdata,other.mdata);
}
1、定义
如果一个构造函数的第一个参数是自身的引用,且任何额外的参数都有默认值,则此构造函数是拷贝构造函数。
2、特征
1)拷贝构造函数的第一个参数必须是引用类型。如果其参数不是引用类型则调用永远也不会成功,因为为了调用拷贝构造函数,我们必须拷贝它的实参名单为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。
2)如果我们没有为一个类定义拷贝构造函数,编译器会为我们定义一个。与合成的默认构造函数不同,即使我么定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数。
3、应用
1)对类类型的成员,会使用其拷贝构造函数来拷贝;内置类型的成员则直接拷贝。
2)如果想阻止拷贝,就将拷贝构造函数和拷贝赋值运算符声明为私有的,并且故意不去实现他们,这样,即便friend函数也无法完成拷贝了。
三、赋值运算符
//赋值运算符
MyString & MyString::operator=(const MyString &other)
{
//检查自赋值
if(this==&other)
return*this;
delete []mdata;
//分配新的资源,并复制内容
int len=strlen(other.mdata);
mdata=new char[len];
mdata=other.mdata;
return *this;
}
1、定义
赋值运算符就是一个名为operator=的函数,类似于其他任何函数,运算符函数也有一个返回类型和一个参数列表。
2、特征
1)赋值运算符通常应该返回一个指向其左侧运算对象的引用。
2)如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。
四、析构函数
//析构函数
MyString::~MyString()
{
delete []mdata;
}
1、定义
析构函数执行与构造函数相反的操作,析构函数释放对象的资源,一、并销毁对象的非static数据成员。
2、调用
1)变量在离开其作用域时被销毁
2)当一个对象被销毁时,其成员被销毁
3)容器(无论是标准库容器还是数组)被销毁时,其元素被销毁
4)对于动态分配的对象,当对指向他的指针执行delete运算符时被销毁
3、注意
1)析构函数不接受参数,不能被重载,对于一个给定的类,只会有唯一一个析构函数。
2)当一个类作为基类会派生出其他子类时,它的析构函数应该为virtual的,不然会造成内存泄露。
五、总结
如果一个类需要一个析构函数,它肯定也需要一个拷贝构造函数和一个拷贝赋值运算符。