c++ 操作符operator的重载

本文详细介绍了C++中自定义Point类的运算符重载实现,包括构造函数、赋值运算符、算术运算符及输入输出运算符的重载。通过具体代码示例,展示了如何正确实现拷贝构造、移动构造、拷贝赋值和移动赋值,以及成员运算符和非成员运算符的重载。

一个模拟Point点的类,内置函数类型为int.

实现默认构造,自定义构造,拷贝构造、移动构造,拷贝赋值、移动赋值
重载成员运算符函数operator+=”、“operator+”、“operator<<
重载非成员运算法函数operator*”

1、类的声明

class TestOperatorPoint {
public:
    // default
    TestOperatorPoint() = default;

    TestOperatorPoint(int x, int y) : 
        x(x), y(y) //参数名、成员变量可以同名
    {}


    // copy constructor
    TestOperatorPoint(const TestOperatorPoint& pt);
    // move constructor
    TestOperatorPoint(TestOperatorPoint&& pt);
    //// 错误的拷贝赋值
    //TestOperatorPoint operator=(const TestOperatorPoint& pt);
    // copy assign
    TestOperatorPoint& operator=(const TestOperatorPoint& pt);
    // move assign
    TestOperatorPoint& operator=(TestOperatorPoint&& pt);

	////////////////// operations
    TestOperatorPoint operator+(const TestOperatorPoint& pt)
    TestOperatorPoint& operator+=(const TestOperatorPoint& pt);

    //  输出重载(友元函数)
    friend std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt);


//private:
    int x, y;
};

2、类的具体实现

2.1 构造函数和赋值函数

以下类定义时的实现,类定义外实现是需要加类作用域。

    // copy constructor
    TestOperatorPoint(const TestOperatorPoint& pt)
    {
        std::cout << "copy constructor" << std::endl;
        x = pt.x;
        y = pt.y;
    }

    // move constructor
    TestOperatorPoint(TestOperatorPoint&& pt)
    {
        std::cout << "move constructor" << std::endl;
        x = std::move(pt.x);  // x,y是基本类型,移动时不影响
        y = std::move(pt.y);
    }

    //// 错误的拷贝赋值
    //TestOperatorPoint operator=(const TestOperatorPoint& pt)
    //{
    //    std::cout << "wrong copy assign" << std::endl;
    //   // x = pt.x;
    //    //y = pt.y;
    //   // return *this;   // 如果不返回引用,会将*this对象进行一次拷贝赋值,
    //    return pt;       // 直接返回也会再进行一次拷贝赋值,
    //}

    // copy assign
    TestOperatorPoint& operator=(const TestOperatorPoint& pt)
    {
        std::cout << "copy assign" << std::endl;
        x = pt.x;
        y = pt.y;
        return *this;   
    }

    // move assign
    TestOperatorPoint& operator=(TestOperatorPoint&& pt)
    {
        std::cout << "move assign" << std::endl;
        x = std::move(pt.x);  // x,y是基本类型,移动时不影响; 
        y = std::move(pt.y);
        return *this;
    }

需要说明的是,当成员变量是非基本类型,如string、vector、指针对象以及其他类对象时,move的作用在于减少过多的构造、赋值操作。
例如,如下操作

string str1 = "abc";
string str2 = std::move(str1)

结果是 str1为"",str2为"abc"。 str2进行的是移动构造。


测试代码

TestOperatorPoint pt1;
TestOperatorPoint pt2(3, 4);   //自定义构造

TestOperatorPoint pt3(pt2);   // 拷贝构造
pt1 = pt2;                    // 拷贝赋值,   pt1已经存在


TestOperatorPoint pt4;
TestOperatorPoint pt5(1, 1);
TestOperatorPoint pt6(2, 2);

TestOperatorPoint pt7(std::move(pt5));   // 移动构造
pt4 = std::move(pt6);                    // 拷贝赋值,   pt4已经存在

2.2 类成员操作符重载(包括友元函数)

  • 赋值(=)、下标([ ])、调用( ( ) )和成员访问箭头( -> )必须是成员函数;
  • 改变对象状态、递增递减、解引用等与自身类型密切相关的运算符,通常是成员函数;
	////////////////// operations

    TestOperatorPoint operator+(const TestOperatorPoint& pt)
    {
        TestOperatorPoint tmp;
        tmp.x = x + pt.x;
        tmp.y = y + pt.y;
        return tmp;     // 返回局部临时变量,编译器优化为 移动操作,不要画蛇添足
    }

    TestOperatorPoint& operator+=(const TestOperatorPoint& pt) 
    {
        x += pt.x;
        y += pt.y;
        return *this;   // 明确是对自己要修改,要返回引用。否则多一次拷贝构造。
    }


操作符重载的测试代码,如下

TestOperatorPoint pt1(1, 2);
TestOperatorPoint pt2(3, 4);

// 移动构造(先执行pt1的操作符重载成员函数,返回临时变量进行移动构造)
TestOperatorPoint pt3 = pt1 + pt2; 


TestOperatorPoint pt4(0,0);
pt4 += pt1;  // 仅调用pt4的+=函数

2.3 普通的非成员操作符重载

  • 算术、相等、关系和位运算,以及可以转换任意一端的运算符对象,应该定义为普通的普通的非成员操作符重载。
  • 输入输出运算符必须是普通的非成员函数。
2.3.1 operator*(const TestOperatorPoint& pt, T v)和operator*(T v,const TestOperatorPoint& pt)

例如,一个TestOperatorPoint对象,仅想将其成员变量x,y同时扩大一个倍数(可以是int, float, double等非类内置类型int),这种情况可以定义为

// 混合类型表达式定义为普通的非成员函数(第二个参数不是同一个类类型)
template<typename T>
TestOperatorPoint operator*(const TestOperatorPoint& pt, T v)
{
    TestOperatorPoint tmp;
    tmp.x = pt.x * v;     // 会强转为TestOperatorPoint.x的成员类型
    tmp.y = pt.y * v;
    return tmp;
}

测试使用 TestOperatorPoint pt5 = pt4 * 3.14;,是没有问题;

但是如果我们先交换乘法顺序(对称性),如TestOperatorPoint pt6 = 3.14 * pt4; 这里是无法执行的,因为 3.14double类型,是没有定义重载operator*(TestOperatorPoint)函数的,因此我们需要定义如下普通操作符函数

// 上面乘法运算的重载,对称性
template<typename T>
TestOperatorPoint operator*(T v, const TestOperatorPoint& pt)
{
    TestOperatorPoint tmp;
    tmp.x = pt.x * v;     // 会强转为TestOperatorPoint.x的成员类型
    tmp.y = pt.y * v;
    return tmp;
}

此时,TestOperatorPoint pt6 = 3.14 * pt4;能够正常执行。

2.3.2 重载输入操作符函数 operator<<

通常自定义打印输出的<<函数时,需要访问类的非公有数据成员,因此定义为友元函数。若不需要,可以定义为非友元函数。

自定义打印输入重载运算符。

    //  输出重载(定义时实现)
    friend std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt);
    {
        out << "[" << pt.x << ", " << pt.y << "]";
        return out;
    }

当在外部实现友元函数时,函数不要加类的作用域。

std::ostream& operator<< (std::ostream& out, const TestOperatorPoint& pt)
{
    out << "[" << pt.x << ", " << pt.y << "]";
    return out;
}

例如测试,std::cout << pt4 << std::endl;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值