在类内给成员变量分配内存使用new和delete中最常遇到的问题就是内存泄漏,造成这样的原因往往是不对应,而且对C++的构造函数和析构函数不是很了解。C++会默认提供下面的函数:
• 默认构造函数,如果没有定义构造函数:
• 默认析构函数,如果没有定义;
• 复制构造函数,如果没有定义;
• 赋值运算符,如果没有定义:
• 地址运算符,如果没有定义。
如果没有构造函数就会存在提供默认构造函数,如果定义了,就不会提供,但是复制构造函数是默认隐藏提供的,而且赋值运算符,即将一个类对象赋值给同类的另一个对象。这里分别讲解需要注意的要点。
1、复制构造函数:复制构造函数用于将一个对象复制到另一个对象,其原型为: Classname (const Classname &);
需要知道何时被使用,其使用的地方大致集中在3处:在用一个对象给另一个对像用=初始化时;在传入实参的时候会默认复制一个副本,调用复制构造函数;在函数返回值的时候也会返回副本,即调用一个复制构造函数。第二第三个都比较好理解,着重说一下第一个,因为涉及到好多的初始化方式:
StringBad ditto (motto) ; // calls StringBad (const StringBad A)
StringB admetoo = motto; // calls StringBad (const StringBad &)
StringBad also = StringBad(motto); // calls StringBad (const StringBad &)
StringBad * pStringBad = new StringBad (motto);
以其中的第二条作为例子,说下过程,这里首先调用admetoo的复制构造函数,并把motto作为参数穿进去,将motto的所有成员变量的值都传给admetoo的成员变量,这样看上去两者就有相同的值了。
隐式的构造函数会带来很多不便,隐式的复制构造函数会将原对象的属性值原封不动的复制给新对象,这样在复制指针的时候实际上覆制的为同一个指针,当操作被复制的新对象的指针后,原来对象的指针也会受到影响。所以在一个类中含有指针,或者含有静态变量,或者在构造函数中使用new的一般都要显示的重新定义复制构造函数(此过程被叫做深度复制)。
2、默认的赋值运算符:Class_name & Class_name::operator=(const Class_name &) ;
虽然与复制构造函数稍有差别,但是在使用=的时候,实际是将原对象的数据赋值给新对象,新对象的指针等数据依然是原对象的值,会导致指针的不恰当操作。
//这种方式是:用有参数的和没参数的构造函数分别定义两个对象,然后让对象1给对象2赋值
StringBad headlinel("CeleryS talks at Midnight");
StringBad knot;
knot = headlinel; // assignment operator invoked
其最标准的写法如下:
StringBad & StringBad::operator = (const StringBad & st)
{
if (this == &st) //object assigned to itself
return *this; // all done
delete [] str; // free old string
len = st.len;
str = new char [len + 1 ]; // get space for new string
std :: strcpy(str, st.str ); // copy the string
return *this ; // return reference to invoking
}
其实说到底就是在重载=,当然还可以传入不同的参数来重载出不同的运算符,如传入char*,就可以将char*赋值给stringbad。
3.包含类成员的类的逐成员赋值:
如果类包含多个类,但是这些类都需要new属性,那么还需要在为这个大类重写复制构造函数吗?答案是不用的,这个类是有一定的智能型的,根据组件类的复制构造函数去逐个赋值。
总结来说,当构造函数中包含new等动态分配内存的操作时,就要对赋值构造函数,赋值运算符等进行显示的定义,从而进行深度复制,防止使用默认的构造函数,使指针操作异常。