(一二九)类中使用new

来源:互联网 发布:淘宝摇一摇在哪 编辑:程序博客网 时间:2024/05/10 17:25

在类定义——主要是类的构造函数中使用new时,需要注意很多问题:

①按照一个new对应一个delete的原则。

 

②构造函数中使用new/ new [],那么析构函数中就应该使用delete/ delete[]

 

③析构函数中使用delete/ delete[],那么构造函数、默认构造函数、复制构造函数,都应该使用new/ new[]

 

④且newdelete应该保持一致,要么都不带[],要么都带[]

 

⑤不同构造函数中的new也应该保持一致,要么都带,要么都不带[]

 

⑥但可以把指针初始化为空指针(空指针支持被deletedelete[])(空指针的定义方式是,=NULL或者=nullptr,也有=0的,不过前两者比较好)

 

⑦当遇见new时,复制构造函数 应自定义(深度复制),否则按值传递会导致出现问题。复制构造函数的定义思路,有一些类似构造函数。

参数:const 类引用

返回值和返回类型:无

基本类型的(非指针):按值传递;

指针:构造函数用new的,也跟着一起new,但不需要delete(因为是初始化对象时使用)

计数器(如果有):需要更新;

 

⑧应自定义 赋值运算符 (面对对象时),通过深度复制将一个对象赋值给另一个对象。思路类似构造函数:

参数:const 类引用

返回值:*this

返回类型:类对象的引用;

注意:需要有防止自己赋值自己的语句判断,判断条件是地址(this==&目标对象,原因在于==判断需要有运算符重载才可以

基本类型:按值传递;

指针:先deletenew(因为被赋值的对象,是已经被初始化过的,也就是说new过,因此应对应delete);

计数器(如果有):不更新;

 

 

例如:

类为Man,私有成员int achar*bint len; 分别表示数值,字符串,字符串长度。

代码:

 

class Man{int a;//数值char*b;//指针int len;//指针指向字符串的长度public:Man::Man();Man::Man(int m, const char* n);Man::Man(const Man& n);Man& Man::operator=(const Man&n);};//默认构造函数:Man::Man()//这里没使用默认参数{a = 0;//a为0b = nullptr;//空指针,可以被delete或者delete[]len = 0;//长度为0}//构造函数:Man::Man(int m, const char* n){len = strlen(n);//len=被传递的字符串长度b = new char[len + 1];//然后newstrcpy_s(b, len + 1, n);//复制到成员ba = m;//给a一个初始值}//复制构造函数:Man::Man(const Man& n){len = n.len;//长度自然相等b = new char[len + 1];//new,是因为这是初始化对象时调用strcpy_s(b, len + 1, n.b);//复制过去a = n.a;//a相等}//赋值运算符重载(深度复制);Man& Man::operator=(const Man&n)//调用的是被赋值的对象,隐式传递给函数{if (this == &n) return *this;//用于自己赋值给自己,判断地址的原因在于对象==对象需要运算符重载a = n.a;//相等len = n.len;//自然也相等delete[]b;//因为已经初始化过了,新的长度不确定,因此要delete[]掉老的再重新申请动态内存b = new char[len + 1];//newstrcpy_s(b, len + 1, n.b);//复制return *this;//返回对象,*this表示当前对象}

 

 

一个类包含另一个类对象作为成员时:

假如有String类,在之前,我们已经自定义String类的复制构造函数了(也就是说可以把一个类对象作为参数,在初始化另一个类对象时使用)。

 

又有Man类,Man类里有两个数据成员,一个是int类型变量m,一个是String类对象n

 

那么,对于Man类对象ab来说,可以无需自定义赋值运算符,即可直接使用a=b这种方法,将b赋值给a

 

原因在于,默认的赋值运算符,是逐值赋值的。也就是a.m=b.m; a.n=b.n; 这样。

 

int类型属于基本类型,可以直接使用赋值运算符,这是我们知道的。

 

String类型是我们自定义的类型,因为重载了赋值运算符(面向对象的),因此我们可以直接将一个String类对象赋值给另一个String类对象(调用重载赋值运算符函数,而不是调用默认赋值运算符函数),此时调用的是 重载后的赋值运算符 。

又已知,自定义的重载赋值运算符函数,并不会生成临时对象(默认的也不会),也不会让两个对象的指针成员指向同一个地址(因此不会导致析构函数调用时出错)。于是,不会带来不良后果,可以直接将String类对象赋值给另一个。

 

因此,对Man类使用赋值运算符(逐值赋值2个数据成员)并不会带来什么不良后果。

 

 

但假如Man类还有其他成员,需要定义复制构造函数和赋值运算符,情况会更复杂,在这种情况下,这些函数必须显式的调用String类的复制构造函数和赋值运算符,将在13章介绍。

——不明白什么叫显式的调用String类的复制构造函数和赋值运算符,是指像a.n=b.n这样么?

 


0 0