C++面向对象编程<六>:Big Three,三个特殊函数

来源:互联网 发布:精通shell编程 知乎 编辑:程序博客网 时间:2024/05/16 16:56

简介

这里看class的另一个分类:class with pointer members.
先看String的声明

#ifndef _MYSTRING_#define _MYSTRING_class String{    public:        String(const char* cstr = 0);        String(const String& str);                //拷贝构造         String& operator = (const String& str);  //拷贝赋值         ~String();        char* get_c_str const () {return m_data};    private:        char* m_data;   };#endif

如上所示:Big Three,三个特殊的函数:拷贝构造函数、拷贝赋值函数、析构函数。其实不写,编译器也会有默认的这些函数,那么什么时候需要写呢?就得看看编译器默认的这些函数够不够用。(默认的就是一个个位进行复制)
class with pointer members一般这样设计:指针作为成员变量,在需要内存的时候动态申请,而不是直接在class里放数组,因为不知道数组需要多大。显然得自己写Big three函数
class with pointer members 就一定要写构造函数、拷贝构造函数、析构函数

构造函数和析构函数

先看构造函数的实现

inline String::String(const char* cstr = 0){    if (cstr)    {        m_data = new char[strlen(cstr) + 1];        strcpy(m_data,cstr);    }    else //未指定值,形成空字符串     {        m_data = new char[1];        *m_data = '\0';    }}

析构函数需要清理空间,这里的class有动态申请空间,若没有释放掉的话,会有内存泄露

inline String::~String(){    delete [] m_data;}

拷贝构造函数

如上String的设计,使用如下

String a("Hello");String b("World");b = a;

String类只包含m_data指针,并不包含其申请的动态空间,
如果使用默认拷贝构造函数,编译器会自己一个字节一个字节的复制,会出现的y严重的后果。如果把a的内容复制到b中去,就会出现a的m_data和b的m_data指向一个地方,那么b指向的内容”World”就没有指针指向他了,会出现内存泄露。而赋值后两个指针指向一块内存也很危险:你更改a,b就会受影响,所以这种叫浅拷贝,只会拷贝指针。会有两个隐患。(见下示意图)
这里写图片描述

改进方法就是使用深拷贝,如下
要去写的就是深拷贝
先看拷贝构造函数,如下

String::String(const String& str){    m_data = new char[ strlen(str.m_data + 1) ];    strcpy(m_data,str.m_data);}

使用如下

String s1("Hello");String s2(s1);     //以s1为蓝本、初值创建s2String s3 = s1;    //调用拷贝构造函数 //把s1赋值到s3上,但s3也是新创建的对象(既然新创建的就要调用构造函数)。

拷贝赋值

使用如下

b = a;

首先明白:b和a对象都有内容,那么把b赋值给a,需要以下三个步骤
三步骤:先清空b的内容,再为b分配一个空间,然后再将a的内容拷贝过来给b。
下面看函数

inline String& String::operator = (const String& str){    if (this == &str)  //检测自我赋值         return *this;    delete[] m_data;    m_data = new char[ strlen(str.m_data + 1)];    strcpy(m_data,str.m_data);    return *this;}

注意:自我赋值检测的必要性,谁会做自我赋值的动作呢?所以说这样效率高,
如果没有写这两行,不仅仅是效率问题。如果没有这两行,可以试试赋值自己会出现什么bug。所以我自我赋值的检测是必要的

再看一个程序,看下什么时候调用 big three 函数

String str1("hello");  //调用构造函数String str2(str1);    //调用拷贝构造函数String str3 = str1;   //调用拷贝构造函数str3 = str2;          //调用拷贝赋值函数

先给出String的完整程序

#ifndef __MYSTRING__#define __MYSTRING__class String{public:                                    String(const char* cstr=0);                        String(const String& str);                       String& operator=(const String& str);            ~String();                                       char* get_c_str() const { return m_data; }private:   char* m_data;};#include <cstring>inlineString::String(const char* cstr){   if (cstr) {      m_data = new char[strlen(cstr)+1];      strcpy(m_data, cstr);   }   else {         m_data = new char[1];      *m_data = '\0';   }}inlineString::~String(){   delete[] m_data;}inlineString& String::operator=(const String& str){   if (this == &str)      return *this;   delete[] m_data;   m_data = new char[ strlen(str.m_data) + 1 ];   strcpy(m_data, str.m_data);   return *this;}inlineString::String(const String& str){   m_data = new char[ strlen(str.m_data) + 1 ];   strcpy(m_data, str.m_data);}#include <iostream>using namespace std;ostream& operator<<(ostream& os, const String& str){   os << str.get_c_str();   return os;}#endif
1 0
原创粉丝点击