为什么类的拷贝构造参数加引用、重载赋值函数的返回值和参数加引用

class string

{

public:

string(const char *str=NULL);

string(const string& str);     //copy构造函数的参数为什么是引用呢? 

string& operator=(const string & str); //赋值函数为什么返回值是引用呢?参数为什么是引用呢?

~string();

};

下面我就给大家解释一下:

class String1
{
public:
    String1(const char*str = "")//默认构造参数//可以不用参数
    {

          m_str = new char[strlen(str) + 1];

           strcpy(m_str, str);

          cout << "create String" << endl;
    }
    String1(const String1 &s)
    {
         m_str = new char[strlen(s.m_str) + 1];
          strcpy(strm_,s.m_str);
         cout << "copy String" << endl;
    }
    String1& operator=(const String1 &s)
    {

        delete[] m_str;

       m_ str = new char[strlen(s.m_str) + 1];

       strcpy(m_str, s,m_str);

        cout << "=====" << endl;    
          return *this;
    }
    ~String1()
    {
       delete[]m_str; 
       m_str=nullptr; 
     }
    char* m_str;

};
void main()
{
    String1 str1("a");
    String1 str2("b");
    String1 str3(str1);
    String1 str4=str2;
    str4 = str1;

}

 

1.首先给大家解释下问什么拷贝构造函数参数为什么要加引用?

    列如:String1 str3(str1); 我们假如拷贝构造函数参数不是引用类型的话, 那么将使得 str3.String1(str1)变成str1传值给str3.String1(String1 s)即 String1 s = str1,因为 s 没有被初始化, 所以 String1 s = str1 继续调用拷贝构造函数,接下来的是构造s,也就是 s.String1(str1),必然又会有s传给String1(String1 s), 即 String1 s = str1;那么又会触发拷贝构造函数,就这下永远的递归下去。

    所以, 拷贝构造函数是必须要带引用类型的参数的, 而且这也是编译器强制性要求的

2.解释完上面的问题我就给大家解释下为什么重载赋值函数的参数和返回值要用引用

    还是一样假设赋值函数参数没有引用String1& operator=(const String1 s) ,那么会出现这样的结果:

image

    大家可能会有疑问为什么,会出现三次“copy String”,这就是问题的根源,第三次出现的原因是因为str4=str1,这个赋值语句,其实是将str1以参数形式传递给String1& operator=(const String1 s) 由于s不是引用类型所以会调用拷贝构造函数创建临时对象,将实参传递给形参,因为你的成员变量中有指针类型 ,如果你自定义的构造函数,那就相当于多调用了一次拷贝构造,关系不大就是执行效率低点,但是你的拷贝构造函数是系统默认的拷贝构造函数(浅克隆),那就意味着你的形参的的成员变量指针和实参的成员变量指针指向的是同一个内存空间,如果函数结束,形参调用析构函数,释放对象成员指针指向空间,那么实参成员指针指向空间也被释放,如果再用实参调用成员方法访问成员指针指向的数据系统就会报错。

     返回值加引用的原理和参数加引用的原理一样,只是细节不同而已这是不加返回值引用的运行结果。

image

     如果返回值时, return *this后马上就调用拷贝构造函数,创建临时对象。但是万一由于没有自定义拷贝构造函数  ,就会调用默认的拷贝构造函数。我们知道调用默认的拷贝构造函数时当在类中有指针成员时就会出错(浅拷贝)。因为返回值对象成员指针指向的内存空间会在函数结束后调用析构函数释放掉,那么临时对象的成员指针指向的空间也会被释放掉。

      所以如果你不用引用做返回时,就必须定义自定义的拷贝构造函数。

      当然咯  有指针成员时 必须要注意自定义拷贝构造了

3.那么,为什么要重载赋值函数呢?

赋值运算符的重载函数:

       用一个已存在的对象赋值给相同类型的已存在对象。(浅拷贝)

默认赋值运算符的重载函数:

       赋值运算符重载函数用于类对象的赋值操作,当我们未实现该函数时,编译器会自动为我们实现该函数。同拷贝构造函数一样,系统默认的赋值运算符重载函数是浅拷贝,类中含有指针类型的变量,须自定义赋值运算符重载函数用深拷贝来实现。

4.为什么要避免自赋值呢?

1)自己给自己赋值完全是毫无意义,为了效率。

2)如果类的数据成员中含有指针,自赋值有时会导致灾难性的后果。对于指针间的赋值,先要将p所指向的空间delete掉,然后再为p重新分配空间,将被拷贝指针所指的内容拷贝到p所指的空间。如果是自赋值,那么p和被拷贝指针是同一指针,在赋值操作前对p的delete操作,将导致p所指的数据同时被销毁

所以虽然指针是C++的魅力所在,但是它同样很危险,所以请细心使用。

posted @ 2019-08-08 20:41  Shallow_tipsy  阅读(645)  评论(0编辑  收藏  举报