这是就需要我们自己重载构造函数了,即定义自己的复制构造函数,
1: String::String(const String& a) 2: { 3: len=a.len; 4: str=new char(len+1); 5: strcpy(str,a.str); 6: }
这里执行的是深拷贝。这个函数的功能是这样的:假设对象A中的str指针指向地址2000,内容为“I am a C++ Boy!”。我们执行代码String B=A时,我们先开辟出一块内存,假设为3000。我们用strcpy函数将地址2000的内容拷贝到地址3000中,再将对象B的str指针指向地址3000。这样,就互不干扰了。
c)还有一段代码
1: String String3; 2: String3=test4;
问题和上面的相似,大家应该猜得到,它同样是执行了浅拷贝,出了同样的毛病。比如,执行了这段代码后,析构函数开始执行。由于这些变量是后进先出的,所以最后的String3变量先被删除:这个字符串将被删除:String:第四个范例。执行正常。最后,删除到test4的时候,问题来了:程序崩溃。原因我不用赘述了。
那怎么修改这个赋值操作呢,当然是自己定义重载啦,
版本一,
1: String& String::operator =(const String &a) 2: { 3: if(this == &a) 4: return *this; 5: delete []str; 6: str = NULL; 7: len=a.len; 8: str = new char[len+1]; 9: strcpy(str,a.str); 10: 11: return *this; 12: } //重载operator=
版本二,
1: String& String::operator =(const String& a) 2: { 3: if(this != &a) 4: { 5: String strTemp(a); 6: 7: len = a.len; 8: char* pTemp = strTemp.str; 9: strTemp.str = str; 10: str = pTemp; 11: } 12: return *this; 13: }
这个重载函数实现时要考虑填补很多的陷阱!限于篇幅,大概说下,返回值须是String类型的引用,形参为const 修饰的Sting引用类型,程序中要首先判断是否为a=a的情形,最后要返回对*this的引用,至于为什么需要利用一个临时strTemp,是考虑到内存不足是会出现new异常的,将改变Srting对象的有效状态,违背C++异常安全性原则,当然这里可以先new,然后在删除原来对象的指针方式来替换使用临时对象赋值。
我们根据上面的要求重新修改程序后,执行程序,结果显示为,从图的右侧可以到,这次执行正确了。
3.2 如何对付内存泄漏
写出那些不会导致任何内存泄漏的代码。很明显,当你的代码中到处充满了new 操作、delete操作和指针运算的话,你将会在某个地方搞晕了头,导致内存泄漏,指针引用错误,以及诸如此类的问题。这和你如何小心地对待内存分配工作其实完全没有关系:代码的复杂性最终总是会超过你能够付出的时间和努力。于是随后产生了一些成功的技巧,它们依赖于将内存分配(allocations)与重新分配(deallocation)工作隐藏在易于管理的类型之后。标准容器(standard containers)是一个优秀的例子。它们不是通过你而是自己为元素管理内存,从而避免了产生糟糕的结果。
如果不考虑vector和Sting使用来写下面的程序,你大脑很会费劲的…..
1: #include 运行结果:这个程序利用标准库的string和vector来申请和管理内存,方便简单,若是设想使用new和delete来重新写程序,会头疼的。 这些技巧并不完美,要系统化地使用它们也并不总是那么容易。但是,应用它们产生了惊人的差异,而且通过减少显式的内存分配与重新分配的次数,你甚至可以使余下的例子更加容易被跟踪。 如果你的程序还没有包含将显式内存管理减少到最小限度的库,那么要让你程序完成和正确运行的话,最快的途径也许就是先建立一个这样的库。模板和标准库实现了容器、资源句柄等等 如果你实在不能将内存分配/重新分配的操作隐藏到你需要的对象中时,你可以使用资源句柄(resource handle),以将内存泄漏的可能性降至最低。 这里有个例子:需要通过一个函数,在空闲内存中建立一个对象并返回它。这时候可能忘记释放这个对象。毕竟,我们不能说,仅仅关注当这个指针要被释放的时候,谁将负责去做。使用资源句柄,这里用了标准库中的auto_ptr,使需要为之负责的地方变得明确了。 1: #include
注 意,程序中没有出现显式的内存管理,宏,溢出检查,显式的长度限制,以及指针。通过使用函数对象和标准算法(standard algorithm),我可以避免使用指针——例如使用迭代子(iterator),不过对于一个这么小的程序来说有点小题大作了。