c++--运算符重载

1.重载的运算符
(1).重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。
(2).除了重载的函数调用运算符operator()之外,其他重载运算符不能含有默认实参。
(3).对一个重载的运算符,其优先级和结合律与对应的内置运算符保持一致。
(4).当一个重载的运算符是成员函数时,this绑定到左侧运算对象。故,成员运算符函数的参数数量比运算对象数量少一个。
(5).对一个运算符函数,或者是类的成员,或者至少含一个类类型的参数。

2.运算符函数的调用形式:
// 假设有非成员函数 operator+
显式:operator+(data1, data2);
隐式:data1+data2
// 假设有成员函数 operator+
显式:data1.operator+(data2)
隐式:data1+data2

3.关于将运算符函数定义为成员还是非成员:
赋值(=),下标([]),调用(()),成员访问箭头(->)必须为成员。

4.运算符重载
4.1.重载<<
(1).通常,输出运算符的第一个形参是一个非常量ostream对象的引用。
a.为引用,是因为ostream对象不能被拷贝。
b.非常量是因为,向流写入内容会改变其状态。
(2).第二个形参一般是一个常量的引用,该常量是我们想打印的类类型。
a.是引用,是因为可以避免值拷贝。
b.常量是因为,打印对象不会改变对象的内容。
(3).为与其他输出运算符一致,operator<<一般返回它的ostream形参。
(4).一个实例

#include <iostream>
class A
{
public:
	int m_a;
	int m_b;
};

std::ostream &operator<<(std::ostream &os, const A&item)
{
	os << item.m_a << " " << item.m_b;
	return os;
}

4.2.重载>>
(1).通常,输入运算符的第一个形参是运算符将要读取的流的引用,
(2).第二个形参是将要读入到的非常量对象的引用。
(3).该运算符通常会返回某个给定流的引用。
(4).一个实例

#include <iostream>
class A
{
public:
	int m_a;
	int m_b;
};

std::istream& operator>>(std::istream &is, A& item)
{
	char a;
	is >> item.m_a >> a >> item.m_b;
	return is;
}

执行输入时,可能发生下列错误:
(1).流含有错误类型的数据时,读取操作可能失败。
(2).读取操作到达文件末尾或遇到输入流的其它错误时也会失败。

4.3.算术运算符
4.3.1.最佳实践
(1).类定义了一个算术运算符时,一般也要定义一个复合赋值运算符。
(2).一般借用复合赋值运算符来定义算术运算符。
(3).通常,算术和关系运算符定义为非成员函数以允许左侧或右侧的运算对象进行转换。
(4).这些运算符一般不需改变运算对象的状态,故形参可以是常量的引用。
(5).算术运算的结果一般位于局部变量内。操作完成一般返回局部变量的副本作为结果。
(6).复合赋值运算符更适合作为类成员来定义。
(7).一个实例

#include <iostream>
class A
{
friend bool operator==(const A &a1, const A &a2);
public:
	A():m_a(0), m_b(0){}
    A(int i):m_a(i), m_b(i){}
    void print()
    {
        printf("a_%d,b_%d\n", m_a, m_b);
    }

    A& operator+=(const A &a2)
    {
        m_a = m_a + a2.m_a;
        m_b = m_b + a2.m_b;
        return *this;
    }
private:
	int m_a;
	int m_b;
};

A operator+(const A &a1, const A &a2)
{
	A sum = a1;
	sum += a2;
	return sum;
}

bool operator==(const A &a1, const A &a2)
{
	return a1.m_a == a2.m_a && a1.m_b == a2.m_b;
}

bool operator!=(const A& lhs, const A& rhs)
{
	return !(lhs == rhs);
}

int main()
{
    A a1(10);
    A a2(10);
    A a3 = a1 + a2;
    a3.print();
    return 0;
}

上述是一个为类型A定义运算符+,+=,==,!=的实例。我们采用类成员定义复合赋值。
下述是一个复合赋值采用非成员的版本。

#include <iostream>
class A
{
friend bool operator==(const A &a1, const A &a2);
friend A operator+=(A &a1, const A &a2);
public:
	A():m_a(0), m_b(0){}
    A(int i):m_a(i), m_b(i){}
    void print()
    {
        printf("a_%d,b_%d\n", m_a, m_b);
    }

private:
	int m_a;
	int m_b;
};

// 返回值是复合赋值作为表达式的结果
// 参数1是复合赋值左边操作数,此操作数会被复合赋值所影响。
A operator+=(A &a1, const A &a2)
{
    a1.m_a += a2.m_a;
    a1.m_b += a2.m_b;
    return a1;
}

A operator+(const A &a1, const A &a2)
{
	A sum = a1;
	sum += a2;
	return sum;
}

bool operator==(const A &a1, const A &a2)
{
	return a1.m_a == a2.m_a && a1.m_b == a2.m_b;
}

bool operator!=(const A& lhs, const A& rhs)
{
	return !(lhs == rhs);
}

int main()
{
    A a1(10);
    A a2(10);
    A a3 = a1 + a2;
    a3.print();
    return 0;
}

4.4.关系运算符
4.4.1.最佳实践
(1).通常,关系运算符定义为非成员函数以允许左侧或右侧的运算对象进行转换。
(2).这些运算符一般不需改变运算对象的状态,故形参可以是常量的引用。
以下是一个关系运算符重载的例子

#include <iostream>

class A
{
friend bool operator==(const A& a1, const A& a2);
public:
    A():m_a(0),m_b(0){}
    A(int i):m_a(i),m_b(i){}
private:
    int m_a;
    int m_b;
};

bool operator==(const A& a1, const A& a2)
{
    if(a1.m_a == a2.m_a && a1.m_b == a2.m_b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool operator!=(const A& a1, const A& a2)
{
    return !(a1 == a2);
}

int main()
{
    A a1(1), a2(2);
    printf("a1==a2?%d\n", a1 == a2);
    return 0;
}

4.5.赋值运算符
(1).赋值运算符限制性因素有:
赋值作为表达式存在一个结果数值。
赋值会修改左边操作数状态。
(2).最佳实践
a,赋值一般实现为成员函数
b.为类型自定义赋值运算符时,运算符内部必须处理:
b.1.基类部分赋值
b.2.类型成员赋值
c.为类型自定义赋值运算符时,需要考虑自赋值场景。
d.赋值与拷贝针对包含资源的类涉及浅拷贝,深拷贝的概念。又涉及到左值,右值,移动语义,完美转发的概念。这些后续统一放在专门一篇讲解。
(3).一个实例

#include <iostream>

class Norm1
{
public:
    Norm1(char i):m_i(i){}
    void print()
    {
        printf("norm1:%c\n", m_i);
    }
private:
    char m_i;
};

class Norm2
{
public:
    Norm2(char i):m_i(i){}
    void print()
    {
        printf("norm2:%c\n", m_i);
    }
private:
    char m_i;
};

class Base
{
public:
    Base(char c = 'b'):m_n1(c),m_n2(c){}
    void print()
    {
        printf("base:\n");
        m_n1.print();
        m_n2.print();
    }
private:
    Norm1 m_n1;
    Norm2 m_n2;
};

class A : public Base
{
public:
    A(char c = 'a'):Base(c), m_n1(c+1),m_n2(c+1){}
    A& operator=(const A& a)
    {
        if(this == &a)
        {
            return *this;
        }

        Base::operator=(a);
        m_n1 = a.m_n1;
        m_n2 = a.m_n2;
        return *this;
    }
    void print()
    {
        Base::print();
        m_n1.print();
        m_n2.print();
    }
private:
    Norm1 m_n1;
    Norm2 m_n2;
};

int main()
{
    A a1('x');
    A a2('a');
    a1.print();
    a2.print();
    a1 = a2;
    printf("after:\n");
    a1.print();
    a2.print();
    return 0;
}

4.6.下标运算符
对下标运算符最好定义常量和非常量版本,对常量版本返回常量引用。
4.6.1.强制性约束
从强制性约束角度要求形参列表只能有一个形参

#include <iostream>
class Norm
{
public:
    void print() const
    {
        printf("Norm_%d\n", m_i);
    }

private:
    int m_i;
};

class A
{
public:
    int operator[](Norm n)
    {
        printf("test[]\n");
        return 1;
    }

private:
    Norm m_arrA[10];
};

int main()
{
    A a;
    Norm n;
    int i = a[n];
    return 0;
}

上述是一个合法的重载的[]运算符。
4.6.2.最佳实践
(1).形参最好是std::size_t。
(2).重载运算符最好提供常量版本,非常量版本。
这样对类型的常量实例,非常量实例均可执行[]运算。
(3).返回值应该设计成引用类型。
标准库类型[]运算符也均返回引用类型。

#include <iostream>
class Norm
{
public:
    void print() const
    {
        printf("Norm_%d\n", m_i);
    }

private:
    int m_i;
};

class A
{
public:
    Norm &operator[](std::size_t i)
    {
        printf("[]\n");
        return m_arrA[i];
    }

    const Norm &operator[](std::size_t i) const
    {
        printf("[]const\n");
        return m_arrA[i];
    }

private:
    Norm m_arrA[10];
};

int main()
{
    A a;
    a[1].print();
    const A a1 = A();
    a1[1].print();
    return 0;
}

上述是一个[]运算符重载的最佳实例

4.7.递增和递减运算符
4.7.1.强制性约束
(1).对后置版本的++,--,形参列表必须提供单个类型为int的形参。
(2).对前置版本的++,--,形参列表必须为空。

#include <iostream>

class A
{
public:
    A() : m_i(0) {}

    int operator++()
    {
        printf("++()\n");
        m_i++;
        return 1;
    }

    int operator++(int n)
    {
        printf("++(int)\n");
        A temp = *this;
        m_i++;
        return 1;
    }

private:
    int m_i;
};

int main()
{
    A a;
    a++;
    ++a;
    return 0;
}

上述是一个合法的前置++,后置++运算符的重载实现。

4.7.2.最佳实践
(1).定义++,--运算符时候,应该同时定义前置版本,后置版本。
(2).前置版本应该返回类型自身的引用。后置版本应该返回类型自身。
与标准库类型保持一致。
(3).不应该提供常量版本。

#include <iostream>
class Norm
{
private:
    int m_i;
};

class A
{
public:
    A() : m_i(0) {}

    A &operator++()
    {
        printf("++()\n");
        m_i++;
        return *this;
    }

    A operator++(int)
    {
        printf("++(int)\n");
        A temp = *this;
        m_i++;
        return temp;
    }

private:
    int m_i;
};

int main()
{
    A a;
    a++;
    ++a;
    return 0;
}

上述是一个前置++,后置++的最佳实践。

4.8.成员访问运算符:*,->
4.8.1.强制性约束
(1).对*运算符,必须提供0个形参或1个形参。
(2).对->运算符,必须提供0个形参。

#include <iostream>

class Norm
{
public:
    Norm(int i) : m_i(i) {}
   
    void print()
    {
        printf("i_%d\n", m_i);
    }

private:
    int m_i;
};

class A
{
public:
    int operator*(Norm)
    {
        printf("*\n");
        return 1;
    }

    int operator->()
    {
        printf("->\n");
        return 2;
    }

private:
    int m_i;
};

int main()
{
    A a;
    A *p = &a;
    Norm n(1);
    p->operator->();
    p->operator*(n);
    a.operator*(n);
    a.operator->();
    // a->print();
    //*a;
    return 0;
}

上述我们为类型定义了一个合法的*运算符,一个合法的->运算符。

4.8.2.最佳实践
(1).*运算符的形参应该是0个。
因为只有这样,才能使用A a; *a;这样的方式触发*运算符访问。
(2).->运算符的返回类型,应该要么是重载了->运算符的类型,要么是指针类型。
因为如下述,我们执行A a; a->print();这样的操作时。操作处理细节为:
1.若a重载了->运算符。则执行运算符,获得返回值。否则,报错。
2.若返回值是指针类型,进入到对指针类型执行->情形。终止。
3.若返回值不是指针类型且重载了->运算符,则执行运算符,获得返回值。否则,报错。
4.进入2。

我们下述的a->print()对应的输出为:
在这里插入图片描述

#include <iostream>

class Norm
{
public:
    Norm(int i) : m_i(i) {}
    Norm *operator->()
    {
        printf("norm ->\n");
        return this;
    }

    void print()
    {
        printf("i_%d\n", m_i);
    }

private:
    int m_i;
};
class A
{
public:
    Norm operator*()
    {
        Norm n(1);
        printf("*\n");
        return n;
    }

    Norm operator->()
    {
        Norm n(11);
        printf("->\n");
        return n;
    }

private:
    int m_i;
};

int main()
{
    A a;
    A *p = &a;
    p->operator->();
    p->operator*();
    a.operator*();
    a.operator->();

    a->print();
    *a;
    return 0;
}

在这里插入图片描述
4.8.3.注意点
以4.8.2中实例为说明对象。整理c++类型中成员访问细节。

int main()
{
    A a;
    A *p = &a;
    p->operator->();
    p->operator*();
    a.operator*();
    a.operator->();

    a->print();
    *a;
    return 0;
}

(1).针对指针类型,基于指针执行->可以访问到指针指向对象的成员。此行为不允许重载。
(2).针对非指针类型,称其为对象类型,基于对象类型执行.可以访问到对象的成员。此行为不允许重载。
(3).调用类型*,->运算符的形式有显式和隐式。
a.显式调用诸如:

int main()
{
    A a;
    A *p = &a;
    p->operator->();
    p->operator*();
    a.operator*();
    a.operator->();
    return 0;
}

b.隐式调用诸如:

int main()
{
    A a;
    a->print();
    *a;
    return 0;
}

4.9.函数调用运算符
4.9.1.强制性约束
对形参列表无强制性约束。

#include <iostream>

class A
{
public:
    A() {}
    int operator()(int)
    {
        printf("(int)\n");
        return 0;
    }

    int operator()(int, int)
    {
        printf("(int,int)\n");
        return 1;
    }

private:
    int m_i;
};

int main()
{
    A a;
    a(1);
    a(1, 2);
    return 0;
}

上述我们定义了两个函数调用运算符,分别接收一个参数,两个参数。

定义类函数调用运算符的类,类的对象称作函数对象。

4.10.转换构造函数
4.10.1.强制性约束
我们称只传递一个实参即可调用的构造函数为转换构造函数

#include <iostream>

class A
{
public:
    A(int i, int j = 0) : m_i(i)
    {
        printf("A(int)_%d\n", i);
    }

private:
    int m_i;
};

void fun(A a)
{
}

int main()
{
    fun(1);
    return 0;
}

上述我们定义了一个转换构造函数。因为有了此转换构造函数,上述fun(1)执行中先基于1构造得到类型A的实例,再执行函数调用。
若要阻止这类默认转换,只需将转换构造用explicit修饰。此时,只能通过显式构造基于int得到类型A实例。

#include <iostream>

class A
{
public:
    explicit A(int i, int j = 0) : m_i(i)
    {
        printf("A(int)_%d\n", i);
    }

private:
    int m_i;
};

void fun(A a)
{
}

int main()
{
    fun(A(1));
    return 0;
}

4.11.类型转换运算符
4.11.1.强制性约束
类型转换运算符的形参列表必须为空

#include <iostream>

class Norm
{
public:
private:
    char m_c;
};

class A
{
public:
    explicit A(int i, int j = 0) : m_i(i)
    {
        printf("A(int)_%d\n", i);
    }

    operator int()
    {
        printf("int()\n");
        return m_i;
    }

    operator Norm()
    {
        printf("Norm()\n");
        Norm n;
        return n;
    }

private:
    int m_i;
};

void fun(A a)
{
}

int main()
{
    fun(A(1));
    A a(1);
    int n = 1 + a;
    printf("n_%d\n", n);

    Norm nn = a;
    return 0;
}

上述我们定义了两个类型转换运算符,分别转换到int,Norm类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/218379.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32通用定时器

本文实践&#xff1a;实现通过TIM14_CH1输出PWM&#xff0c;外部显示为呼吸灯。 通用定时器简介 拥有TIM2~TIM5、TIM9~TIM14 一共10个定时器&#xff0c;具有4路独立通道&#xff0c;可用于输入捕获、输出比 较&#xff0c;同时包含了基本定时去的所有功能。 通用定时器的结…

音视频技术开发周刊 | 322

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 超级AI不会主宰人类&#xff0c;但人工智能必须开源&#xff01;LeCun最新采访引全网300万人围观 LeCun最新访谈视频中&#xff0c;再次坦露了自己对开源AI的看法。超级AI…

[MySQL--基础]多表查询

前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;生活中最大的挑战就是发现自己是谁。然后&#xff0c;坚定不移地成为那个人。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法 MySQL数据库 多表查询 前言多表关系概述&#x1f…

怎么翻译英文医学文献资料

文献翻译是一项要求严谨、精确且地道的工作&#xff0c;对于医学文献翻译更是如此。那么&#xff0c;怎么翻译英文医学文献资料&#xff0c;医学英文文献翻译公司哪个好&#xff1f; 专业人士指出&#xff0c;在翻译医学文献时&#xff0c;理解原文的语境是至关重要的。这不仅需…

Vue JAVA开发常用模板

1.VsCode添加模板 左下角设置》用户代码片段 新建全局代码片段》将模板粘贴仅文件&#xff08;prefix用于指定触发关键字&#xff09; 添加成功过后输入配置的关键字即可使用 1.1 vue2模板 {// Example:"Print to console": {"prefix": "vue2",…

创建conan包-Understanding Packaging

创建conan包-Understanding Packaging 1 Understanding Packaging1.1 Creating and Testing Packages Manually1.2 Package Creation Process 本文是基于对conan官方文档Understanding Packaging翻译而来&#xff0c; 更详细的信息可以去查阅conan官方文档。 1 Understanding …

Leetcode刷题详解——等差数列划分

1. 题目链接&#xff1a;413. 等差数列划分 2. 题目描述&#xff1a; 如果一个数列 至少有三个元素 &#xff0c;并且任意两个相邻元素之差相同&#xff0c;则称该数列为等差数列。 例如&#xff0c;[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。 给你一个整数数组 …

24、到底什么是感受野?

在之前的文章中介绍卷积算法时,一直在强调一个地方,那就是卷积算法是——卷积核在输入图像上滑动扫描的过程。 在每一次扫描时,可以把卷积核的在长宽方向的大小看做一个窗口,透过窗口可以看到的输入图像的范围,就称为感受野。 也就是神经网络(卷积)在每一次扫描过程中…

RHEL8.9 静默安装Oracle19C

RHEL8.9 静默安装Oracle19C 甘肃圆角网络科技开发有限公司 说明(GUI)&#xff1a;  1.实际业务场景中&#xff0c;Linux环境一般情况下是没有GUI的。没有GUI并不意味着没有安装图形界面。可能在部署Linux操作系统环境的时候安装了桌面环境&#xff0c;只是启动的时候设置了启动…

龙迅LT2611UX 四端口LVDS转HDMI(2.0)

1.描述&#xff1a; LT2611UX 四端口LVDS TO HDMI2.0。 LT2611UX是一款高性能得LVDS到HDMI2.0转换器得STB&#xff0c;DVD应用程序&#xff0c;LVDS输入可以配置单端口&#xff0c;双端口或者四端口&#xff0c;带有一个高速时钟通道&#xff0c;最多可运行三到四个高速数据…

FacetWP Relevanssi Integration相关性集成插件

点击阅读FacetWP Relevanssi Integration相关性集成插件原文 FacetWP Relevanssi Integration相关性集成插件是FacetWP与用于高级搜索的 Relevanssi 插件的集成显着增强了您网站的搜索功能。这个强大的工具使您的用户能够轻松找到他们寻求的特定内容&#xff0c;无论他们的查询…

【分布式算法】Raft算法详解

目录 一、什么是分布式一致性 二、Raft算法概述 三、Raft 算法的实现原理 3.1、Leader 选举 3.1.1、背景 3.1.2、有哪些成员身份 ​编辑 3.1.3、节点的状态转换 3.1.4、选举领导者的过程 3.1.5、节点间如何通讯&#xff1f; 3.1.6、什么是任期&#xff1f; 3.1.7、选…

SQL数据库知识点总结归纳

前后顺序可以任意颠倒,不影响库中的数据关系 关系数据库的逻辑性强而物理性弱,因此关系数据库中的各条记录前后顺序可以任意颠倒,不影响库中的数据关系 一名员工可以使用多台计算机(1:m),而一台计算机只能被一名员工使用(1:1),所以员工和计算机两个实体之间是一对多…

如何写出一个性能优化的单例模式

总结/朱季谦 单例模型是面试当中最常见的一种设计模式&#xff0c;它是一种对象创建模式&#xff0c;用于产生一个对象的具体实例&#xff0c;可以确保系统中一个类只产生一个实例。 简而言之&#xff0c;单例模式可以带来两个好处&#xff1a; 1、对于频繁使用到的对象&…

Linux--网络编程-ftp(TCP)网络通信-文件交互

项目要求&#xff1a;实现以下内容 远程控制&#xff1a; 1、查看服务器当前路径文件 ls 3、进入、退出服务器文件夹 cd 4、上传文件到服务器 put xxx 本地控制&#xff1a; 1、查看本地&#xff08;客户端&#xff09;文件 lls 2、进入客户端文件夹 lcd 3、获取服务器的文件…

MySQL笔记-第01章_数据库概述

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第01章_数据库概述1. 为什么要使用数据库2. 数据库与数据库管理系统2.1 数据库的相关概念2.2 数据库与数据库管理系统的关系2.3 常见的数据库…

n皇后问题的最优解及优化

n皇后问题的最优解 时间复杂度 package algorithm;public class NQueen {public static int num(int n){if(n < 1){return 0;}int[] record new int[n];//n皇后的n*n的棋盘&#xff0c;record[i]表示第i行的皇后放在了第几列return process(0,record,n);}/***返回n皇…

深入学习锁--Synchronized各种使用方法

一、什么是synchronized 在Java当中synchronized通常是用来标记一个方法或者代码块。在Java当中被synchronized标记的代码或者方法在同一个时刻只能够有一个线程执行被synchronized修饰的方法或者代码块。因此被synchronized修饰的方法或者代码块不会出现数据竞争的情况&#x…

Vue练习 v-model 指令在状态和表单输入之间创建双向绑定

效果&#xff1a; <template><h2>Text Input</h2><input v-model"text"> {{ text }}<h2>Checkbox</h2><input type"checkbox" id"checkbox" v-model"checked"><label for"checkbox…

使用VBA创建Excel条件格式

实例需求&#xff1a;数据总行数不确定&#xff0c;现需要将Category区域&#xff08;即C列到J列&#xff09;中第3行开始的区域设置条件格式&#xff0c;规则如下&#xff1a; 只对部分指定单元格应用色阶条件格式&#xff08;3色&#xff09;指定单元格应满足条件&#xff1…