函数重载(Function Overloading)是指在一个类中可以存在多个同名函数,但它们的参数列表(个数、类型或顺序)必须不同。在调用函数时,编译器会根据传入的参数类型或数量来确定调用哪一个函数。
我们来举个例子说明:
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
using namespace std;
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
int main() {
cout << add(1, 2) << endl;
cout << add(1, 2, 3) << endl;
cout << add(1.2, 1.4) << endl;
return 0;
}
这段代码展示了函数重载的一个简单示例,包含了三个重载的 add
函数。
int add(int a, int b)
:这个函数接受两个整数参数 a
和 b
,并返回它们的和。
int add(int a, int b, int c)
:这个函数接受三个整数参数 a
、b
和 c
,并返回它们的和。
double add(double a, double b)
:这个函数接受两个双精度浮点数参数 a
和 b
,并返回它们的和。
我们可以通过传入参数的不同系统会进行不同的函数调用,我们接着再来看两个函数:
int add(int a, int b) {
return a + b;
}
double add(int a, int b) {
return a + b;
}
这两段代码能否进行重载呢?我们运行一下:
这里我们可以发现报错了,大概意思就是说两个函数有歧义,定义重复了,好这里我们可以发现函数的重载无法通过不同的函数返回类型进行区分,可重载的本质到底是什么呢?为什么相同的函数名,他却可以准确的进行调用呢?下面我们接着聊。
C++在编译阶段,利用命名倾轧(name mangling)技术来区分参数不同的同名函数。
图中红框标注的部分,即命名倾轧的结果
函数名 | 命名倾轧 |
---|---|
int add(int a, int b); | _Z3addii |
int add(int a, int b,int c); | Z3addiii |
double add(double a, double b); | _Z3adddd |
由此来进行相同函数名的调用。函数名的重载大概知道怎么回事之后,我们再来聊聊运算符重载又是怎么一回事呢?
运算符重载(Operator Overloading)是一种特性,它允许在用户自定义类型中重新定义标准运算符的行为。通过运算符重载,可以使得用户自定义类型的对象可以像内置类型一样使用标准运算符,使得代码更加直观和易读。
在C++中,你可以重载许多不同的运算符,包括算术运算符(如 +
、-
、*
、/
等)、比较运算符(如 ==
、!=
、<
、>
等)、赋值运算符(如 =
)、成员访问运算符(.
、->
)等。
运算符的重载和函数的重载基本原理是差不多的,下面我们通过几个简单例子,来进行说明。
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
using namespace std;
class A{
public:
A(int x = 100, int y = 100) : x(x), y(y) {};
private:
int x, y;
};
int main() {
A a(10, 20), b(20, 30);
cout << a << endl;
return 0;
}
大加看看这样一段代码,我们如何使cout << a << endl;
这段代码可以正常输出呢?这时我们就要用到运行符的重载,重载<<
左移运算符。
class A{
public:
A(int x = 100, int y = 100) : x(x), y(y) {};
friend ostream &operator<<(ostream &, const A &);
private:
int x, y;
};
ostream &operator<<(ostream &out, const A &a) {
out << "(" << a.x << ", " << a.y << ")";
return out;
}
因为<<
运算符是一个二元运算符,所以传递过去两个值,分别为cout
和a
,然后我们还得返回ostream
类型的变量。
那如果我们需要运行如下代码时:
cout << (a + b) << endl;
我们应该怎么正确运行这段话呢?那必然时重载加法运算符。该如何重载呢?我们直接给代码:
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
using namespace std;
class A{
public:
A(int x = 100, int y = 100) : x(x), y(y) {};
A operator+(const A &a) {
return A(this->x + a.x, this->y + a.y);
}
friend ostream &operator<<(ostream &, const A &);
private:
int x, y;
};
ostream &operator<<(ostream &out, const A &a) {
out << "(" << a.x << ", " << a.y << ")";
return out;
}
int main() {
A a(10, 20), b(20, 30);
cout << a << endl;
cout << (a + b) << endl;
return 0;
}
这里我们简单了解几个运行符重载的案例,下面我们来聊一聊比较好玩的运行符重载。
数组对象,听名字我们就应该知道,这是一个可以当作数据用的一个对象,可是数组有什么特点呢?是不是可以通过下标来访问内存里面的值呢?好,在这里我们也来实现一下。
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
using namespace std;
class A{
public:
A(int n = 100) : n(n), data(new int[n]) {};
private:
int n;
int *data;
};
int main() {
A a(10), b(20);
cout << a[2] << endl;
return 0;
}
我们这里想让cout << a[2] << endl;
这段代码可以正常运行,是不是就应该重载[]
这个运行符了。
class A{
public:
A(int n = 100) : n(n), data(new int[n]) {};
int &operator[](int ind) { return data[ind]; }
private:
int n;
int *data;
};
重载完[]
这个运算符之后我们是不是可以进行一些数组的常用操作,如下:
int main() {
A a(10), b(20);
for (int i = 0; i < 10; i++) a[i] = i;
for (int i = 0; i < 10; i++) cout << a[i] << endl;
return 0;
}
好,我们来运行一下:
没有什么问题,到这里我们数组对象的基础功能就实现了,主要就是重载了一下[]
这个运算符,我们接着在来看看函数对象。
函数对象,听名字可以知道这是长得像函数的对象,函数有什么长什么样呢?(如:add(a, b))所以在这里我们主要是来重载()
这个运算符的,好我们接着来看一段代码:
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
using namespace std;
class A{
public:
int operator() (int a, int b) { return a + b; }
private:
};
int main() {
A a;
cout << a(4, 5) << endl;
return 0;
}
这样就可以很简单的重载一个函数对象,在这里我们在引入一个新的词汇(在c语言中也有):函数指针。这是干嘛的嘞,在c语言中的函数指针是可以指向这一类函数的指针变量,在c++中新的函数指针不仅保留了c语言中原本可以指向函数的功能,还新加了可以指向函数对象的功能,我们来用代码实现一下:
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
class A{
public:
int operator() (int a, int b) {
cout << "class : ";
return a + b;
}
private:
};
int add(int a, int b) {
cout << "add : ";
return a + b;
}
int main() {
A a;
function q;
q = add;
cout << q(3, 4) << endl;
q = a;
cout << q(5, 6) << endl;
return 0;
}
我们来看看输出结果:
函数对象主要就是重载了()
这个运算符,我们在来看一下指针对象应该怎么表示呢?
指针对象,听名字我们应该知道这是一个长的像指针的一个对象,我们来思考一下指针有哪些操作呢?我们先来简单写一个strucr
和class
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
class Pointer{
public:
private:
};
int main() {
A a, b;
return 0;
}
实现取地址功能:Pointer p = &a, q = &b;
int main() {
A a, b;
// 取地址
Pointer p = &a, q = &b;
return 0;
}
要实现这样一个功能,我们得写一个能接收地址的构造函数,如下:
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
private:
A *p;
};
这样我们就实现了取地址的功能。
实现间接引用功能:p->x = 3;
int main() {
A a, b;
// 取地址
Pointer p = &a, q = &b;
// 间接引用
p->x = 3;
p->y = 4;
return 0;
}
我们这里需要重载一些->
运算符
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
private:
A *p;
};
这样我们就实现了->
运算符的重载,因为最后我们得返回一个A
类的地址,所以返回值的类型为A *
,返回值为this->p
。
实现地址的输出:cout << p << endl;
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
// 地址的输出
friend ostream &operator<<(ostream &, const Pointer &);
private:
A *p;
};
// 地址的输出
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
int main() {
A a, b;
// 取地址功能
Pointer p = &a, q = &b;
// 间接引用功能
p->x = 3;
p->y = 4;
// 地址的输出
cout << p << endl;
return 0;
}
这里我们主要重载一下<<
左移运算符:
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
因为<<
运算符是一个二元运算符,所以传递过去两个值,分别为cout
和p
,然后我们还得返回ostream
类型的变量。
实现值的输出:cout << *p << endl;
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
// 值的输出
ostream &operator<<(ostream &out, const A &a){
out << "("<< a.x << ", " << a.y << ")";
return out;
}
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
// 地址的输出
friend ostream &operator<<(ostream &, const Pointer &);
// 值的输出
A &operator*() { return *p; }
private:
A *p;
};
// 地址的输出
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
int main() {
A a, b;
// 取地址功能
Pointer p = &a, q = &b;
// 间接引用功能
p->x = 3;
p->y = 4;
// 地址的输出
cout << p << endl;
// 值的输出
cout << *p << endl;
return 0;
}
在这里我们得先重载一下*
运算符,然后这时*p
的值为一个A
类型的值,我们还得重载一下A
类型的<<
左移运算符:
ostream &operator<<(ostream &out, const A &a){
out << "("<< a.x << ", " << a.y << ")";
return out;
}
A &operator*() { return *p; }
我们来分析一下重载*
运算符时,应该返回上面类型的值,这里需要返回上面类型的值,主要看我们需要什么,或者说如果是个指针的话,应该满足什么样的功能,*p
的得有赋值的功能(如:*p = A(100, 200);),所以我们得返回的是A &
类型的值,而不能是A
类型,如果是A
类型的话,返回回来的就不是真实的地址,而只是一个拷贝在值,进行赋值之后,源地址不会进行更改,与我们所需要的功能不符。
地址的增加与减少:cout << p + 1 << endl;
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
// 值的输出
ostream &operator<<(ostream &out, const A &a){
out << "("<< a.x << ", " << a.y << ")";
return out;
}
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
// 地址的输出
friend ostream &operator<<(ostream &, const Pointer &);
// 值的输出
A &operator*() { return *p; }
// 地址的增加与减少
A *operator+(int ind) { return p + ind; }
A *operator-(int ind) { return p - ind; }
int operator-(const Pointer &q) { return this->p - q.p; }
private:
A *p;
};
// 地址的输出
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
int main() {
A a, b;
// 取地址功能
Pointer p = &a, q = &b;
// 间接引用功能
p->x = 3;
p->y = 4;
// 地址的输出
cout << p << endl;
// 值的输出
cout << *p << endl;
// 地址的增加与减少
cout << p + 1 << endl;
cout << p - 1 << endl;
cout << p - q << endl;
return 0;
}
这里我们实现了加法的重载以及两次减法的重载,在+ 1
、-1
时,我们说需要的是一个地址,所以返回类型为A *
,而在p - 1
中我们需要的是偏移量,是一个int
类型,所以返回值为int。
模仿数组:cout << p[0] << endl;
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
// 值的输出
ostream &operator<<(ostream &out, const A &a){
out << "("<< a.x << ", " << a.y << ")";
return out;
}
class Pointer{
public:
// 取地址功能
Pointer(A *p) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
// 地址的输出
friend ostream &operator<<(ostream &, const Pointer &);
// 值的输出
A &operator*() { return *p; }
// 地址的增加与减少
A *operator+(int ind) { return p + ind; }
A *operator-(int ind) { return p - ind; }
int operator-(const Pointer &q) { return this->p - q.p; }
// 模仿数组
A &operator[](const int ind) { return *(p + ind); }
private:
A *p;
};
// 地址的输出
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
int main() {
A a, b;
// 取地址功能
Pointer p = &a, q = &b;
// 间接引用功能
p->x = 3;
p->y = 4;
// 地址的输出
cout << p << endl;
// 值的输出
cout << *p << endl;
// 地址的增加与减少
cout << p + 1 << endl;
cout << p - 1 << endl;
cout << p - q << endl;
// 模仿数组
cout << p[0] << endl;
return 0;
}
模仿数组和*p
的返回值是一个道理,因为我们得为p[0]
赋值,所以我们的返回值为A &
。
const Pointer cp = &a;
,const
定义一个指针对象,然后在写上面哪些操作,又应该怎么写呢?我们直接先写代码:
/*************************************************************************
> File Name: test1.cpp
> Author:Xiao Yuheng
> Mail:[email protected]
> Created Time: Sun Oct 29 09:48:44 2023
************************************************************************/
#include
#include
using namespace std;
struct A{
A(int x = 0, int y = 0) : x(x), y(y) {}
int x, y;
};
// 值的输出
ostream &operator<<(ostream &out, const A &a){
out << "("<< a.x << ", " << a.y << ")";
return out;
}
class Pointer{
public:
// 取地址功能
Pointer(A *p = nullptr) : p(p) {};
// 间接引用功能
A *operator->() { return p; }
A *operator->() const { return p; }
// 地址的输出
friend ostream &operator<<(ostream &, const Pointer &);
// 值的输出
A &operator*() { return *p; }
const A &operator*() const { return *p; }
// 地址的增加与减少
A *operator+(int ind) { return p + ind; }
A *operator+(int ind) const { return p + ind; }
A *operator-(int ind) { return p - ind; }
A *operator-(int ind) const { return p - ind; }
int operator-(const Pointer &q) { return this->p - q.p; }
// 模仿数组
A &operator[](const int ind) { return *(p + ind); }
const A &operator[](int ind) const { return *(p + ind); }
private:
A *p;
};
// 地址的输出
ostream &operator<<(ostream &out, const Pointer &p) {
out << p.p;
return out;
}
int main() {
A a, b;
// 取地址功能
Pointer p = &a, q = &b;
// 间接引用功能
p->x = 3;
p->y = 4;
// 地址的输出
cout << p << endl;
// 值的输出
cout << *p << endl;
// 地址的增加与减少
cout << p + 1 << endl;
cout << p - 1 << endl;
cout << p - q << endl;
// 模仿数组
cout << p[0] << endl;
// const 操作
const Pointer cp = &a;
cout << "a value = " << *cp << endl;
cout << "&a = " << cp << endl;
cout << "&a + 1 = " << (cp + 1) << endl;
cout << "&a - 1 = " << (cp - 1) << endl;
cout << "a value = " << cp[0] << endl;
return 0;
}
可以发现我们在重载const
运算符的时候,时而返回值加const
,时而不加const
,这是什么原因呢?这是和我们的需求有关系,因为const Pointer
表示的功能应该是无法通过cp
指针来进行更改a
的值,所以只要涉及到返回&
这个的都得加上const
,那有人就会想了,我们可以都加上const
吗?这得看需求来进行不同的操作,如果你不需要更改他返回回来的值的话,就可以全部加const
,否则就不加。