当前位置: 首页 > news >正文

十二、操作符重载

十二、运算符重载

函数重载(Function Overloading)

  • 不同的函数有着一样的名字(多态)

  • 在C++中,一个函数的表示不仅取决于它的名称,还取决于参数的数量、类型,以及是否带有关键字 const

    示例

    void swap(unsigned long&,unsigned long&);
    void swap(double&,double&);
    void swap(char&,char&);
    void swap(Point&,Point&);
    

    它们都是不同的函数。同名函数有着相似的功能。

为什么运算符需要重载

class complex(){
public:complex(double x = 0,double y = 0){re = x; im = y;}const complex Add(const complex& c){double t1 = re + c.re;double t2 = im + c.im;return complex(t1,t2);}
private:double re,im;
};
void main(){complex c,c1,c2(5.5,2);c = c1.Add(c2);
}

这串代码是没错,但我们希望类之间的+,-可以像内建类型一样,比如:

int a = 9;int b = 3;
int c = a + b;

我们希望可以将c = c1.Add(c2); 改成 c = c1 + c2; 这里就需要用到操作符重载。

12.1 引言

  • 运算符重载是另外一种调用函数的方式。
  • 它的目的是让涉及你的类的代码更容易编写,尤其更容易阅读。
  • 在表达式中,凡是只包含内建数据类型的运算符,都是无法被改变的。只有包含用户自定义类型的表达式,才能有重载的运算符。
  • 不能定义新的运算符符号,但是当现有运算符集合不够用时,可以使用函数调用的形式来代替。

12.2 语法格式

一般语法格式

  • 运算符函数的名称是关键字operator 加上实际的运算符本身。

    type operator@(参数列表)
    {//实现代码
    }
    
  • 定义一个重载运算符就像定义一个函数,但是函数的名字是operator@ ,其中@ 表示被重载的运算符

  • 运算符重载函数中的参数个数,取决于两个因素:

    • 一元(unary)运算符(一个操作数)还是二元(binary)运算符(两个操作数)
    • 是定义为全局函数还是成员函数:
      • 如果是全局函数:
        • 一元运算符:一个参数
        • 二元运算符:两个参数
      • 如果是成员函数:
        • 一元运算符:无参数(隐含this)
        • 二元运算符:一个参数(另一个操作数)

示例:

//C12:OperatorOverloadingSyntax.cpp
#include <iostream>
using namespace std;class Integer {int i;
public:Integer(int ii) :i(ii) {}void print() { cout << i << endl; }const Integer operator+(const Integer& rv) const;
};
const Integer Integer::operator+(const Integer& rv) const {return Integer(i + rv.i);
}void main() {int i = 1, j = 2, k = 0;k = i + j;Integer ii(1), jj(2), kk(0);kk = ii + jj;kk.print();
}
  • kk = ii + jj; 这一步会先计算右边的ii + jj,然后正好匹配

    const Integer operator+(const Integer& rv) const;
    

    因为这是一个成员函数,所以ii + jj会被翻译为:

    ii.operator+(jj)
    

    也就是说,编译器看到ii + jj,就会转化为

    const Integer temp = ii.operator+(jj);
    

    这时ii是隐式的thisjj是参数rv

    然后再

    kk  = temp;
    

    所以表达式

    kk = ii + jj;
    

    等于编译器内部操作

    const Integer temp = ii.operator+(jj);//调用operator+
    kk = temp;//把结果赋值给kk
    

成员函数

这里讲述,当运算符是重载函数是成员函数时的语法

格式:
type operator@(argument){/*……*/}
  • 表示你要重载一个运算符@ (比如 +-++ 等),其返回类型是type,参数列表根据运算符类型不同而不同。
用法:
  • 一元前缀运算符:

    • 例子:@a
    • 对应函数:a.operator@()
    • 举例:-a会转化为a.operator-()
  • 一元后缀运算符:

    • 例子:a@

    • 对应函数:a.operator@(int)

    • 说明:后缀运算符需要增加一个int 类型的占位参数来区分前缀形式(C++为了分辨前缀还是后缀而决定的,有int 是后缀)

      举例:a++ 会转为 a.operator++(int) ,而++a 则是 a.operator++()

  • 二元运算符:

    • 例子:a@b
    • 对应函数:a.operator@(b)
    • 举例:a+b会转为a.operator+(b)
注意:
  • 成员函数都会隐式包含一个this 指针,指向左侧对象a
  • 前缀一元运算:无参数(如++a
  • 后缀一元运算:只有一个整型参数int(占位符)(如a++
  • 二元运算符:只有一个参数(右操作数),而this指向左操作数。
示例

complex.h

class complex{
public:complex (double r = 0,double i = 0);const complex operator+(const complex& c); //二元运算符const complex operator-();//一元前缀运算符complex& operator+=(const complex& c);//二元运算符void print() const;
private:double real,imag;
};

complex.cpp

#include <iostream>
#include "complex.h"using namespace std;complex::complex(double r, double i) {real = r; imag = i;
}
const complex complex::operator+(const complex& c) {double r = real + c.real;double i = imag + c.imag;return complex(r, i);
}
const complex complex::operator-() {return complex(-real, -imag);
}
complex& complex::operator+=(const complex& c) {real += c.real;imag += c.imag;return *this;
}void complex::print() const {cout << "(" << real << "," << imag << ")" << endl;
}

user.cpp

#include <iostream>
#include "complex.h"
void main()
{complex c1(3.5,5.5),c2(1.5,3.5);complex c;c = c1 + c2; // 对应着c = c1.operator(c2)c.print();c += c1 += c2; //c1.operator+=(c2);c.operator+=(c1);c.print();c = -c1;   //c = c1.operator-()c.print();
}

输出

(5,9)
(10,18)
(-5,-9)

友元函数

格式:

friend type operator@(argument){/*……*/}

用法:
  • 前缀一元运算符:@a -> operator@(a)
  • 后缀一元运算符:a@ -> operator@(a,int)
  • 二元运算符:a@b -> operator@(a,b)
注意:

友元函数和成员函数不同的原因在于,友元函数没有隐形指针。

示例

complex.h

//complex.h
class complex {
public:complex(double r = 0, double i = 0);friend const complex operator+(const complex& c1, const complex& c2); //二元运算符friend const complex operator-(const complex& c);//一元前缀运算符friend complex& operator+=(complex& c1,const complex& c2);//二元运算符void print() const;
private:double real, imag;
};

complex.cpp

//complex.cpp
#include <iostream>
#include "complex.h"using namespace std;complex::complex(double r, double i) {real = r; imag = i;
}const complex operator+(const complex& c1, const complex& c2) {double r = c1.real + c2.real;double i = c1.imag + c2.imag;return complex(r, i);
}
const complex operator-(const complex& c) {return complex(-c.real, -c.imag);
}
complex& operator+=(complex& c1, const complex& c2) {c1.real += c2.real;c1.imag += c2.imag;return c1;
}
void complex::print() const {cout << "(" << real << "," << imag << ")" << endl;
}

user.cpp

//user.cpp
#include <iostream>
#include "complex.h"void main() {complex c1(3.5, 5.5), c2(1.5, 3.5);complex c;c = c1 + c2; //c = operator(c1,c2)c.print();c += c1 += c2; //operator+=(c1,c2);operator+=(c,c1);c.print();c = -c1; //operator-(c1)c.print();
}

输出

(5,9)
(10,18)
(-5,-9)

运算符重载函数与普通函数

  • 运算符函数的声明和调用函数与普通函数相同。
  • 运算符函数的参数可以是一个或2个,而普通函数可以有多个参数
  • 运算符函数必须是成员函数或者至少有一个参数是用户自定义类型(类或结构)

12.3 可重载的运算符(Overloaded Operators)

12.3.1 一元运算符

  • 可重载的一元运算符包括:+-!~++--&*

12.3.2 二元运算符

  • 可重载的二元运算符包括:+-*/&==!=
  • <><=>=&&||<<>>
  • =+=-=*=/=&=|=^=<<=>>=

12.3.3 参数与返回值

  • 就像处理任何函数参数一样,如果你只需要读取参数而不修改它,默认应该将其作为常量引用 传递。
  • 我们选择哪种类型的返回值,取决于该运算符的预期含义。(例如二元加法运算符operatoa+ ,就应该返回一个常量值——两个操作数的和)
  • 所有赋值运算符都会修改左值,因此应该返回一个非常量引用(便于链式调用)。例如:a += b
  • 对于逻辑运算符,通常期望返回的最差情况是一个int最理想的是一个bool

示例,依旧是12.2当中成员函数的例子,来表现当返回值是const的情况

//成员函数
const complex complex::operator+(const complex& c){double r = real + c.real;double i = imag + c.imag;return complex(r,i);
}complex& complex::operator+=(const complex& c){real = r.real;imag = c.imag;return *this;
}

(a+b)按值返回一个const

  • f(a+b) :表达式a+b 的结果变成了一个临时的const对象 ,在调用函数f() 时使用这个临时对象。
  • (a+b).g():由于 a+b 的返回值是const ,这意味着只能调用const成员函数

示例,来表现临时对象

示例1

#include <iostream>
using namespace std;
class A{int i;
public:A(int x = 0){i = x;cout << "Constructor." << endl;}A(const A&x){i = x.i ; cout << "Copy Constructor." << endl;}~A() { cout << "Destructor." << endl; }
};
A f(){return A(1);}
void main(){f();cout << "main() End." << endl;
}

输出

Constructor.
Destructor.
main() End.

示例2

此时我们将示例1中的A f(){return A(1);} 替换成A f(){A a(1);return A(a);}

#include <iostream>
using namespace std;
class A {int i;
public:A(int x = 0) { i = x; cout << "Constructor." << endl; }A(const A& x) { i = x.i; cout << "Copy Constructor." << endl; }~A() { cout << "Destructor." << endl; }
};
A f() {A a(1); return A(a); // 显式调用拷贝构造
}
void main() {f();  // 接收返回值,确保 Copy Constructor 被触发(C++14)cout << "main() End." << endl;}

输出

Constructor.
Copy Constructor.
Destructor.
Destructor.
main() End.

解析:

return A(a)

这一行:

  • 创建了一个临时对象(记作temp),它是a的拷贝。
  • A(a)是一个 临时的 unnamed 对象 ,将作为函数f()的返回值。

然后main()里面调用:

f();

这个临时返回值没有被捕获,所以:

  • 临时对象仍然被创建(必须被创建,因为return A(a)是显示拷贝),这里不用return a,是因能这样可以会被编译器自动优化,导致看不到返回值有临时对象这一过程。
  • 它(临时返回值 temp )会在这条语句结束后立即析构。
建议
  • 如果运算符的第一个操作数是某个类的对象,那么这个运算符应该被声明为该类的成员函数。(例如 a + b 、 a + 5)
  • 否做,它应该被声明为友元函数。(例如 6 + a)

错误样例

#include <iostream>
using namespace std;
class complex
{
public:complex(double r = 0, double i = 0) {real = r;imag = i;cout << "Constructor." << r << "." << i << endl;}const complex operator+(const complex& c);void print() const {cout << "(" << real << "," << imag << ")" << endl;}
private:double real, imag;
};const complex complex::operator+(const complex& c) {double r = real + c.real;double i = imag + c.imag;return complex(r, i);
}
void main() {complex c, c1(3.5, 5.5);c = c1 + 1.5;//ok: c = c1.operator+(1.5)c.print(); //(5,5.5)//c = 1.5 + c1; //error: c = 1.5.operator+(c1)
}

输出

Constructor.0.0     //complex c
Constructor.3.5.5.5   //complex c1(3.5,5.5)
Constructor.1.5.0   // c1.operator.(1.5)中的1.5被隐式转换为complex类型
Constructor.5.5.5   //return complex(r,i),创建了一个新的临时complex(5.5,5.5)
(5,5.5)

正确样例1

#include <iostream>
using namespace std;
class complex
{
public:complex(double r = 0, double i = 0) {real = r;imag = i;cout << "Constructor." << r << "." << i << endl;}friend const complex operator+(const complex& c1,const complex& c2);void print() const {cout << "(" << real << "," << imag << ")" << endl;}
private:double real, imag;
};const complex operator+(const complex& c1,const complex& c2) {double r = c1.real + c2.real;double i = c1.imag + c2.imag;return complex(r, i);
}
void main() {complex c, c1(3.5, 5.5);c = c1 + 1.5;//ok: c = operator(c1,1.5)c.print(); //(5,5.5)c = 1.5 + c1; //ok: c = operator(1.5,c1)
}

输出

Constructor.0.0
Constructor.3.5.5.5
Constructor.1.5.0
Constructor.5.5.5
(5,5.5)
Constructor.1.5.0
Constructor.5.5.5

正确样例2

#include <iostream>
using namespace std;
class X {
public:void operator+(int) {}X(int) {}
};
void operator+(X, X) {}
void operator+(X, double) {}
void f(X a)
{a + 1;//a.operator+(1)1 + a;//::operator+(X(1),a)a + 1.0;//::operator+(a,1.0)
}
int main() {X a(1);f(a);return 0;
}

12.3.4特殊运算符

下标运算符[ ]
  • 操作符[]可以为一个类进行重载。
  • 注意:它必须被重载为一个成员函数
  • 因为它的语法必须是对象[索引]
#include <iostream>
using namespace std;
class vect{
public:vect(int size){v = new int[size];}~vect(){delete []v;}int& operator[](int i);
private:int* v;
};int& vect::operator[](int i){cout << "Subscripting" << endl;return v[i];
}
int main(){vect a{5};a[2] = 12;		//a.operator[](2) = 12cout << a[2] << endl;return 0;
}

输出

Subscripting
Subscripting
12
解引用操作符:->
  • ->运算符必须作为成员函数进行重载。
  • 该函数应该返回一个指向类的指针。
#include <iostream>
using namespace std;
struct Student {int age;int ID;
};
class X {
public:X() { S = new Student; S->age = 0; S->ID = 0; }Student* operator->() { return S; }~X() { delete S; }void print() { cout << S->age << "." << S->ID << endl; }
private:Student* S;
};int main() {X x;x->age = 20;//(x.operator->())->age = 20x->ID = 001;//(s.operator->())->ID = 001x.print();return 0;
}

如果没有 S->age = 0; S->ID = 0; 那么刚开始S里面的 age , ID 会被初始化为垃圾值

输出

20.1
自增(++)和自减(–)运算符
  • 自增(++)和自减(–)运算符可以作为前缀(例如++x)或后缀(例如x++)使用。重载时需要分别定义前缀版本和后缀版本(互相区分的方法是后缀多一个整型的占位符)。通常,这两个运算符重载为类的成员的函数

  • 前缀形式的函数原型:T operator++();后缀形式:T operator++(int)

    这里要说明一般来说前缀形式写成 T& operator++(),包括下面的T& operator--(),因为这样可以优化掉拷贝函数,并且返回值还可以作为左值(有确切地址的变量)来调用成员函数。

  • 前缀形式的函数原型:T operator--();后缀形式:T operator--(int)

  • 上述int 不参与实际使用,仅用于与前缀形式区分后缀形式

#include<iostream>
using namespace std;
class Increase {
public:Increase(int val = 0) { value = val; }Increase(const Increase& i) { cout << "Copy Constructor" << endl; value = i.value; }void display() const { cout << value << endl; }Increase operator++();  //前缀Increase operator++(int);  //后缀
private:int value;
};
Increase Increase::operator++()
{cout << "++value" << endl;++value;return *this;
}
Increase Increase::operator++(int)
{cout << "value++" << endl;int temp = value;value++;return Increase(temp);
}void main() {Increase a(10), b(10), c;c = ++a;	//a.operator++()c.display();c = b++;	//b.operator++(int)c.display();
}

输出

++value
Copy Constructor
11
value++
10

除此之外,还有=<<>>

下表则展现了函数表达式

12.3.5无法重载的运算符

  • . 对象成员访问运算符
  • .* 指针到成员运算符
  • ::作用域解析运算符
  • ?: 三元条件运算符
  • sizeof
  • typeid
  • 注意:C++中没有内置的指数(幂)运算符;也无法引入新的运算符符号;重载运算符不会改变原有的优先级和结合性规则。

12.4成员还是非成员

  • 在重载运算符时,可以选择将其实现为类的成员函数或非成员函数(通常以友元函数形式)。

  • 一般来说,当运算符需要访问类的内部实现细节 ,或该运算符本身必须定义为成员(例如,赋值=,下标[],函数调用(),以及->++-- 运算符)时,应使用成员函数形式重载。

  • 对于对称的二元运算符(如算数运算符、比较运算符等),将其定义为非成员的友元函数通常更为灵活。这样可以允许运算符左右两侧的操作数进行类型转换。例如,假设类X有一个接受int的构造函数:如果operator+ 使X的成员函数,那么a + 1可以执行(调用a.operator+(1)),但 1 + a 将无法匹配;而将 operator+ 定义为友元函数后,表达式 1 + a可以被转换为 operator+(X(1),a) 来执行,使得两种操作数顺序都支持。

  • 总而言之,当运算符主要作用于类本身 且不需要左操作数进行额外转换时,倾向于成员函数实现;当运算符涉及不同类型之间的交互,或希望支持交换操作数顺序的调用时,使友元函数会更合适。

  • 也可以参考下图

    image-20250505185516710

12.5重载赋值运算符(Overloading assignment)

  • 赋值运算符用于将一个已存在的对象赋值为同类的另一个对象。其重载函数通常声明为:X& X::operator=(const X& other) ,即返回当前对象的引用,参数为待赋值的同类对象的常量引用。

  • 目的:通过重载赋值运算符,可以使已存在的类对象 更新为另一个同类对象的内容。

  • 若用户自定义了赋值运算符,则赋值时将调用自定义版本;如果未定义,编译器会尝试生成一个默认的公有赋值运算符(对每个成员执行逐项赋值)。

  • 例如:

    Date t1;//调用构造函数创建 t1
    Date t2;//调用拷贝构造函数,用t1初始化t2
    Date t3;//调用构造函数创建 t3
    t3 = t2;//调用赋值运算符,将t2 赋值给已存在的 t3
    

示例,用来区分拷贝构造函数和赋值运算符

#include <iostream>
using namespace std;class Location {
public:Location(int xx = 0, int yy = 0) { X = xx; Y = yy; }Location(const Location& p) {X = p.X;Y = p.Y;cout << "Copy constructor called." << endl;}Location& operator=(const Location& p);//赋值运算符int GetX() { return X; }int GetY() { return Y; }
private:int X, Y;
};Location& Location::operator=(const Location& p) {X = p.X;Y = p.Y;cout << "Assignment operator called." << endl;return *this;
}
void main() {Location A(1, 2), B;B = A;cout << "B = " << B.GetX() << "," << B.GetY() << endl;Location C = A;cout << "C = " << C.GetX() << "," << C.GetY() << endl;
}

输出

Assignment operator called.
B = 1,2
Copy constructor called.
C = 1,2
  • 注意:我们通常需要在赋值运算符中防止自赋值。

    Location& Location::operator=(const Location& p) {if (this == &p){return *this;}X = p.X;Y = p.Y;cout << "Assignment operator called." << endl;return *this;
    }
    
  • 当我们在重载一个运算符时,我们不能改变:

    • 运算符的数量
    • 优先级
    • 语法结构
    • 将运算符组合(比如把=+组合成+=
  • 如果类包含指针等动态分配资源,默认的浅拷贝赋值会导致多个对象共享同一块内存资源,这可能造成严重错误:如析构时发生双重释放(多次释放同一内存),或赋值覆盖时产生内存泄漏

    示例1

    class A{int *p;
    public:A(int i){p = new int(i);}~A(){delete p;}
    };
    void main(){A a(5);A b(a);			//拷贝函数A c(6),d(10);d = c;		//赋值运算古
    }
    
    • 这个示例中,因为是 a.pb.p 指向同一内存,5所在的内存区域会被释放两次,因此报错。
    • c.pd.p 指向一样的存储区域。6所在的内存区域将会被释放两次(会报错)。10所在的区域会永远失去。

image-20250505204849026

示例1的修改

class A{int *p;
public:A(int i){p = new int(i);}~A(){delete p;}A(const A&r);A& operator=(const A& r);
};
A::A(const A&r){p = new int(*r.p);
}
A& A::operator=(const A&r){if (this == &r) return *this;delete p;		//释放p指向的内存p = new int(*r.p);return *this;
}void main(){A a(5);A b(a);A c(6),d(10);d = c;
}

这个示例就是正确的

image-20250505220716345

示例2

#include <iostream>
using namespace std;
class A{int* p;
public:A(int i){cout << "Constructor" << endl;p = new int(i);}~A(){cout << "Destructor" << endl;delete p;}A (const A& r)//Copy Constructor{cout << "Copy-constructor" << endl;p = new int(*r.p);}A& operator=(const A&r);//赋值运算符void output() {cout << p << "->" << *p << endl;}
};
A& A::operator=(const A&r){cout << "Assignment" << endl;if (this == &r) return *this;delete p;p = nullptr;p = new int(*r.p);return *this;
}
int main(){A a(5);A b(a);//拷贝构造函数A c(6),d(10);d = c;//赋值运算符a.output();b.output();c.output();d.output();return 0;
}

输出

Constructor
Copy-constructor
Constructor
Constructor
Assignment
000001D298B1C530->5
000001D298B1C870->5
000001D298B1C2F0->6
000001D298B1CCB0->6
Destructor
Destructor
Destructor
Destructor

12.6 自动类型转换(Automatic type conversion)

  • 构造函数可以被用来指定类型转换。
  • 实现自动类型转换的第二种方法是通过转换运算符重载

12.6.1 构造函数类型转换

#include <iostream>
using namespace std;class complex{
public:complex(double r = 0,double i = 0);complex(const complex& a);friend const complex operator+(const complex& c1,const complex& c2);
private:double real,imag;
};complex::complex(double r,double i)
{real = r;imag = i;cout << "constructor:" << real << "," << imag << endl;
}complex::complex(const complex& a){real = a.real;imag = a.imag;cout << "copy constructor:" << real << "," << imag << endl; 
}const complex operator+(const complex& c1,const complex& c2){double r = c1.real + c2.real;double i = c1.imag + c2.imag;return complex(r,i);
}
void main(){complex c,c1(3.5,5.5);c = c1 + 1.5;
}

输出

constructor:0,0  //c
constructor:3.5,5.5		//c1
constructor:1.5,0	//c = operator+(c1,complex(1.5)),double -> complex
constructor:5,5.5	//return complex(r,i)

限制构造函数只能定义从其他类型转换为本类的规则,但无法定义从本类转换为基础类型,或者将本类转换为另一个已有类类型的规则(除非修改那个类的定义来添加相应构造函数)

12.6.2转换运算符

  • 为补充构造函数转换的不足,可以在类中定义类型转换运算符来将类转换为其他类型。其一般形式为:X::operator type()
  • 上述声明中,目标类型type 是运算符名称的一部分,不在函数返回类型位置出现。该运算符函数没有显式参数,它将类 类型转换为指定的目标类型。
#incldue <iostream>
using namespace std;
class complex
{double real,imag;
public:complex(double r = 0,double i = 0){real = r;imag = i;cout << "(" << real << "," << imag << ")" << endl;}operator int(){return int(real);}
};
void main(){complex c(3.5,5.5);int b = d;cout << b << endl;cout << int(c) << endl;
}

12.7 重载<<和>>

  • 流插入运算符<<流提取运算符>> 通常重载为友元函数,用于实现自定义类型于输入输出流之间的序列化。

    因为友元函数可以访问类的私有成员变量,同时<<>>左边是ostreamistream类型(例如cout << "hello world"<< 的左边就是ostream 类型),因此要把流运算符重载为友元函数

  • 下面示例定义了一个date类,并重载<<>> 运算符以支持日期的输入和输出。

#include <iostream>
using namespace std;class date {
public:date(int x = 0, int y = 0, int z = 0) { year = x, month = y, day = z; }friend ostream& operator<<(ostream& ost, const date& d);friend istream& operator>>(istream& ist, date& d);
private:int year, month, day;
};
ostream& operator<<(ostream& ost, const date& d) {//注意ost引用ostream类对象ost << d.year << ends << d.month << ends << d.day << endl;return ost;
}
istream& operator>>(istream& ist, date& d) {//注意ist引用istream类对象cout << "请输出年月日的数值的数值,如:2025 5 6" << endl;ist >> d.year >> d.month >> d.day;return ist;
}int main() {date d;cin >> d;	//即调用函数operator>>(cin,d)cout << d;	//operator<<(cout,d)return 0;
}

12.8 总结

  • 运算符函数的命名方式
  • 按运算元个数分类:一元、二元、三元运算符
  • 可以重载的运算符
  • 不可重载的运算符
  • 重载实现方式
    • 成员函数方式
    • 友元函数方式
  • 特殊运算符:[]++--= 、类型转换、<<>>

以访问类的私有成员变量,同时<<>>左边是ostreamistream类型(例如cout << "hello world"<< 的左边就是ostream 类型),因此要把流运算符重载为友元函数

  • 下面示例定义了一个date类,并重载<<>> 运算符以支持日期的输入和输出。
#include <iostream>
using namespace std;class date {
public:date(int x = 0, int y = 0, int z = 0) { year = x, month = y, day = z; }friend ostream& operator<<(ostream& ost, const date& d);friend istream& operator>>(istream& ist, date& d);
private:int year, month, day;
};
ostream& operator<<(ostream& ost, const date& d) {//注意ost引用ostream类对象ost << d.year << ends << d.month << ends << d.day << endl;return ost;
}
istream& operator>>(istream& ist, date& d) {//注意ist引用istream类对象cout << "请输出年月日的数值的数值,如:2025 5 6" << endl;ist >> d.year >> d.month >> d.day;return ist;
}int main() {date d;cin >> d;	//即调用函数operator>>(cin,d)cout << d;	//operator<<(cout,d)return 0;
}

12.8 总结

  • 运算符函数的命名方式
  • 按运算元个数分类:一元、二元、三元运算符
  • 可以重载的运算符
  • 不可重载的运算符
  • 重载实现方式
    • 成员函数方式
    • 友元函数方式
  • 特殊运算符:[]++--= 、类型转换、<<>>

相关文章:

  • 项目售后服务承诺书,软件售后服务方案,软件安装文档,操作文档,维护文档(Word原件)
  • 在CentOS 7上仅安装部署MySQL 8.0客户端
  • 在Text-to-SQL任务中应用过程奖励模型
  • AI中的MCP是什么?MCP的作用及未来方向预测 (使用go-zero 快速搭建MCP服务器)
  • 如何使用主机名在 CMD 中查找 IP 地址?
  • M0基础篇之DAC
  • 华为行业认证是什么?如何考取华为行业认证?
  • BUUCTF 大流量分析(三) 1
  • HAProxy + Keepalived + Nginx 高可用负载均衡系统
  • [网络层]ICMP协议
  • Java:编程世界的常青树与数字化转型的基石
  • Maven 项目构建时编译错误问题排查与解决
  • IDEA+git将分支合并到主分支、IDEA合并分支
  • OpenCV直方图与直方图均衡化
  • 解决vue create 创建项目,不能使用上下键选择模板的问题
  • 网页禁止粘贴的解决方法(以学习通网页为例)
  • 笔记本电脑升级实战手册【扩展篇1】:flash id查询硬盘颗粒
  • kkfileview文件上传安全漏洞原理分析及解决方案
  • 在Linux中安装JDK并且搭建Java环境
  • 如何禁止chrome自动更新
  • 夜读丨取稿费的乐趣
  • 第二期人工智能能力建设研讨班在京开班,近40国和区域组织代表参加
  • 港股持续拉升:恒生科技指数盘中涨幅扩大至6%,恒生指数涨3.3%
  • 智利观众也喜欢上海的《好东西》
  • 巴基斯坦称对印度发起军事行动
  • 习近平会见古共中央第一书记、古巴国家主席迪亚斯-卡内尔