首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C++——拷贝构造和 运算符重载

C++——拷贝构造和 运算符重载

作者头像
lovevivi
发布2022-12-22 14:05:37
发布2022-12-22 14:05:37
4920
举报
文章被收录于专栏:萌新的日常萌新的日常

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

1. 拷贝构造函数

1.值传递

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//全缺省构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    date(date d)//值传递  date d 会报错
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(d1);//拷贝构造
    return 0;
}
  • 这里为什么会报错?
  • 存在递归拷贝

d对象初始化时需要自动调用构造函数, 调用函数之前需先传参, 传参过程中又是一个拷贝构造

  • 上面是一个严谨的分析过程,没看懂就继续往下看

d2 (d1) ——(用d1拷贝构造d2) 对象初始化之前自动调用构造函数 调用函数之前先传参 将参数d1传给构造函数 的参数d 的过程中, 由于是传值调用,相当于将d1的拷贝传给d 即拷贝构造 —— date d (d1) (用d1构造d) 再次进行拷贝构造 参数 d1传给 参数d 的过程中,又进行拷贝构造 无休止递归下去

2. 引用传递

由于d为d1的别名,所以 参数d1传给 d的过程中, 不会发生拷贝构造

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//全缺省构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    date(const date&amp; d)//引用传递
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(d1);//拷贝构造
    return 0;
}

加入const,是为了防止由于操作失误导改变d本身 如:假设 d._year =_year , _year代表d2._year ,将d2中的年赋值给d1的年,就会导致报错

3. 浅拷贝问题

代码语言:javascript
复制
#include<iostream>
using namespace std;
class stack
{
public:
    stack(int n)//构造函数
    {
         _a = (int*)malloc(sizeof(int) * n);
         _size = 0;
         _capity = n;
    }
    ~stack()//析构函数
    {
        free(_a);
        _a = nullptr;
        _size = _capity = 0;
    }

private:
    int * _a;
    int _size;
    int _capity;
};
int main()
{
    stack s1(10);
    stack s2(s1);//拷贝构造
    return 0;//空间会被释放两次,程序崩溃
}
  • 以上代码为什么一运行就会报错?

-

s1._a 指针指向 开辟的10个字节的空间,由于是拷贝构造,所以将s1._a指针的值 赋值给了 s2._a指针, 使s2._a指针同样指向与s1._a相同的位置, 又由于是先构造的后析构,所以理应先析构 s2 ,s2free这块空间后,由于s1._a与s2._a指向同一个位置,s1还会对这块空间再次free 同一块空间释放两次,会导致崩溃

2. 运算符重载

1. 为什么不可以直接比较?

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    return 0;
}
  • date 实例化出的对象 d1 与d2 的大小可以直接比较嘛?
  • 不可以,自定义类型是自己定义的,怎么比较大小由自己规定 内置类型 是编译器自己定义的类型,它知道要怎么比

C++为了增强代码的可读性引入运算符重载,运算符重载是具有特殊函数名的函数

2. 操作符 ==

1. 错误写法
代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
//private://需要将_year等改为public
    int _year;
    int _month;
    int _day;
};
bool operator ==(const date&amp; d1, const date&amp; d2)//由几个参数,就接收几个
{
//判断年月日是否都相等
    return d1._year == d2._year &amp;&amp; d1._month == d2._month &amp;&amp; d1._day == d2._day;
}
int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 == d2;
    cout << (d1 == d2) << endl;// 0 代表假
    return 0;
}

但是这种方法会 把date类中prviate的里面的成员变量变成共有的 否则就会报错

2. 正确写法

写入类中作为成员函数

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    bool operator ==( const date&amp; d2)// 由于隐藏的this指针的存在,&amp;d1传过来由this指针接收
    {
        return _year == d2._year &amp;&amp; _month == d2._month &amp;&amp; _day == d2._day;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 == d2;//  d1.operator==(d2) 等价于 d1.operator==(&amp;d1,d2)
    cout << (d1 == d2) << endl;
    return 0;
}

传入类中,由于隐藏的this指针的存在,所以&d1传过去被this接收了,_year代表 d1._year 但是因为它是隐藏的,所以&d1也不需要表现出来直接传入d2即可

3. 操作符 >

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    bool operator >( const date&amp; d2)// 由于隐藏的this指针的存在,&amp;d1传过来由this指针接收
    {
        if (_year > d2._year)//年大为大
        {
            return true;
        }
        else if (_year == d2._year &amp;&amp; _month > d2._month) //年相,月大为大
        {
            return true;
        }
        else if (_year == d2._year &amp;&amp; _month == d2._month &amp;&amp; _day > d2._day)//年 月相等,天大为大
        {
            return true;
        }
        else
        {
            return false;
        }
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 > d2;//等价于 operator>(&amp;d1,d2)
    cout << (d1 > d2) << endl;
    return 0;
}

判断大于时 ,同样存在一个隐藏的this指针,我们只需要判断 年大的就为大 ,年相等 月大的就为大,年月相等,天大的就为大,其他情况都为假

4. 注意事项

1. 不能通过连接其他符号来创建新的操作符 (如 operator@) 2.重载操作符必须有一个类类型或者枚举类型的操作数

date 就属于是自定义类型

3.用于内置类型的操作符,其含义不能改变(如 int 加法 不能改变) 4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参

this指针是隐藏的,所以也不用传过来,this指针默认为第一个参数,并且代表d1

5. ( . * ) (:: ) (sizeof ) (? : 三目运算符) ( . ) 以上5个运算符不能重载

5.操作符 !=

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    bool operator==(const date&amp; d2)//d1==d2
    {
        return _year == d2._year &amp;&amp; _month == d2._month &amp;&amp; _day == d2._day;
    }
    bool operator!=(const date&amp; d2)
    {
        return !(*this == d2);//借助上面的d1==d2的相反即 d1!=d2
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 != d2;
    cout << (d1 != d2) << endl;
    return 0;
}

借助上面已经写好的d1==d2 ,取其相反 即为 d1!=d2

6. 操作符 <

代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    bool operator >(const date&amp; d2)// 由于隐藏的this指针的存在,&amp;d1传过来由this指针接收
    {
        if (_year > d2._year)//年大为大
        {
            return true;
        }
        else if (_year == d2._year &amp;&amp; _month > d2._month) //年相,月大为大
        {
            return true;
        }
        else if (_year == d2._year &amp;&amp; _month == d2._month &amp;&amp; _day > d2._day)//年 月相等,天大为大
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    bool operator<(const date&amp; d2)// d1 < d2
    {
        return !(*this > d2);
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 < d2;
    cout << (d1 < d2) << endl;
    return 0;
}

若直接写d1<d2 的条件太复杂,直接采用 d1 >d2 的相反,*this 代表 d1

7 .赋值操作符

1. 正常使用
代码语言:javascript
复制
#include<iostream>
using namespace std;
class date
{
public:
    date(int year = 1, int month = 1, int day = 1)//构造
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    void operator=(const date&amp; d2)//为了防止自己给自己赋值的事情发生,如:d1=d1
    {
        if ( this == &amp;d2)
        {
            _year = d2._year;
            _month = d2._month;
            _day = d2._day;
        }
    }
private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    date d1(2022,12,21);
    date d2(2022,12,22);
    d1 =d2;
    return 0;
}
2. 连续赋值情况的考虑

如果为内置类型,如int 则可以进行连续赋值 对于 int 来说,j=z ,返回值为z ,i=z,生成最终结果 但是如果为自定义类型,连续赋值就不可以,从右开始 d3=d1 同样也应该有一个返回值,用来与d2进行赋值

传值返回

由于*this为date 类型,属于传值返回,即返回一个临时变量,所以需进行拷贝构造 同时也会多开辟一块空间存储, 就会导致当return ( * this) 返回时,传入 拷贝构造中创建临时变量 ,再次从中返回时,才能返回到 主函数中

传引用返回

传 * this的引用作为变量的别名,相当于 * this本身,不会消耗一块空间,自然在return*this 返回时,不会进入拷贝构造中,而是直接返回

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 拷贝构造函数
    • 1.值传递
    • 2. 引用传递
    • 3. 浅拷贝问题
  • 2. 运算符重载
    • 1. 为什么不可以直接比较?
    • 2. 操作符 ==
      • 1. 错误写法
      • 2. 正确写法
    • 3. 操作符 >
    • 4. 注意事项
    • 5.操作符 !=
    • 6. 操作符 <
    • 7 .赋值操作符
      • 1. 正常使用
      • 2. 连续赋值情况的考虑
      • 传值返回
      • 传引用返回
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档