C++笔记:C++中的重载

重载的概念

一.函数重载

代码演示例子:


#include<iostream>
using namespace std;//函数名相同,在是每个函数的参数不相同
void output(int x) {printf("output int : %d\n", x);return ;
}void output(long long x) {printf("output long long : %llX\n", x);return ;
}void output(const char *s) {if (!s) {printf("output string : nullptr\n");return ;}printf("output string : %s\n", s);return ;
}void output(int x, int y) {printf("output double int : %d, %d\n", x, y);return ;
}
//如果调用这个函数时,没有传入参数3,也就是c
//那么c的默认值就为123,并且程序不会编译不通过
void output(int a, char b, int c = 123) {printf("output a = %d, b = %c, c = %d\n", a, b, c);return ;
}//如果这里没有第三个参数,那么就会造成编译报错
//因为没有办法通过返回值类型进行区分重载类型
//也就是没有办法区分和上面的double int进行区分
const char *output(int a, int b, char c) {printf("output a = %d, b = %d, c = %c\n", a, b, c);return "yes";
}int main() {output(3);//这里匹配的是int类型的output//在数字后面加上LL 表示这个数字是longlong类型output(3LL);//这里匹配的是longlong类型的outputoutput("hello world");//匹配参数为char *的outputoutput(3, 4);//匹配参数为两个int类型的outputoutput(3LL, 4);//近似匹配了两个int类型的output//下面这个output(NULL),他会匹配到int类型//NULL可以表示成0值//然后它也可以表示成一个地址,然后它又是0值的地址,地址有64位也就是8字节,那他也会匹配long long类型//然后还表示一个空地址,还会匹配到char *类型//output(NULL);//而在C++中 nullptr只表示空地址,这样他就会精确匹配对应的函数output(nullptr);output(12, 'p');cout << output(3, 4, 'c') << endl;return 0;
}

然后如果output(NULL)那行代码没有注释掉,那么就会出现,注释说明的情况,无法匹配造成歧义:

对于成员函数重载和普通函数的重载一样,我就不用代码进行演示了。

二.运算符重载

不能重载的运算符:

类外:

参考代码:

#include <iostream>
#include <cstdio>
using namespace std;class Point {
public :Point() : x(0), y(0), output_width(0) {}Point(int x, int y) : x(x), y(y), output_width(0) {}
private :int x, y;int output_width;//利用友元进行解决私有成员属性的访问问题friend ostream &operator<<(ostream &out, const Point &p);friend int operator*(const Point &, const Point &);friend Point operator+(const Point &, int );friend double operator*(const Point &, double );friend double operator*(double , const Point &);friend Point &operator-(Point &, int x) ;friend Point &operator-(Point &, Point &) ;friend Point &operator-(Point &, const char *) ;
};
//ostream类型就像cout的对象的类型
//加上const为了同时支持const和非const限定的对象
ostream &operator<<(ostream &out, const Point &p) {out << "("  << p.x << ", " << p.y << ")";return out; 
}int operator*(const Point &p1, const Point &p2) {return p1.x * p2.x + p1.y * p2.y;
}Point operator+(const Point &p1, int x) {Point p(p1.x + x, p1.y + x);return p;
}double operator*(const Point &p1, double x) {return p1.x * x + p1.y * x;
}double operator*(double x, const Point &p1) {//这里他调用的就是上面运算符重载的方法return p1 * x;
}Point &operator-(Point &p, int x) {p.output_width = x; return p;
}Point &operator-(Point &p1, Point &p2) {char str[100] = {0};snprintf(str, 99, "(%%%dd, %%%dd)", p1.output_width, p1.output_width);printf(str, p2.x, p2.y);return p1;
}Point &operator-(Point &p, const char *s) {printf("%s", s);return p;
}Point &operator^(Point &p1, const Point &p2) {return p1;
}Point &operator^(Point &p1, int x) {return p1;
}int main() {Point p1(5, 6), p2(3, 6), p3(6, 9), p4(10, 12);//cout他也不认识我们创建的Point类//如果要对cout进行对p1进行输出//那么我们就要对<<这个左移运算符进行重载cout << p1 << endl;//这里对运算符的重载可以不用实现和我一样的,可以通过自己的想象然后来实现//然后实现的结果和自己的想象的需要是一样的结果cout << p1 * p2 << endl;cout << p1 * 2.3 << endl;cout << 2.3 * p1 << endl;cout << p1 + 5 << endl;//实现效果//p1 - 6设置输出的位宽为6//- p2 输出p2的值,并且输入的位宽为p1的位宽//- "\n" 换行p1 - 6 - p2 - "\n";return 0;
}

练习:

        实现下面的代码:

    Point p1(5, 6), p2(3, 6), p3(6, 9), p4(-2, -4);cout << p1 << p2 << p3 << p4 << endl;//^运算符将每个类中的成员属性x,y输出到一个坐标轴中//^1时打印出这个坐标轴p1^p2^p3^p4^1;

参考代码:

#include <iostream>
#include <cstdio>
using namespace std;#define MAX_N 20class Point {
public :Point() : x(0), y(0) {}Point(int x, int y) : x(x), y(y) {}~Point() {}static void init_x_y_axis() {for (int i = 0; i < MAX_N; i++) {Point::x_y_axis[i] = new int[MAX_N + 5];}return ;}static void set_x_y_axis(int x, int y) {if (x_y_axis[x + MAX_N / 2][y + MAX_N / 2]) return ;x_y_axis[x + MAX_N / 2][y + MAX_N / 2] = 1;return ;}void output() {cout << sizeof(Point::x_y_axis[0]) << endl;cout << sizeof(Point::x_y_axis) << endl;}friend Point &operator^(Point &, Point &);friend Point &operator^(Point &, int);friend ostream &operator<<(ostream &, const Point &);
private :int x, y;//创建一个类属性//用来当作坐标轴static int **x_y_axis;
};int **Point::x_y_axis = new int*[MAX_N + 5];Point &operator^(Point &p1, Point &p2) {//将每个对象的x,y输出到坐标轴中Point::set_x_y_axis(p1.x, p1.y);Point::set_x_y_axis(p2.x, p2.y);return p1;
}Point &operator^(Point &p1, int num) {//我们要求的是^1才打印if (num != 1) return p1;for (int y = MAX_N / 2; y > -MAX_N / 2; y--) {for (int x = -MAX_N / 2; x < MAX_N / 2; x++) {!x && y && printf("%3d", y);if (!y) {printf("%3d", x);} else {if (Point::x_y_axis[x + (MAX_N / 2)][y + (MAX_N / 2)]) {printf("%3c", '*');}else if (x) printf("%3c", ' ');}}putchar(10);}return p1;
}ostream &operator<<(ostream &out, const Point &p1) {out << "p("  << p1.x << ", " << p1.y << ")" << endl;return out;
}int main() {Point::init_x_y_axis();Point p1(5, 6), p2(3, 6), p3(6, 9), p4(-2, -4);cout << p1 << p2 << p3 << p4 << endl;//^运算符将每个类中的成员属性x,y输出到一个坐标轴中//^1时打印出这个坐标轴p1^p2^p3^p4^1;return 0;
}

实现效果:

        实现结果可以和我不一样,但是一定要实现你自己的想法,C++的语法就是非常的灵活并且也非常容易出错,所以这才是C++的难处.

        注意:学习C++是学习C++的设计模式。

类内:

    对于下面的运算符,只能再类内中重载,但是不是意思是只能重载这些运算符,对于类外可以重载的运算符,类内一样也可以重载

在说类内的运算符之前说一个知识点左值右值: 

左值右值

带入代码理解:

#include<iostream>
using namespace std;#define LEFT_OR_RIGHT(expr) {\printf("expr : %s\n", #expr);\left_or_right(expr);\printf("\n");\
}void left_or_right(int &x) {printf("left value : %d\n", x);return ;
}void left_or_right(int &&x) {printf("right value : %d\n", x);return ;
}int main() {int a = 123;//a可以通过单一变量a访问//那么他就是左值LEFT_OR_RIGHT(a);//a + 1是中间产生临时的一个值//那么他无法通过单一变量进行访问到//那他就是一个右值//因为在过了下面这行代码后,我们没有办法进行通过单一变量进行访问到它LEFT_OR_RIGHT(a + 1);//任何字面量的值都是右值LEFT_OR_RIGHT(123);//这里a++你带入进去的是a的值//然后带入后,a进行了a += 1//那么在这行代码之后你无法通过单一变量去访问到之前的a值//那么它就是右值LEFT_OR_RIGHT(a++);//++a带入的是 a += 1的值//在这行代码之后,它可以通过单一变量a去访问到这个值//那他就是左值LEFT_OR_RIGHT(++a);//a += 2同理它可以通过变量a去访问到这个值LEFT_OR_RIGHT(a += 2);return 0;
}

执行结果,和我代码注释推断的结果是一样的:

然后下一个版本:

#include<iostream>
using namespace std;#define LEFT_OR_RIGHT(expr) {\printf("expr : %s\n", #expr);\left_or_right(expr);\printf("\n");\
}void left_or_right(int &&, int);void left_or_right(int &x, int flag = 1) {printf("left value : %d\n", x);if (flag) left_or_right(x, 0);return ;
}void left_or_right(int &&x, int flag = 1) {printf("right value : %d\n", x);if (flag) left_or_right(x, 0);return ;
}namespace test1 {
int main() {int a = 123;//a可以通过单一变量a访问//那么他就是左值LEFT_OR_RIGHT(a);//a + 1是中间产生临时的一个值//那么他无法通过单一变量进行访问到//那他就是一个右值//因为在过了下面这行代码后,我们没有办法进行通过单一变量进行访问到它LEFT_OR_RIGHT(a + 1);//任何字面量的值都是右值LEFT_OR_RIGHT(123);//这里a++你带入进去的是a的值//然后带入后,a进行了a += 1//那么在这行代码之后你无法通过单一变量去访问到之前的a值//那么它就是右值LEFT_OR_RIGHT(a++);//++a带入的是 a += 1的值//在这行代码之后,它可以通过单一变量a去访问到这个值//那他就是左值LEFT_OR_RIGHT(++a);//a += 2同理它可以通过变量a去访问到这个值LEFT_OR_RIGHT(a += 2);return 0;
}
}int main() {//test1::main();left_or_right(123);return 0;
}

执行结果:

为什么呢,123先调用右值引用没有问题吧,然后现在flag为1,需要再次调用left_or_right()函数,然后呢现在他的参数为x而不是123,那么在执行完成调用函数这句代码之后,我还是可以通过x进行访问到这个值,在右值引用这个函数的作用域里面这个x是持久态,我调用完成这个函数我是可以进行访问到的,所以第二次调用就会调用左值引用。

那么问题来了,我想保持当前的右值引用如何操作呢:

现在去理解move函数就是把move函数里的参数强制转换为右值,如果你想了解的更深可以自己看看move函数的原型,以及具体如何操作的。  

if (flag) left_or_right(move(x), 0);

改完这行代码后执行结果:

还有一种方式:

forward<>()函数,forward 函数通常用于完美转发,用于将参数原封不动地传递给另一个函数,保持参数的值类别不变。

if (flag) left_or_right(forward<int &&>(x), 0);

forward在这句代码的作用就是将x作为右值引用作为传入参数,但是x他还是左值引用。

但是move是将x变为了右值引用。

移动构造 

那么说完左值右值,那么对于构造函数还有一种形式,叫做移动构造:

代码演示:

#include <iostream>
#include <cassert>
#include <ctime>
using namespace std;class Array {
public :Array(int n) : n(n), arr(new int[n]) {cout << "array default constructor " << arr << endl;}Array(const Array &a) : n(a.n), arr(new int[n]) {cout << "copy array constructor " << arr << endl;for (int i = 0; i < n; i++) arr[i] = a[i];return ;}//再函数调用时,如果没有返回值优化//那么调用func()函数时,会先默认构造a,//默认构造返回值的隐匿对象,这里创建了新的空间//然后a拷贝给匿名对象//然后又将匿名拷贝给b对象,有创建了新空间//而移动构造,直接将第一次a创建的空间直接给匿名对象//匿名对象又通过移动构造将创建的空间给b对象,省去了中间创建新空间的步骤和释放空间的步骤Array(Array &&a) : n(a.n), arr(a.arr) {cout << "move constructor" << arr << endl;a.arr = nullptr;a.n = 0;return ;}//这里为什么要返回int &//因为你在访问该位置时,又可以能会将该位置进行赋值//所以需要返回int &int &operator[](int ind) const{assert(ind >= 0 && ind < n);return arr[ind];}void output(const char *frm) {for (int i = 0; i < n; i++) {printf(frm, i, arr[i]);}}~Array() {if (arr) delete[] arr;cout << "array disconstructor " << arr << endl;return ;}
private :int n;int *arr;
};Array func() {int n = 7;Array a(n);for (int i = 0; i < n; i++) {a[i] = i;}return a;
}int main() {srand(time(0));int n = 10;printf("a = ");Array a(n);for (int i = 0; i < n; i++) {a[i] = rand() % 100;}a.output("a[%d] = %d\n");Array b = func();b[0] = 999;b.output("b[%d] = %d\n");//如果对于a对象不用了,想将a对象所有的东西给c对象//那么就可以调用移动构造,使用move函数将a对象传入时变为右值Array c(move(a));c.output("c[%d] = %d\n");return 0;
}

 类内的运算符重载代码演示:
 前后++代码演示:
#include<iostream>
using namespace std;class Point {
public :Point(int x, int y) : x(x), y(y) {}Point(const Point &a) : x(a.x), y(a. y) {}//+重载, p1 + p2 就是 p1.x + p2.x, p1.y + p2.y//因为返回的是一个新的值,所以是右值,返回值类型就不是引用,也就是不是左值Point operator+(const Point &p) {//他这里是成员方法,比如它可以访问this指针Point ret(x + p.x, y + p.y);return ret;}//由于是前++,就不需要参数//前++返回的是左值,所以返回值类型也是左值//返回的对象也是本身Point &operator++() {cout << "++class" << endl;this->x += 1, this->y += 1;return *this;}//后++是一个右值,在演示左值和右值代码中有示例//那么返回的是一个新的值,所以返回值类型也是右值,而不是左值引用//参数列表中有参数,就是后++Point operator++(int) {cout << "class++" << endl;Point ret(*this);//代码设计逻辑和技巧++(*this);return ret;}friend ostream &operator<<(ostream &out, const Point &p);
private :int x, y;
};ostream &operator<<(ostream &out, const Point &p) {out << "(" << p.x << ", " << p.y << ")";return out;
}int main() {Point a(1, 2), b(3, 4);cout << "a : " << a << endl;cout << "b : " << b << endl;cout << a + b << endl;//这样去调用运算符重载的函数和上行代码执行效果一样cout << a.operator+(b) << endl;cout << "++a : " << ++a << endl;cout << "b++ : "<< b++ << endl;cout << "b : "<< b << endl;return 0;
}

=赋值运算符重载代码演示:


#include<iostream>
using namespace std;class A {
public :A() {cout << "default constructor " << this << endl;}A(const A &a) {cout << "copy constructor " << this << endl;}A(const A &&a) {cout << "move constructor " << this << endl;}//对于=重载A &operator=(const A &a) {//new关键字的原地构造//在this指针这块位置调用拷贝构造//this指针指向的就是下面的对象cnew(this) A(a);cout << "operator= " << this << endl;return *this;}A &operator=(A &&a) {new(this) A(move(a));cout << "operator= " << this << endl;return *this;}
};int main() {A a, c, d;A b = a;c = a;d = move(a);cout << "a = " << &a << endl;cout << "b = " << &b << endl;cout << "c = " << &c << endl;cout << "d = " << &d << endl;return 0;
}
[]、->、()运算符重载代码演示:

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;class Array_Object {
public :int operator[](int ind) {return 2 * ind;}
};class Function_Object {
public :int operator()(int x) {return 2 * x; }
};class Point {
public :Point(int x, int y) : x(x), y(y){printf("x = %d, y = %d\n", x, y);}int x, y;
};class Point_Object {
public :Point_Object() : p(new Point(rand() % 100, rand() % 100)) {}Point *operator->() {return p; }~Point_Object() {delete p;}
private :Point *p;
};int main() {srand(time(0));Array_Object arr;Function_Object fun;Point_Object p;cout << p->x << " " << p->y << endl;for (int i = 0; i < 10; i++) {cout << "arr[" << i << "]" << arr[i] << endl;}for (int i = 0; i < 10; i++) {cout << "fun(" << i << ")" << fun(i) << endl;}return 0;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手撕netty源码(一)- NioEventLoopGroup

文章目录 前言一、NIO 与 netty二、NioEventLoopGroup 对象的创建过程2.1 创建流程图2.2 EventExecutorChooser 的创建 前言 processOn文档跳转 本文是手撕netty源码系列的开篇文章&#xff0c;会先介绍一下netty对NIO关键代码的封装位置&#xff0c;主要介绍 NioEventLoopGro…

浅谈叉车车载电脑的市场现状

叉车的起源 叉车源于美国&#xff0c;兴于日本&#xff0c;虽然中国起步较晚&#xff0c;但是近些年来发展迅速。叉车又称叉式装载车&#xff0c;是对于成件托盘类货物进行装卸、堆垛和短距离运输&#xff0c;实现重物搬运作业的轮式工业车辆。 叉车的分类 叉车分为以上六大类…

OpenWrt上的docker容器无法访问外网解决

容器里能ping通OpenWrt的管理地址和wan口地址&#xff0c;但ping外网别的ip或域名就无法访问 简单修改设置就可以&#xff1a; Luci>网络>防火墙>转发&#xff1a;接受 ->保存应用

【Web】DASCTF X GFCTF 2024|四月开启第一局 题解(全)

目录 EasySignin cool_index SuiteCRM web1234 法一、条件竞争(没成功) 法二、session反序列化 EasySignin 先随便注册个账号登录&#xff0c;然后拿bp抓包改密码(username改成admin) 然后admin / 1234567登录 康好康的图片功能可以打SSRF&#xff0c;不能直接读本地文…

Hive安装部署

Apache Hive是一个基于Hadoop分布式文件系统、使用MapReduce算法执行大规模离线数据分析的数据仓库&#xff0c;本文主要描述Hive的安装部署。 如上所示&#xff0c;Hive总体应用架构图&#xff0c;其中&#xff0c;Hive基于HBase或者使用Hadoop分布式文件系统执行MapReduce的分…

Zephyr sensor子系统学习

一、背景 2023年7月份nRF Connect SDK 2.4.0最新版本&#xff0c;使用的Zephyr V3.3版本。从Zephyr 3.5版本在子系统中加入了sensing子系统。 现在最新的nRF Connect SDK 2.6.0 release支持v3.5.99-ncs1&#xff0c;已经支持sensing子系统 nRF52840现在官方支持两个传感器de…

yudao-cloud微服务系统系统模块+后台管理系统成功运行

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 系列文章目录 第一章 芋…

python基础知识—while和for循环(三)

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 一&#xff1a;while循环1.1程序的三种执行流程1.2while循环1.3循环变量和死循环 二&#xff1a;for循环2.1for循环2.2range 一&…

OSI七层模型、TCP/IP五层模型理解(个人解读,如何理解网络模型)

OSI七层模型 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般称为OSI参考模型或七层模型。它是一个七层的、抽象的模…

UVa12313 A Tiny Raytracer

UVa12313 A Tiny Raytracer 题目链接题意分析AC 代码 题目链接 UVA - 12313 A Tiny Raytracer 题意 给出 《训练指南》题意翻译 本题的任务是实现一个小型光线追踪渲染器。场景由若干三角形网格&#xff08;triangle mesh&#xff09;组成&#xff0c;有且仅有一个点光源&…

ESP32开发

目录 1、简介 1.1 种类 1.2 特点 1.3 管脚功能 1.4 接线方式 1.5 工作模式 2、基础AT指令介绍 2.1 AT指令类型 2.2 基础指令及其描述 2.3 使用AT指令需要注意的事 3、AT指令分类和提示信息 3.1 选择是否保存到Flash的区别 3.2 提示信息 3.3 其他会保存到Flash的A…

界面组件DevExpress Blazor UI v23.2 - 支持.NET 8、全新的项目模版

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 DevExpress Blazor控件目前已经升级…

RISC-V CVA6 在 Linux 下相关环境下载与安装

RISC-V CVA6 在 Linux 下相关环境下载与安装 所需环境与源码下载 CVA6 源码下载 首先&#xff0c;我们可以直接从 GitHub 一次性拉取所有源码&#xff1a; git clone --recursive https://github.com/openhwgroup/cva6.git如果这里遇到网络问题&#xff0c;拉取失败&#x…

阿里云企业邮箱API的使用方法?调用限制?

阿里云企业邮箱API性能如何优化&#xff1f;配置邮箱API的优势&#xff1f; 阿里云企业邮箱以其稳定、高效和安全的特点&#xff0c;受到了众多企业的青睐。而阿里云企业邮箱API的开放&#xff0c;更是为企业提供了更加灵活、便捷的管理和操作方式。下面&#xff0c;我AokSend…

Linux的学习之路:22、线程(2)

摘要 本章继续讲一下线程的东西 目录 摘要 一、抢票 二、加锁保护 三、死锁 1、死锁四个必要条件 2、避免死锁 四、同步 1、常见的线程安全的情况 2、常见不可重入的情况 3、常见可重入的情况 4、可重入与线程安全联系 5、可重入与线程安全区别 一、抢票 这里回…

启动 UE4编辑器报 加载 Plugin 失败

启动 UE4编辑器报 加载 Plugin 失败&#xff0c;报如下错误&#xff1a; Plugin ‘SteamVR’ failer to load because module ‘SteamVR’ could not be found. Please ensure the plugin is properly installed, otherwise consider disabling the plugin for this project. …

新时代凌迟:考研

我不喜欢上班&#xff0c;但我很欣赏老板的品味&#xff0c;因为咱们公司竟然还在订阅报纸&#xff0c;而且只有一份&#xff0c;《中国青年报》。 这份报纸我最喜欢看的是“冰点周刊”专栏&#xff0c;因为这个栏目能让读者相信&#xff1a;报纸远远可以超越一天的生命。 昨天…

前端框架编译器之模板编译

编译原理概述 编译原理&#xff1a;是计算机科学的一个分支&#xff0c;研究如何将 高级程序语言 转换为 计算机可执行的目标代码 的技术和理论。 高级程序语言&#xff1a;Python、Java、JavaScript、TypeScript、C、C、Go 等。计算机可执行的目标代码&#xff1a;机器码、汇…

JavaEE初阶——多线程(六)——线程池

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程的第六篇文章,关于线程池 如果有不足的或者错误的请您指出! 目录 3.线程池3.1标准库的线程池3.2 标准库自己提供的几个工厂类3.3自己实现一个线程池完成大体框架接下来完…

OpenHarmony实战开发-使用SmartPerf-Host分析应用性能

简介 SmartPerf-Host是一款深入挖掘数据、细粒度展示数据的性能功耗调优工具&#xff0c;可采集CPU调度、频点、进程线程时间片、堆内存、帧率等数据&#xff0c;采集的数据通过泳道图清晰地呈现给开发者&#xff0c;同时通过GUI以可视化的方式进行分析。该工具当前为开发者提…