对运算符进行重载【C++】
一、多态性的概念
????????多态性是面向对象程序设计的重要特性之一。
????????多态性主要体现在:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行
为。也就是说,每个对象可以用自己的方式去响应共同的消息。
????????C++ 支持两种形式的多态性,一种是编译时的多态性,称为静态联编。一种是运行时多态,
称为动态联编。
? ? ? ? 多态的实现:函数重载、运算符重载、虚函数。
二、运算符重载
????????在以前的学习中,C++ 中预定义的运算符的操作对象只能是基本数据类型。实际上,对于很
多用户自定义的类型(如类),也需要有类似的运算操作。
?(1)运算符重载是对已有的运算符赋予多重含义
必要性:C++ 中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类
型(如类)
实现机制:将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的
实参。编译系统对重载运算符的选择,遵循函数重载的选择原则。
(2)运算符重载规则
????????C++ 中运算符的重载虽然给我们设计程序带来很多的方便,但对运算符的重载时,以下的几
种情况需要注意:
????????a.?一般来说,不改变运算符原有含义,只让它能针对新类型数据的实际需要,对原有运算符
进行适当的改造。例如,重载“+”运算符后,它的功能还是进行加法运算。
????????b.?重载运算符时,不能改变运算符原有的优先级别,也不能改变运算符需要的操作数的数
目。重载之后运算符的优先级和结合性都不会改变。
????????c.?不能创建新的运算符,只能重载 C++?中已有的运算符。
????????d.?有些运算符不能进行重载。如:“ . ” 类成员运算符、“ * ” 类指向运算符、“ :: ”类作用域运符、“ ? : ” 条件运算符及 “ sizeof ”?求字节数运算符。
????????e.?“ = ” 和 “ & ” 不必用户重载
????????运算符重载不能滥用,只有当用户自定义类型上的操作与内置运算符之间存在逻辑对应关系
时,重载的运算符才能使程序显得更自然、更直观。
(3)运算符重载的实现
????????运算符重载的本质就是函数重载。在实现过程中,首先把指定的运算表达式转化为对运算符
函数的调用,运算对象转化为运算符函数的实参,然后根据实参的类型来确定需要调用的函数,这
个过程是在编译过程中完成的。
????????运算符重载形式有两种:重载为类的成员函数和重载为类的友元函数。
a. 运算符重载为类的成员函数
语法形式为:
函数类型 operator 运算符(形参表) {
? ? ? ? 函数体;
}
b. 运算符重载为类的友元函数
运算符重载还可以为友元函数。当重载友元函数时,将没有隐含的参数 this 指针。语法形式如下:
friend 函数类型 operator 运算符(形参表);
重载为类成员函数时:参数个数 = 原操作数个数 - 1(后置 ++、-- 除外);
重载为友元函数时:参数个数 = 原操作数个数,且至少应该有一个自定义类型的形参。
三、双目运算符重载
(1)双目运算符 B:
????????如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为 A 类对
象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2 所属的类型。经重载后,表达式
oprd1 B oprd2 相当于 oprd1.operator B(oprd2)。
【题目】将”+”、”-”运算重载为复数类的成员函数。
【规则】实部和虚部分别相加减。
【操作数】两个操作数都是复数类的对象。
#include<iostream>
using namespace std;
class complex { //复数类声明
public:
//构造函数
complex(double r = 0.0, double i = 0.0) {
real = r;
imag = i;
}
complex operator + (complex c2); // + 重载为成员函数
complex operator - (complex c2); // - 重载为成员函数
/*
friend complex operator + (complex c1, complex c2);
// 运算符 + 重载为友元函数
friend complex operator - (complex c1, complex c2);
// 运算符 - 重载为友元函数
*/
void display(); //输出复数
private:
double real; //复数实部
double imag; //复数虚部
};
complex complex::operator + (complex c2) { //重载函数实现
complex c;
c.real = c2.real + real;
c.imag = c2.imag + imag;
return complex(c.real, c.imag);
}
/*
// 运算符重载友元函数实现
complex operator +(complex c1,complex c2) {
return complex(c2.real+c1.real, c2.imag+c1.imag);
}
*/
complex complex::operator - (complex c2) { //重载函数实现
complex c;
c.real = real - c2.real;
c.imag = imag - c2.imag;
return complex(c.real, c.imag);
}
/*
// 运算符重载友元函数实现
complex operator -(complex c1,complex c2) {
return complex(c1.real-c2.real, c1.imag-c2.imag);
}
*/
void complex::display() {
cout << "(" << real << "," << imag << ")" << endl;
}
int main() {
complex c1(5, 4), c2(2, 10), c3; //声明复数类的对象
cout << "c1 ="; c1.display();
cout << "c2 ="; c2.display();
c3 = c1 - c2; //使用重载运算符完成复数减法
cout << "c3 = c1 - c2 =";
c3.display();
c3 = c1 + c2; //使用重载运算符完成复数加法
cout << "c3 = c1 + c2 =";
c3.display();
return 0;
}
?【例】两字符串相加法
#include <iostream>
#include <cstring>
using namespace std;
class String{
private:
char name[200];
public:
String(char* str) {
strcpy(name, str);
}
~String() {}
String operator+ (const String&);
void display() {
cout << "The string is:" << name << endl;
}
};
static char* str;
String String::operator + (const String& a) {
strcpy(str, name);
strcat(str, a.name);
return String(str);
}
int main() {
str = new char[200];
String demo1("Just Do Yourself!1 ");
String demo2("Just Do Yourself!2 ");
demo1.display();
demo2.display();
String demo3 = demo1 + demo2;
demo3.display();
String demo4 = demo3 + "Just Do Yourself!3 ";
demo4.display();
delete []str;
return 0;
}
?(2)双目运算符重载为友元函数
????????如果需要重载一个运算符,使之能够用于操作某类对象的私有成员,可以此将运算符重载为
该类的友元函数。双目运算符 B 重载后,表达式?oprd1 B oprd2?等同于?operator B (oprd1, oprd2
)。例如:将 +、-(双目)重载为复数类的友元函数。两个操作数都是复数类的对象。
(3)注意:
? ? ? ? C++ 规定 “ = ”、“ [] ”、“ () ”、" -> "重载为成员函数;“ << ”、“ >> ” 和类型转换运算符重载为
友元函数;一般将双目运算符重载为友元函数,单目运算符和符号运算符重载为成员函数。
四、单目运算符重载
????????类的单目运算符可重载为一个没有参数的非静态成员函数或者带有一个参数的非成员函数,
参数必须是用户自定义类型的对象或者是对该对象的引用。一般情况下,单目运算符最好重载为成
员函数。
????????在 C++ 中,单目运算符有?++?和?--?,它们是变量自动增?1?和自动减?1?的运算符。在类中可以
对这两个单目运算符进行重载。
? ? ? ? “ ++ ” 和 “ -- ” 重载运算符有前缀和后缀两种运算符重载形式,以 “ ++ ” 重载运算符为例,语
法格式为:
函数类型 operator ++?() ;? ? ? ? ???// 前缀运算
使用前缀运算符的格式为:
++ 对象;
函数类型 operator -- ( int );? ? ? ? // 后缀运算
使用后缀运算符的格式为:
对象 ++;
Example:?
// 声明成类成员函数
#include <iostream>
using namespace std;
class Increase {
public:
Increase(int x) :value(x) {}
Increase operator++(); //前增量
Increase operator++(int); //后增量
void display() {
cout << "The value is " << value << endl;
}
private:
int value;
};
Increase Increase::operator ++ () {
value++; //先增量
return *this; //再返回原对象
}
Increase Increase::operator ++ (int) {
Increase temp(*this); //临时对象存放原有对象值
value++; //原有对象增量修改
return temp; //返回原有对象值
}
int main() {
Increase n(20);
n.display();
(n++).display(); //显示临时对象值
n.display(); //显示原有对象
++n;
n.display();
++(++n);
n.display();
(n++)++; //第二次增量操作对临时对象进行
n.display();
return 0;
}
//声明成友元函数
#include <iostream>
using namespace std;
class Increase {
public:
Increase(int x) :value(x) {}
friend Increase operator++(Increase&); //前增量
friend Increase operator++(Increase&, int); //后增量
void display() {
cout << " The value is " << value << endl;
}
private:
int value;
};
Increase operator ++ (Increase& a) {
a.value++; //前增量
return a; //再返回原对象
}
Increase operator ++ (Increase& a, int) {
Increase temp(a); //通过拷贝构造函数保存原有对象值
a.value++; //原有对象增量修改
return temp; //返回原有对象值
}
int main() {
Increase n(20);
n.display();
(n++).display(); //显示临时对象值
n.display(); //显示原有对象
++n;
n.display();
++(++n);
n.display();
(n++)++; //第二次增量操作对临时对象进行
n.display();
return 0;
}
五、插入和提取运算符重载
????????I / O?流库一个重要的特性——新的数据类型的输入输出。为了实现该功能,用户可以重载“
<<”和“>>”运算符。重载为类的友元函数,一般格式:
friend istream & operator >> (istreaam &,ClassName &);
friend ostream & operator << (ostream &,ClassName &);
?Example:
【例1】重载插入和提取运算符,表示日期。
#include<iostream>
using namespace std;
class Date {
public:
Date(int y, int m, int d) {
Year = y;
Month = m;
Day = d;
}
friend ostream & operator << (ostream& os, Date& date);
friend istream & operator >> (istream& is, Date& date);
private:
int Year, Month, Day;
};
ostream& operator << (ostream& os, Date& date) {
os << date.Year << "/" << date.Month << "/" << date.Day << endl;
return os;
}
istream& operator >> (istream& is, Date& date) {
is >> date.Year >> date.Month >> date.Day;
return is;
}
int main() {
Date Date1(2021, 7, 1);
cout << "Current date:" << Date1 << endl;
cout << "Enter new date:";
cin >> Date1;
cout << endl<<"New date:" << Date1 << endl;
return 0;
}
【例2】复数类问题
#include <iostream>
using namespace std;
class Complex{
public:
Complex(double r = 0, double i = 0) {
Real = r;
Imag = i;
}
Complex& operator + (const Complex& a) {
Real += a.Real;
Imag += a.Imag;
return *this;
}
friend ostream& operator << (ostream& MyStream, const Complex& a);
private:
double Real;
double Imag;
};
ostream& operator << (ostream& MyStream, const Complex& a) {
MyStream << a.Real << " + " << a.Imag << "i" << endl;
return MyStream;
}
int main() {
Complex c1(1, 2), c2(3, 4), z;
z = c1 + c2;
cout << c1 << c2 << z;
return 0;
}
【例3】重载插入和提取运算符,直接输入和输出对象。
#include <iostream>
using namespace std;
class CMoney{
public:
friend ostream& operator << (ostream&, CMoney&);
friend istream& operator >> (istream&, CMoney&);
CMoney(int m = 0, int c = 0) {
Dollar = m;
Cents = c;
}
int Dollar, Cents; // 公有成员
};
ostream& operator << (ostream& os, CMoney& m) {
os << m.Dollar << "元" << m.Cents << "分"<<endl;
return os;
}
istream& operator >> (istream& is, CMoney& m) {
is >> m.Dollar >> m.Cents;
return is;
}
int main() {
CMoney m;
cout << "输入两个整数:";
cin >> m;
cout << m;
return 0;
}
?六、特殊运算符重载 —— 赋值运算符重载
????????在 C++?中有两种类型的赋值运算符:一类是?“?+=?”?和?“?-=?”?等先计算后赋值的运算符,另一
类是?“?=?”?即直接赋值的运算符。
1. 运算符 “ += ” 和 “ -= ” 的重载
【例】实现复数类 “ += ” 和 “ -= ” 的重载。
#include <iostream>
using namespace std;
class Complex {
public:
Complex(double r, double i) {
real = r;
image = i;
}
Complex operator -= (Complex& t);
Complex operator += (Complex& t);
void Print();
private:
double real, image;
};
Complex Complex::operator -= (Complex& t) {
real -= t.real;
image -= t.image;
return *this;
}
Complex Complex::operator += (Complex& t) {
real += t.real;
image += t.image;
return *this;
}
void Complex::Print() {
if (image < 0)
cout << real << image << "i" << endl;
else
cout << real << " + " << image << "i" << endl;
}
int main() {
Complex c1(5.0, 3.0), c2(2.1, 1.8), c3(5.3, 4.2);
c1 -= c2;
cout << "c1 = ";
c1.Print();
c3 += c2;
cout << "c3 = ";
c3.Print();
return 0;
}
?2. 运算符 “ = ” 的重载
【例】实现运算符 “ = ” 的重载。
#include <iostream>
#include <cstring>
using namespace std;
class Message{
public:
Message() {
buffer = NULL;
}
~Message() {
delete buffer;
}
void Display() {
cout << buffer << endl;
}
void Set(char* string) {
if (buffer != NULL)
delete buffer;
buffer = new char[strlen(string) + 1];
strcpy(buffer, string);
}
void operator=(const Message& Message) {
if (buffer != NULL)
delete buffer;
buffer = new char[strlen(Message.buffer) + 1];
strcpy(buffer, Message.buffer);
}
private:
char* buffer;
};
int main() {
Message c1;
c1.Set("第一条消息");
c1.Display();
Message c2;
c2.Set("第二条消息");
c2.Display();
c1 = c2;
c1.Display();
return 0;
}
3. 下标运算符重载
????????下标运算符 “[ ]” 通常用于在数组中标识数组元素的位置,下标运算符重载可以实现数组数据
的赋值和取值。下标运算符重载函数只能作为类的成员函数,不能作为类的友元函数。下标运算符
“[ ]” 函数重载的一般形式为:
返回类型 类名 :: operator [?] (形参) {
? ? ? ? 函数体
}
// 形参在这里表示下标,规定只能有一个参数
?【例】实现下标运算符 " [ ] " 的重载。
#include <iostream>
using namespace std;
class Demo {
public:
Demo (){}
int& operator[](int i); // 声明下标运算符 [] 重载函数
private:
int Vector[5];
};
int& Demo ::operator[](int i) { // 定义下标运算符 [] 重载函数
return Vector[i];
}
int main() {
Demo v;
for (int i = 0; i < 5; i++)
v[i] = i + 1;
for (int i = 0; i < 5; i++)
cout << v[i] << " ";
cout << endl;
return 0;
}
|