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

ops/2025/3/19 16:52:28/

重载的概念

一.函数重载

代码演示例子:


#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.ppmy.cn/ops/12231.html

相关文章

常用的正则表达式组成

正则表达式是一种强大的文本处理工具&#xff0c;用于匹配、搜索、替换、分割等多种操作。以下是正则表达式的基本组成和语法&#xff0c;以及如何使用它们来构建复杂的模式。 正则表达式语法 1. 基本字符匹配 - 普通字符: 大多数字符&#xff0c;如a-z、A-Z、0-9&#xff0c…

聊聊redisson的RRateLimiter

序 本文主要研究一下redisson的RRateLimiter RRateLimiter redisson/src/main/java/org/redisson/api/RRateLimiter.java public interface RRateLimiter extends RRateLimiterAsync, RExpirable {/*** Initializes RateLimiters state and stores config to Redis server.…

Vue基于高德地图API封装一个地图组件

一、参考资料 高德开放平台 | 高德地图API (amap.com) 二、安装及配置 pnpm i vuemap/vue-amap --save man.ts 密钥及安全密钥需要自己到高德地图开放平台控制台获取. import { createApp } from vue import App from ./App.vue import router from ./router i…

智慧安防视频监控EasyCVR视频汇聚平台无法自动播放视频的原因排查与解决

国标GB28181协议EasyCVR安防视频监控平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流…

appium相关的知识

>adb shell dumpsys window | findstr mCurrentFocus adb devices # 实例化字典 desired_caps = dict() desired_caps[platformName] = Android desired_caps[platformVersion] = 9 # devices desired_caps[deviceName] = emulator-5554 # 包名 desired_caps[appPackage] …

论文笔记:How Can Large Language Models Understand Spatial-Temporal Data?

arxiv 202401 1 intro LLM在NLP和CV领域表现出色&#xff0c;但将它们应用于时空预测任务仍然面临挑战&#xff0c;主要问题包括&#xff1a; 数据不匹配 传统的LLMs设计用于处理序列文本数据&#xff0c;而时空数据具有复杂的结构和动态性&#xff0c;这两者之间存在显著差异…

面试二十、BST二叉排序树

静态查找表&#xff1a; 当有序表是静态的&#xff0c;即其内容在创建后不再发生变化&#xff0c;适合使用顺序表作为存储结构。顺序表通过数组实现&#xff0c;可以提供常数时间的随机访问&#xff0c;因此在静态情况下&#xff0c;适合顺序表存储&#xff0c;这样可以简化数据…

uniapp同步开发h5+小程序双平台踩坑记录

最近做项目的需求是先发布h5&#xff0c;后续再开发上线微信小程序版&#xff0c;自然我选择了uniapp多平台打包&#xff0c;过程中也踩了一些坑。本篇文章记录了使用uniapp开发h5的注意事项&#xff0c;及打包成小程序需要兼容改动的内容。 1.需要在pages.json注册页面路由 在…