宝杉的博客

UNIX/LINUX;ACE;SNMP;C++
posts - 33, comments - 23, trackbacks - 0, articles - 0

08-14 String类

Posted on 2007-08-30 10:31 宝杉 阅读(263) 评论(0)  编辑 收藏 引用 所属分类: C++

定义:

     class String

     {

       public:

         String(const char *str = NULL);  // 普通构造函数

         String(const String &other);     // 拷贝构造函数

         ~ String(void);                       // 析构函数

         String & operate =(const String &other);  // 赋值函数

       private:

         char     *m_data;               // 用于保存字符串

     };

 

 

普通构造:

         // String的普通构造函数

         String::String(const char *str)

{

     if(str==NULL)

     {

         m_data = new char[1];

         *m_data = \0;

     }   

     else

     {

         int length = strlen(str);

         m_data = new char[length+1];

         strcpy(m_data, str);

     }

}   

注意在声明时,才定义的参数,且赋初值为NULL。在拷贝函数内部,也进行了是否为NULL的检查,进行特殊处理。

 

 

析构函数:

// String的析构函数

         String::~String(void)

{

     delete [] m_data; 

// 由于m_data是内部数据类型,也可以写成 delete m_data;

         }

内部数据类型,delete

外部数据类型,[ ] delete

 

拷贝构造函数与赋值函数:

拷贝函数:

位拷贝:编译器以此方式自动生成缺省的函数,比如构造拷贝和赋值拷贝。但是,如果函数中还有指针变量,那么缺省的函数就隐含了错误。

例如:

以类String的两个对象a,b为例,假设a.m_data的内容为“hello”,b.m_data的内容为“world”。

现将a赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data = a.m_data

这将造成三个错误:

一是b.m_data原有的内存没被释放,造成内存泄露;

二是b.m_dataa.m_data指向同一块内存,ab任何一方变动都会影响另一方;

三是在对象被析构时,m_data被释放了两次。

混淆:

String  a(hello);

String  b(world);

String  c = a;     // 调用了拷贝构造函数,最好写成 c(a);

c = b;    // 调用了赋值函数

例子:

     // 拷贝构造函数

     String::String(const String &other)

     {   

// 允许操作other的私有成员m_data

     int length = strlen(other.m_data);  

     m_data = new char[length+1];

     strcpy(m_data, other.m_data);

}

赋值函数:

// 赋值函数

     String & String::operate =(const String &other)

     {   

         // (1) 检查自赋值

         if(this == &other)

              return *this;

        

         // (2) 释放原有的内存资源

         delete [] m_data;

        

         // 3)分配新的内存资源,并复制内容

     int length = strlen(other.m_data);  

     m_data = new char[length+1];

          strcpy(m_data, other.m_data);

        

         // 4)返回本对象的引用

         return *this;

}   

赋值函数的四个步骤:

(1) 检查自赋值

不会写出a = a的自赋值语句,但间接自赋值有可能发生。

// 内容自赋值

b = a;

c = b;

a = c;  

// 地址自赋值

b = &a;

a = *b;

防止:

if(this == &other)

错写成为

     if( *this == other)

(2) 释放原有的内存资源

(3) 分配新的内存资源,并复制内容

(4) 返回本对象的引用

 

F:拷贝构造和普通构造的区别?

Q:拷贝构造的参数是“引用”,引用不可能是NULL;普通构造函数参数是“指针”,需要类型检查,判断是否是NULL

 

String的赋值函数比构造函数复杂得多,分四步实现:

1)第一步,检查自赋值。你可能会认为多此一举,难道有人会愚蠢到写出 a = a 这样的自赋值语句!的确不会。但是间接的自赋值仍有可能出现,例如

    

// 内容自赋值

b = a;

c = b;

a = c;  

// 地址自赋值

b = &a;

a = *b;

 

也许有人会说:“即使出现自赋值,我也可以不理睬,大不了化点时间让对象复制自己而已,反正不会出错!”

他真的说错了。看看第二步的delete,自杀后还能复制自己吗?所以,如果发现自赋值,应该马上终止函数。注意不要将检查自赋值的if语句

if(this == &other)

错写成为

     if( *this == other)

2)第二步,用delete释放原有的内存资源。如果现在不释放,以后就没机会了,将造成内存泄露。

3)第三步,分配新的内存资源,并复制字符串。注意函数strlen返回的是有效字符串长度,不包含结束符‘\0’。函数strcpy则连‘\0’一起复制。

4)第四步,返回本对象的引用,目的是为了实现象 a = b = c 这样的链式表达。注意不要将 return *this 错写成 return this 。那么能否写成return other 呢?效果不是一样吗?

不可以!因为我们不知道参数other的生命期。有可能other是个临时对象,在赋值结束后它马上消失,那么return other返回的将是垃圾。

 

 

08-22

如果不想让别人使用编译器编写构造拷贝和赋值函数,可以声明为私有:

     class A

     {

       private:

         A(const A &a);                   // 私有的拷贝构造函数

         A & operate =(const A &a);  // 私有的赋值函数

     };

 

如果有人试图编写如下程序:

     A  b(a); // 调用了私有的拷贝构造函数

     b = a;        // 调用了私有的赋值函数

编译器将指出错误,因为外界不可以操作A的私有函数。

但是怎样才能使用构造拷贝和赋值函数呢?

虚拟函数使用:C++exams\destructor

 

在编写派生类的赋值函数时,注意不要忘记对基类的数据成员重新赋值。例如:

C++exams\base_operator

 


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理