本文最后更新于:1 年前
经典例子:
我们以两个类,二维向量vector2和二维新式向量newvector2来说明各种运算符的重载。
vector全部使用友元函数重载,newvector全部使用成员函数重载,两个都可以时同时重载。
其成员为x和y,主要操作为x(如自增自减等等),次要操作为y。
(一)重载++和--:
和--的重载分为前置和后置两种情况,另外,虽然修改了类对象的状态,但是可以使用友元函数传入引用来实现和--的类外重载。
前置++或--的成员函数不需要传入参数,在代码块内不需要拷贝临时变量返回,只通过this指针进行自增/自减。
后置或--需要传入一个int类型的参数,可以不指定名称,因为一般在代码块内部不会使用它,这个参数只是为了占位置,用于和前置/--运算符的区别。在代码块内需要先拷贝一个临时变量返回。只通过this指针自增/自减。
前置++或--的友元函数需要传入一个类对象的引用,不需要拷贝临时变量返回,直接对传入参数进行操作(因为引用可以对其有效修改)。
后置++或--的友元函数需要传入两个参数,一个是类对象的引用,且为左操作数,一个是占位的int类型参数(和上面逻辑相同),需要先拷贝临时变量返回,直接对传入参数进行操作。
详细代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<iostream> using namespace std; class vector2 { friend vector2 operator ++(vector2& v); friend vector2 operator ++(vector2& v, int); friend vector2 operator --(vector2& v); friend vector2 operator --(vector2& v, int); private: double x; double y; public: vector2(double vx = 0, double vy = 0); vector2(const vector2 &v); void print(); ~vector2(); };
class newvector2 { private: double x; double y; public: newvector2(double vx = 0, double vy = 0); newvector2(const newvector2& v); newvector2 operator++(); newvector2 operator++(int vx); newvector2 operator--(); newvector2 operator--(int vy); void print(); ~newvector2(); };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| #include"vector2.h" #include<iostream> using namespace std; vector2::vector2(double vx,double vy):x(vx),y(vy) { cout << "创建了一个二维向量" << endl; }
vector2::vector2(const vector2& v) { this->x = v.x; this->y = v.y; cout << "调用拷贝构造函数创建了一个二维向量" << endl; }
vector2::~vector2() { cout << "析构了一个二维向量" << endl; }
vector2 operator ++(vector2& v) { ++v.x; return v; }
void vector2::print() { cout << "x:" << x << ",y:" << y << endl; return; }
vector2 operator ++(vector2& v, int) { vector2 temp(v); v.x++; return temp; }
vector2 operator --(vector2& v) { --v.x; return v; }
vector2 operator --(vector2& v, int) { vector2 temp(v); v.x--; return temp; }
newvector2::newvector2(double vx, double vy) :x(vx), y(vy) { cout << "创建了一个新式二维向量" << endl; }
newvector2::newvector2(const newvector2& v) { this->x = v.x; this->y = v.y; cout << "调用拷贝构造函数创建了一个二维向量" << endl; }
newvector2::~newvector2() { cout << "析构了一个新式二维向量" << endl; }
void newvector2::print() { cout << "x:" << x << ",y:" << y << endl; return; }
newvector2 newvector2::operator++() { ++this->x; return *this; }
newvector2 newvector2::operator++(int) { newvector2 temp(*this); this->x++; return temp; }
newvector2 newvector2::operator--() { --this->x; return *this; }
newvector2 newvector2::operator--(int) { newvector2 temp(*this); this->x--; return temp; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| //main.cpp #include"vector2.h" int main() { vector2 v(2, 4); vector2 vv; cout << "开始的v: "; v.print(); vv=v++; cout << "v++后: " << endl; cout << "v:"; v.print(); cout << "vv: "; vv.print(); vv=++v; cout << "++v后: " << endl; cout << "v: "; v.print(); cout << "vv: "; vv.print(); vv=v--; cout << "v--后: " << endl; cout << "v: "; v.print(); cout << "vv: "; vv.print(); vv=--v; cout << "--v后: " << endl; cout << "v: "; v.print(); cout << "vv: "; vv.print();
newvector2 newv(2, 4); newvector2 newvv; cout << "开始的newv: "; newv.print(); newvv = newv++; cout << "newv++后: " << endl; cout << "newv:"; newv.print(); cout << "newvv: "; newvv.print(); newvv = ++newv; cout << "++nwev后: " << endl; cout << "newv: "; newv.print(); cout << "newvv: "; newvv.print(); newvv = newv--; cout << "newv--后: " << endl; cout << "newv: "; newv.print(); cout << "newvv: "; newvv.print(); newvv = --newv; cout << "--newv后: " << endl; cout << "newv: "; newv.print(); cout << "newvv: "; newvv.print(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| /*运行结果: 创建了一个二维向量 创建了一个二维向量 开始的v: x:2,y:4 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 v++后: v:x:3,y:4 vv: x:2,y:4 调用拷贝构造函数创建了一个二维向量 析构了一个二维向量 ++v后: v: x:4,y:4 vv: x:4,y:4 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 v--后: v: x:3,y:4 vv: x:4,y:4 调用拷贝构造函数创建了一个二维向量 析构了一个二维向量 --v后: v: x:2,y:4 vv: x:2,y:4 创建了一个新式二维向量 创建了一个新式二维向量 开始的newv: x:2,y:4 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 析构了一个新式二维向量 析构了一个新式二维向量 newv++后: newv:x:3,y:4 newvv: x:2,y:4 调用拷贝构造函数创建了一个二维向量 析构了一个新式二维向量 ++nwev后: newv: x:4,y:4 newvv: x:4,y:4 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 析构了一个新式二维向量 析构了一个新式二维向量 newv--后: newv: x:3,y:4 newvv: x:4,y:4 调用拷贝构造函数创建了一个二维向量 析构了一个新式二维向量 --newv后: newv: x:2,y:4 newvv: x:2,y:4 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个二维向量 析构了一个二维向量 */
|
(二)重载=:
赋值运算符的重载用于对象数据的复制,且必须重载为成员函数,返回类型为对象的引用(为了能够连续操作,否则会有临时变量作为左值出现)。
赋值运算符是用已存在的对象给另一个对象赋新的值,该对象原来是有值的,所以重载的赋值运算符只能被已经存在了的对象调用,而不能凭空产生。
-
和拷贝构造函数的区别:
-
拷贝构造函数生成新的类对象,而重载的赋值运算符是给已有的类对象进行赋值。
——因此,如果是一个类对象初始化使用"="运算符,右侧操作数即使也是这个类的对象,调用的仍然是拷贝构造函数而不是重载的赋值运算符,毕竟其还未被初始化完毕。
-
拷贝构造函数不必检测源对象是否与新建对象相同,而赋值运算符则需要。
-
如果赋值运算符中被赋值的对象有内存分配,需要先把内存释放掉再进行赋值。(要不原来的分配的内存就一直不会被释放了,另外此处的赋值他也是在堆上进行的)
1 2 3 4 5 6 7
|
char* p;
newvector2& operator=(const newvector2& v);
newvector2(double vx = 0, double vy = 0,const char*vp="\0");
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
newvector2::newvector2(double vx, double vy,const char*vp) :x(vx), y(vy) { p = new char[strlen(vp) + 1]; strcpy_s(p, strlen(vp)+1, (char*)vp); cout << "创建了一个新式二维向量" << endl; }
newvector2::newvector2(const newvector2& v) { this->x = v.x; this->y = v.y; p = new char[strlen(v.p) + 1]; strcpy_s(p, strlen(v.p) + 1, v.p); cout << "调用拷贝构造函数创建了一个新式二维向量" << endl; }
newvector2::~newvector2() { delete[]p; p = NULL; cout << "析构了一个新式二维向量" << endl; }
newvector2& newvector2::operator=(const newvector2& v) { this->x = v.x; this->y = v.y; delete[](this->p); this->p = new char[strlen(v.p) + 1]; strcpy_s(this->p,strlen(v.p)+1,v.p); return *this; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
int main() { newvector2 v1(2, 3, "first") newvector2 v2 = v1 newvector2 v3, v4 v4 = v3 = v1 return 0 }
创建了一个新式二维向量 调用拷贝构造函数创建了一个新式二维向量 创建了一个新式二维向量 创建了一个新式二维向量 调用赋值运算符为一个对象赋值 调用赋值运算符为一个对象赋值 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量
|
(三)重载+ (- | * | / | %相同):
一般建议使用友元函数重载,但是也可以使用全局函数或者成员函数重载。
本类型中尽量不要使用引用进行传递,否则会无法进行隐性的类型转化。
当然一般情况下建议使用引用进行传递,不过这样在不加其他函数的情况下不可以进行隐式类型转化。
成员函数的弊端是不能只把基本类型放在左操作数上(不发生隐形类型转化,有限制),不过可以让两个均为基本类型(均发生隐形类型转化)
是比较简单的二元运算符重载。
1 2 3 4 5
|
friend vector2 operator +(vector2 v1, vector2 v2);
newvector2 operator+(newvector2 v);
|
1 2 3 4 5 6 7 8 9 10 11
|
vector2 operator+(vector2 v1,vector2 v2) { return vector2(v1.x + v2.x, v1.y + v2.y); }
newvector2 newvector2::operator+(newvector2 v) { return newvector2(this->x + v.x, this->y + v.y, this->p); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| //main.cpp
int main() { vector2 v1(1, 2); vector2 v2(3, 4); vector2 v3(4, 8); vector2 v4 = v1 + v2 + v3; v4.print(); vector2 v5 = 1.0 + v3; //发生隐性类型转化 v5.print(); vector2 v6 = v3 + 2.0; //同 v6.print(); vector2 v7 = 3.0 + 4.0; //同 v7.print();
newvector2 vv1(1, 2); newvector2 vv2(2, 3); newvector2 vv3(3, 4); newvector2 vv4 = vv1 + vv2 + vv3; vv4.print(); newvector2 vv6 = vv3 + 2.0; //发生隐性类型转化 vv6.print(); //newvector2 vv5 = 1.0 + vv3; 成员函数的弊端:必须把左操作数设置为类对象 newvector2 vv7 = 3.0 + 4.0; //同转化 vv7.print(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| 创建了一个二维向量 创建了一个二维向量 创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 x:8,y:14 调用拷贝构造函数创建了一个二维向量 创建了一个二维向量 创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 x:5,y:8 创建了一个二维向量 调用拷贝构造函数创建了一个二维向量 创建了一个二维向量 析构了一个二维向量 析构了一个二维向量 x:6,y:8 创建了一个二维向量 x:7,y:0 创建了一个新式二维向量 创建了一个新式二维向量 创建了一个新式二维向量 调用拷贝构造函数创建了一个新式二维向量 调用拷贝构造函数创建了一个新式二维向量 创建了一个新式二维向量 析构了一个新式二维向量 创建了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 x:6,y:9 创建了一个新式二维向量 创建了一个新式二维向量 析构了一个新式二维向量 x:5,y:4 创建了一个新式二维向量 x:7,y:0 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个新式二维向量 析构了一个二维向量 析构了一个二维向量 析构了一个二维向量 析构了一个二维向量 析构了一个二维向量 析构了一个二维向量 析构了一个二维向量
|
可以仔细想想每个构造和析构函数出现的原因(当思考题了😋)
(四)重载<和> (其他比较运算符性质相同):
此处只通过友元函数重载,且使用const引用。
注意,此要返回bool值,尽可能不要返回其他类型。
1 2 3 4
|
friend bool operator <(const vector2& v1, const vector2& v2); friend bool operator >(const vector2& v1, const vector2& v2);
|
1 2 3 4 5 6 7 8 9 10 11
| //vector2.cpp //增加两个友元函数的定义 bool operator <(const vector2& v1, const vector2& v2) { return bool(v1.x * v1.x + v1.y + v1.y < v2.x* v2.x + v2.y * v2.y); }
bool operator >(const vector2& v1, const vector2& v2) { return bool(v1.x * v1.x + v1.y + v1.y > v2.x* v2.x + v2.y * v2.y); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include"vector2.h" int main() { vector2 v1(1, 2); vector2 v2(2, 2); if (v1 < v2) { cout << "v1到0距离小于v2" << endl; } else if (v1 > v2) { cout << "v1到0距离大于v2" << endl; } return 0; }
创建了一个二维向量 创建了一个二维向量 v1到0距离小于v2 析构了一个二维向量 析构了一个二维向量
|
(五)重载[]和():
是二元运算符,但是均只能用成员函数重载。
-
重载下表运算符[]:
用于访问数据对象的元素,这种写法更符合我们的习惯。
x[y]等价于x.operator(y)
参数类型和返回类型可以自定。
但是需要我们自己增加下标越界检查,这样[]的使用更为安全。
1 2 3 4 5 6
|
double operator[](int i);
double darray[20];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
newvector2::newvector2(double vx, double vy,const char*vp) :x(vx), y(vy) { for (int i = 0; i < 20; i++) { darray[i] = i+1; } p = new char[strlen(vp) + 1]; strcpy_s(p, strlen(vp)+1, (char*)vp); cout << "创建了一个新式二维向量" << endl; } newvector2::newvector2(const newvector2& v) { this->x = v.x; this->y = v.y; for (int i = 0; i < 20; i++) { this->darray[i] = v.darray[i]; } p = new char[strlen(v.p) + 1]; strcpy_s(p, strlen(v.p) + 1, v.p); cout << "调用拷贝构造函数创建了一个新式二维向量" << endl; }
newvector2& newvector2::operator=(const newvector2& v) { this->x = v.x; this->y = v.y; for (int i = 0; i < 20; i++) { this->darray[i] = v.darray[i]; } delete[](this->p); this->p = new char[strlen(v.p) + 1]; strcpy_s(this->p,strlen(v.p)+1,v.p); cout << "调用赋值运算符为一个对象赋值" << endl; return *this; }
double newvector2::operator[](int i) {
if (i < 20 and i >= 0) { return darray[i]; } else { cout << "越界!将输出限定为0" << endl; return 0.0; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include"vector2.h" int main() { newvector2 v(1, 2, "1"); cout << v[5] << endl; cout << v[222] << endl; return 0; }
创建了一个新式二维向量 6 越界!将输出限定为0 0 析构了一个新式二维向量
|
-
重载函数调用符:
用于函数调用。
x(y1,y2,y3,...)等价于x.operator()(y1,y2,y3,...)
这样,我们可以像使用函数一样使用类的对象,因为其可以存储类的状态,所以相较于函数这种写法更加灵活。
调用运算符的参数表是不唯一的,可以根据需要确定个数及类型。
返回类型也没有限定。
这里的函数调用符用于计算传入两个值作为一个点到(0,0)的距离。
1 2 3
|
double operator()(double xx, double yy);
|
1 2 3 4 5 6
|
double newvector2::operator()(double xx, double yy) { return sqrt(xx * xx + yy * yy); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| #include"vector2.h" int main() { newvector2 v; cout << v(2, 3) << endl; return 0; }
创建了一个新式二维向量 3.60555 析构了一个新式二维向量
|
(六)重载<<和>>:
istream和ostream是c++的预定义流类。
cin是istream的对象,cout是ostream的对象。
运算符<<由ostream重载为插入操作,用于输出基本类型数据,我们可以对其重载,使输出为自定义的数据类型。
运算符>>由istream重载为提取操作,用于输入基本类型数据,我们可以对其重载,使输入为自定义的数据类型。
只能重载为友元函数。(因为cout<<的左操作数只能为cout,右操作数才是需要输出的对象,cin>>同理)
返回类型限定为ostream& 和istream&,第一个参数强制为ostream&和istream&,第二个参数为用户自定义的类型。(引用绝对不可以省略!)
1 2 3 4
|
friend ostream& operator <<(ostream& output, const vector2& v); friend istream& operator >>(istream& input, vector2& v);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
ostream& operator <<(ostream& output, const vector2& v) { output << "x:" << v.x << ",y:" << v.y << endl; return output; }
istream& operator >>(istream& input, vector2& v) { cout << "请分别输入这个二维向量的x和y:" << endl; input >> v.x; input >> v.y; return input; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include"vector2.h" int main() { vector2 v; cin >> v; cout << v; return 0; }
创建了一个二维向量 请分别输入这个二维向量的x和y: 5 9 x:5,y:9 析构了一个二维向量
|