zoukankan      html  css  js  c++  java
  • 面试题1:赋值运算符函数

    题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。
    class CMyString
    {
    public:
    CMyString(char *pData=NULL);//构造函数
    CMyString(const CMyString& str);//拷贝构造函数
    ~CMyString();//析构函数
    private:
    char* m_pData;//数据域,字符指针
    };

    介绍重载赋值

      重载操作符是一些函数,其名字为关键字operator后紧跟需要重载的运算符,比如"operator="表示需要重载"="。像任何其他函数一样,操作符函数有一个返回值和一个形参表。形参表必须具有与该操作符操作数数目相同的形参,但是如果操作符是一个成员函数,它的第一个操作数隐式绑定到this指针,因此形参表中的参数会减少一个。因为赋值运算符必须是类的成员函数,所以this绑定到左操作数的指针。因此,赋值操作符只接受一个形参,且该形参是同一类型的对象,右操作数一般作为const引用传递,跟拷贝构造函数相同。

      赋值操作符的返回类型应该与内置类型赋值运算的返回类型相同,内置类型的赋值运算返回对左操作数的引用,因此赋值操作符也返回对同一类类型的引用。赋值必须返回对*this的引用,也就是左操作数的引用。一般而言,赋值操作符与复合赋值操作符应返回左操作数的引用。

      从上述基础知识我们知道了重载赋值操作符是一个类的成员函数,这个函数的返回类型是左操作数的引用,也就是*this,并且这个函数的参数是一个同类型的常引用变量。通过上述知识我们可以确定重载操作符函数为:

    CMyString& operator=(const CMyString& str);//重载运算符

    完整的代码实现如下:

    View Code
    #include<iostream>
    #include<stdlib.h>
    using namespace std;
    
    class CMyString
    {
    public:
        CMyString(char *pData=NULL);//构造函数
        CMyString(const CMyString& str);//拷贝构造函数
        ~CMyString();//析构函数
        CMyString& operator=(const CMyString& str);//重载运算符
        void Print();//打印字符串
    private:
        char* m_pData;//数据域,字符指针
    };
    
    void CMyString::Print()
    {
        cout<<m_pData<<endl;
    }
    
    //构造函数
    CMyString::CMyString(char *pData)
    {
        if(pData==NULL)//如果构造函数的参数为空
        {
            m_pData=new char[1];
            m_pData[0]='\0';//初始化字符串,内容为'\0'
        }
        else//如果构造函数的参数不为NULL,那么首先求出字符串长度,然后new一个长度为len+1的字符数组
        {
            int len=strlen(pData);
            m_pData=new char[len+1];
            strcpy(m_pData,pData);//字符串拷贝
        }
    }
    
    //析构函数
    CMyString::~CMyString()
    {
        delete[] m_pData;
    }
    
    //拷贝构造函数,拷贝构造函数与构造函数的思路非常类似。
    CMyString::CMyString(const CMyString& str)
    {
        int len=strlen(str.m_pData);
        m_pData=new char[len+1];
        strcpy(m_pData,str.m_pData);
    }
    
    //重载运算符
    CMyString& CMyString::operator=(const CMyString& str)
    {
        //如果传入的参数与当前的实例是同一个实例,则直接返回自身
        if(this==&str)
            return *this;
    
        //释放实例自身已有内存
        delete[] m_pData;
        m_pData=NULL;
    
        //在删除自身内存以后在重新new一个长度为len+1的字符数组,类似拷贝构造函数
        int len=strlen(str.m_pData);
        m_pData=new char[len+1];
        strcpy(m_pData,str.m_pData);
    }
    
    void main()
    {
        char* text="Hello World!";
        CMyString str1(text);
        CMyString str2;
        str2=str1;
    
        str1.Print();
        str2.Print();
    
        system("pause");
    }

    存在的问题:

    在上述代码中,我们首先释放实例自身已有内存,

    //释放实例自身已有内存
    delete[] m_pData;
    m_pData=NULL;

    然后再去开辟一块内存空间让m_pData指向这块内存空间,

    //在删除自身内存以后在重新new一个长度为len+1的字符数组,类似拷贝构造函数
    int len=strlen(str.m_pData);
    m_pData=new char[len+1];

    最后使用strcpy进行字符串赋值。

    strcpy(m_pData,str.m_pData);

    如果因为内存不足,在new char[len+1]阶段抛出异常,那么这时候因为已经释放了m_pData,导致m_pData指向一个空指针,这样可能会导致程序崩溃。有两种方案解决上述问题:

    • 先用new分配新内容,然后删除自己已有内容,最后进行赋值。
    • 创建一个临时实例,交换临时实例与当前实例的m_pData。代码如下:
    View Code
    //重载运算符
    CMyString& CMyString::operator=(const CMyString& str)
    {
        if(this!=&str)
        {
            CMyString strTemp(str);//使用构造函数创建一个临时对象
            //交换临时对象与当前对象的m_pData值
            char* pTemp=strTemp.m_pData;
            strTemp.m_pData=m_pData;
            m_pData=pTemp;
        }
        return *this;
    }

    这样的一个好处是在运行完if语句以后,因为除了strTemp的作用于,该实例会自动调用析构函数,把strTemp.m_pData所指向的内存释放掉,而此时strTemp.m_pData指向的是实例原先m_pData指向的内存,并没有释放当前指向的pTemp这一块内存。还有一点是通过构造函数为临时实例分配内存,如果在new char过程中抛出异常,并没有改变该实例m_pData所指向的内容,也没有释放内存,所以是异常安全性的。

  • 相关阅读:
    每日总结
    每日总结
    《构建之法》读后感3
    每日博客
    每日博客
    每日博客
    每日博客
    预开发软件书
    每日博客
    每日博客
  • 原文地址:https://www.cnblogs.com/xwdreamer/p/2500632.html
Copyright © 2011-2022 走看看