[C++]千万小心局部变量

来源:互联网 发布:淘宝上怎么不买香烟 编辑:程序博客网 时间:2024/05/07 17:18

千万小心局部变量

小心看好你的局部变量,尤其在这种情况下:
当你在一个function中声明了一个对象,然后你不遗余力地在对这个对象进行加工、进行操作,辛苦半天将他作为结果传出。好,编译,没问题,你似乎心满意足。可是别高兴太早,运行一下试试,哦,my god,内存引用错误,操作系统奉上了个无比可爱的小红叉叉,可是你却恨不得把他“一百遍”了。你有过这种经历吗?恩,好,让我们看看下面这段足以产生这样效果的代码:

#include  <iostream>

#include  <cstring>

using namespace std;

class String
{
public:
                     String()
                     {
                     }

                     String(char* str)
                     {
                                 size_t len = strlen(str);
                                 p = new char[len + 1];
                                 strcpy(p,str);
                     }

                     String(const String& rhs)
                     {
                                 size_t len = strlen(rhs.p);
                                 p = new char[len + 1];
                                 strcpy(p,rhs.p);
                     }

                     void print()
                     {
                                 char* pp = p;
                                 while(*pp != '/0')
                                 {
                                       cout << *pp;
                                       ++pp;
                                 }
                     }

                     char* p;
};

int main()
{
         String str1("Hello,world!/n");
         String str2(str1);
         str1.print();
         str2.print();
         return 0;
}

恩~,程序模拟了string的行为,很简单不是吗?运行一下,似乎跑得不错啊。好,我现在想这样操作我的string:
String str3;
str3 = str1 + str2;

怎么办?很自然的想法就是为我的程序添加一个对运算符“+”的重载函数。这样写,看有问题吗:

String operator +(const String& rhs)
{
                     String temp;
                     size_t len = strlen(p) + strlen(rhs.p);
                     temp.p = new char[len];
                     strcpy(temp.p,p);
                     strcat(temp.p,rhs.p);
                     return temp;
}


将下面的main替换掉原来的main函数:

int main()
{
                     String str1("Hello,world!");
                     String str2(str1);
                     String str3;
                     str3 = str1 + str2;?
                     cout << "/nstr1: ";
                     str1.print();
                     cout << "/nstr2: ";
                     str2.print();
                     cout << "/nstr3: ";
                     str3.print();
                     cout << endl;
                     return 0;
}

运行一下,好,没问题!下面我们来回顾一下我们的程序,什么?你是不是发现我们自己在堆上new出来的内存用完后都没释放呢!这怎么能行?!于是,很自然地,我们为我们的程序添加了一个析构函数:

~String()
{
         delete[] p;
}

编译……,OK!没问题!
运行……,oh god!怎么了?小红叉叉!!!
我在文中开始的时候所描述的一幕终于发生了,是不是期待已久啊。呵呵,你可能已经发现了,问题就在+操作符重载函数里面,因为他返回了一个局部变量。当这个局部变量被传出去后,在+操作符重载函数的他本身就销毁了。所以可以判断这个赋值号“=”并没有把对象中的内容一一拷贝给目标。所以,一种办法是重载一下“=”,而另一种更简单的方法便是将str3声明为:String str3 = str1 + str2;从而直接利用拷贝构造函数完成这个任务,试试看,程序是不是又能跑了?下面我们来看看另一种解决方案,我们来重载一下“=”,我们为我们的程序中添加下面的代码:

void operator =(const String& rhs)
{
                  size_t len = strlen(rhs.p);
                  p = new char[len + 1];
                  strcpy(p,rhs.p);
}

编译运行一下看看,呵呵,我们的程序又恢复了活力!

所以,我们看到,在一个function中返回一个局部变量,他会在他本身被销毁之前copy自身,从而将内容赋给目标。因此,你需要重载"="并提供拷贝构造函数;而如果你试图在一个function中返回一个局部变量的指针,那你麻烦大了。这个指针在他本身被销毁之前copy自身(注意这是个地址信息),然而目标再次想访问他的时候,他的原来那份地址已经被销毁了,我们的目标并没有得到他的具体信息,我们当时得到的仅仅是他的一个地址信息而已。重点是:pass-by-value便是调用copy constructor的同意词;initialization初始化是由constructor完成,而assignment赋值是由operator=完成。所以,到此为止,一切都清净了...

小的菜鸟一只,不知天高地厚,发拙作于此,欢迎各位达人拍砖!

                                                                                       sun

                                                                                       2004-7-15

原创粉丝点击