1 C++中动态内存分配引发问题的解决方案
假设我们要开发一个String类,它可以方便地处理字符串数据。我们可以在类中声明一个数组,考虑到有时候字符串极长,我们可以把数组大小设为200,但一般的情况下又不需要这么多的空间,这样是浪费了内存。对了,我们可以使用new操作符,这样是十分灵活的,但在类中就会出现许多意想不到的问题,本文就是针对这一现象而写的。现在,我们先来开发一个String类,但它是一个不完善的类。的确,我们要刻意地使它出现各种各样的问题,这样才好对症下药。好了,我们开始吧!
/* String.h */ #ifndef STRING_H_ #define STRING_H_ class String { private: char * str; //存储数据 int len; //字符串长度 public: String(const char * s); //构造函数 String(); // 默认构造函数 ~String(); // 析构函数
注意:没有赋值构造函数
friend ostream & operator<<(ostream & os,const String& st); }; #endif /*String.cpp*/ #include <iostream> #include <cstring> #include "String.h" using namespace std; String::String(const char * s) { len = strlen(s); str = new char[len + 1]; strcpy(str, s); }//拷贝数据 String::String() { len =0; str = new char[len+1]; str[0]='"0'; } String::~String() { cout<<"这个字符串将被删除:"<<str<<'"n';//为了方便观察结果,特留此行代码。 delete [] str; } ostream & operator<<(ostream & os, const String & st) { os << st.str; return os; } /*test_right.cpp*/ #include <iostream> #include <stdlib.h> #include "String.h" using namespace std; int main() { String temp("天极网"); cout<<temp<<'"n'; system("PAUSE"); return 0; }
运行结果:
天极网
请按任意键继续. . .
大家可以看到,以上程序十分正确,而且也是十分有用的。可是,我们不能被表面现象所迷惑!下面,请大家用test_String.cpp文件替换test_right.cpp文件进行编译,看看结果。有的编译器可能就是根本不能进行编译!
#include <iostream> #include <stdlib.h> #include "String.h" using namespace std; void show_right(const String&); void show_String(const String);//注意,参数非引用,而是按值传递。 int main() { String test1("第一个范例。"); String test2("第二个范例。"); String test3("第三个范例。"); String test4("第四个范例。"); cout<<"下面分别输入三个范例:"n"; cout<<test1<<endl; cout<<test2<<endl; cout<<test3<<endl; String* String1=new String(test1);//复制构造函数测试 cout<<*String1<<endl; delete String1; cout<<test1<<endl; //在Dev-cpp上没有任何反应。 cout<<"使用正确的函数:"<<endl; show_right(test2);//地址传递,参数测试 cout<<test2<<endl; cout<<"使用错误的函数:"<<endl; show_String(test2);//赋值参数传递测试 cout<<test2<<endl; //这一段代码出现严重的错误! String String2(test3);//复制构造函数测试 cout<<"String2: "<<String2<<endl; String String3; String3=test4;//赋值测试 cout<<"String3: "<<String3<<endl; cout<<"下面,程序结束,析构函数将被调用。"<<endl; return 0; } void show_right(const String& a) { cout<<a<<endl; } void show_String(const String a) { cout<<a<<endl; }
运行结果:
下面分别输入三个范例: 第一个范例。 第二个范例。 第三个范例。 第一个范例。 这个字符串将被删除:第一个范例。 使用正确的函数: 第二个范例。 第二个范例。 使用错误的函数: 第二个范例。 这个字符串将被删除:第二个范例。 这个字符串将被删除:?= ?= String2: 第三个范例。 String3: 第四个范例。 下面,程序结束,析构函数将被调用。 这个字符串将被删除:第四个范例。 这个字符串将被删除:第三个范例。 这个字符串将被删除:?= 这个字符串将被删除:x = 这个字符串将被删除:?= 这个字符串将被删除:
现在,请大家自己试试运行结果,或许会更加惨不忍睹呢!下面,我为大家一一分析原因。
首先,大家要知道,C++类有以下这些极为重要的函数:
一:复制构造函数。
二:赋值函数。
我们先来讲复制构造函数。什么是复制构造函数呢?比如,我们可以写下这样的代码:String test1(test2);这是进行初始化。我们知道,初始化对象要用构造函数。可这儿呢?按理说,应该有声明为这样的构造函数:String(const String &);可是,我们并没有定义这个构造函数呀?答案是,C++提供了默认的复制构造函数,问题也就出在这儿。
(1):When,什么时候会调用复制构造函数呢?(以String类为例。)
在我们提供这样的代码:String test1(test2)时,它会被调用;当函数的参数列表为按值传递,也就是没有用引用和指针作为类型时,将会被以赋值构造函数调用。
如:void show_String(const String),它会被调用。其实,还有一些情况,但在这儿就不列举了。
(2):What,它是什么样的函数。
它的作用就是把两个类进行复制。
拿String类为例,C++提供则可以为这样的类,提供如下的默认复制构造函数是这样的:
即:默认完成对所有类成员的赋值操作。
String(const String& a) { str=a.str; len=a.len; }
endl;
参考博客: