fancy于2005年1月5日


        C++的神奇之处就是无论你怎样费劲心机的构思一个类,结果却还是有这样那样的缺陷和错误。
        首先让我们明确一下初始化和赋值的区别。对于c++来说,一个新的对象被创建,就有初始化操作出现;赋值则是修改一个已经被创建的对象的值,这个时候并没有新的对象被创建。
 

EString p=q;//初始化,新对象p被创建
         p=q;//      //赋值,已存在的对象p的值被改变        初始化由构造函数实现,赋值由operator=操作符实现。
        构造函数中有两个容易迷惑的概念:“缺省构造函数”和“缺省的复制构造函数”。前者是不需要任何参数就可以被调用的构造函数;后者是由编译器自动生成的复制构造函数,如果我们没有为类声明一个复制构造函数,那么编译器会自动生成一个。
        我们自己在编写一个类的时候,经常会遇到缺省的复制构造函数的行为和我们的期望值不相符。
        请看以下这段程序,一个字符串类。
        class EString  
        {
        private:
         char *pstr
        public:
         EString(const char *str="");
         virtual ~EString();
         void Release()
        };
        EString::EString(const char *str)
        {
         pstr=new char[strlen(str)+1];
         strcpy(pstr,str);
        }
        EString::~EString()
        {
         if(!pstr)
          delete[] pstr; 
        }
        void EString::Release()
        {
         delete[] pstr; 
        }        上面的EString类中,EString类的缺省复制构造函数会把pstr这个指针进行复制。如果我进行了如下的操作:
        EString p("hello");
        EString q=p;
        p.Release();
        那么对象q中的pstr就会指向被释放的内存,原因是p和q中的pstr实际上都指向同一块内存区域。这是一个非常严重的问题,因此我们必须改进这个类的设计,确保每一个对象都有一份自己独立的数据拷贝。        于是我们需要自己来声明一个复制构造函数:
        class EString  
        {
        private:
         char *pstr
        public:
         EString(const char *str="");
         virtual ~EString();
         EString(const EString& s);
         void Release()
        };
        EString::EString(const char *str)
        {
         pstr=new char[strlen(str)+1];
         strcpy(pstr,str);
        }
        EString::~EString()
        {
         if(!pstr)
          delete[] pstr; 
        }
        EString::EString(const EString& s)
        {
         pstr=new char[strlen(s.pstr)+1];
         strcpy(pstr,s.pstr);
        }
        void EString::Release()
        {
         delete[] pstr; 
        }        通过上边的代码,实现自己的复制构造函数,这样每一个对象都能拥有一份数据私有拷贝了。
        在解决缺省复制构造函数的问题之后,我们理所当然的想到,缺省的赋值操作符也会存在同样的问题。
        我们执行下面的代码:
        EString p("hello");
        EString q;
        q=p;
        p.Release();
        先前缺省复制构造函数出现的问题,又出现在赋值操作符之中了。同样我们需要自己来重载赋值运算符。
        const EString& EString::operator =(const EString &s)
        {
         if(pstr!=s.pstr)
         {
          delete [] pstr;
          pstr=new char[strlen(s.pstr)+1];
          strcpy(pstr,s.pstr);
         }
         return *this;
        }

        重载赋值运算符之后,就可以避免指针指向同一块内存区域,让每个对象具有自己独立的数据了。

        总的来说,我们在对待那些有指针的类的时候需要格外注意,对于它们来说缺省的复制构造函数和缺省的赋值操作符可能是不恰当的,它们只是简单地复制了指针而不是指针所指向那的那些重要的数据。这是最近所考虑到的一些问题,希望能给各位一些小小帮助。