此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C++从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C++》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C++精通面向对象编程》(曾凡锋等)。
7.重载技术
7.1 重载函数基础
-
C {\rm C} C++中,对于不同数据类型的数据做相同或相似的运算而函数名相同的情况,称为重载,被重载的函数称为重载函数;
-
重载函数实例 ( e x a m p l e 7 _ 1. c p p ) ({\rm example7\_1.cpp}) (example7_1.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/14 * 描述:利用重载定义求绝对值函数。 */ #include <iostream> using namespace std; int abssolve(int nPar) { return nPar >= 0 ? nPar : 0 - nPar; } long abssolve(long lPar) { return lPar >= 0 ? lPar : 0 - lPar; } double abssolve(double dPar) { return dPar >= 0 ? dPar : 0 - dPar; } int main() { int nNumber1 = 10, nNumber2 = -10; long lnNumber1 = 10000000, lnNumber2 = -10000000; double fNumber1 = 3.14, fNumber2 = -3.14; cout << "正整型数据绝对值:" << abssolve(nNumber1) << endl; cout << "负整数数据绝对值:" << abssolve(nNumber2) << endl; cout << "正长整型数据绝对值:" << abssolve(lnNumber1) << endl; cout << "负长整数数据绝对值:" << abssolve(lnNumber2) << endl; cout << "正浮点数数据绝对值:" << abssolve(fNumber1) << endl; cout << "负浮点数数据绝对值:" << abssolve(fNumber2) << endl; return 0; }
-
在调用重载函数时,编译器需要决定调用哪个函数,这是通过一一比较重载函数的形参和实参决定的,编译器匹配步骤:
- 寻找一个严格的匹配,如果找到了,则调用此函数;
- 通过特定的转换寻求一个匹配,如果找到了,则调用此函数;
- 通过用户定义的转换寻求一个匹配,如果能找到唯一的一组转换,则调用此函数;
-
函数在返回值类型、参数类型、参数个数、参数顺序上有所不同时,才是函数重载,如果只是返回值不同,则不能被认为是函数重载;
7.2 运算符重载
-
运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型数据的行为;
-
运算符重载是针对新类型数据的实际需要而对原有的运算符进行适当改造的行为;
-
重载的运算符必须是 C {\rm C} C++已有的运算符,不能重载的运算符包括:
- 类属关系运算符" . . .";
- 指针运算符" ∗ * ∗";
- 作用域运算符" : : :: ::";
- s i z e o f {\rm sizeof} sizeof运算符;
- 三目运算符" ? : ?: ?:";
-
重载运算符的形式:
-
一类是重载为类的成员函数,语法格式如下:
c++函数类型 operator 运算符(形参表) { 函数体; }
-
另一类重载为类的友元函数,语法格式如下:
c++friend 函数类型 operator 运算符(形参表) { 函数体; }
- 函数类型:指定重载运算符的返回值类型,即运算后的结果类型;
- o p e r a t o r {\rm operator} operator:定义运算符重载的关键字;
- 运算符:需要重载的运算符的名称,如:" + + +"、" − - −"等,必须是 C {\rm C} C++中已有的可重载运算符号;
- 形参表:重载运算符所需要的参数和类型;
- 当运算符重载为类的友元函数时,需要用 f r i e n d {\rm friend} friend关键字来修饰;
-
-
运算符重载的实质是函数重载,将其重载为类的成员函数,它可以自由地访问本类的数据成员;当运算符重载为类的函数成员时,除了" + + ++ ++"、" − − -- −−"运算符,函数的参数个数比原来的操作数个数要少一个,因为某个对象使用了运算符重载的成员函数后,自身的数据可以直接访问,不需要将自身的数据放在参数表中;
-
对于单目运算符 X {\rm X} X,如:" + + +"(正号)、" − - −"(负号)等,当将其重载为类 C {\rm C} C的成员函数时,用来实现 X o p e r a n d {\rm X\ operand} X operand,其中 o p e r a n d {\rm operand} operand为类 C {\rm C} C的对象,则 X {\rm X} X需要重载为 C {\rm C} C的成员函数,函数不需要形参;当使用 X o p e r a n d {\rm X\ operand} X operand的运算式时,相当于调用 o p e r a n d . o p e r a t o r X ( ) {\rm operand.operator\ X()} operand.operator X();
-
对于双目运算符 Y {\rm Y} Y,如:" + + +"(加号)、" − - −"(减号)等,当将其重载为类 C {\rm C} C的成员函数时,用来实现 o p e r a n d 1 Y o p e r a n d 2 {\rm operand1 \ Y\ operand2} operand1 Y operand2,其中 o p e r a n d 1 {\rm operand1} operand1为类 C {\rm C} C的对象, o p e r a n d 2 {\rm operand2} operand2为其他与 o p e r a n d 1 {\rm operand1} operand1相同或可以转换为相同类型的操作数;当使用 o p e r a n d 1 Y o p e r a n d 2 {\rm operand1\ Y\ operand2} operand1 Y operand2的运算式时,相当于调用 o p e r a n d 1. o p e r a t o r Y ( o p e r a n d 22 ) {\rm operand1.operator\ Y(operand22)} operand1.operator Y(operand22);
-
运算符重载实例 ( O v e r l o a d 1 ) ({\rm Overload1}) (Overload1)项目:
-
项目需求:利用运算符重载来实现复数的加、减运算。
-
复数类定义头文件代码 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CComplex { private: double real; // 复数的实部; double imag; // 复数的虚部; public: CComplex(double pr = 0.0, double pi = 0.0) { real = pr; imag = pi; }; virtual ~CComplex(); static CComplex Add(CComplex c1, CComplex c2); // 成员函数,实现两个复数相加; CComplex operator +(CComplex c); // 重载运算符"+",使其支持复数相加; CComplex operator -(CComplex c); // 重载运算符"-",使其支持复数相减; static void ShowComplex(CComplex c); };
-
复数类实现文件代码 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类实现文件。 */ #include "CComplex.h" CComplex CComplex::Add(CComplex c1, CComplex c2) { CComplex result; result.real = c1.real + c2.real; // 复数的实部相加; result.imag = c1.imag + c2.imag; // 复数的虚部相加; return result; } CComplex::~CComplex() { } // 重载运算符"+"的实现; CComplex CComplex::operator +(CComplex c) { return CComplex(this->real + c.real, this->imag + c.imag); } // 重载运算符"-"的实现; CComplex CComplex::operator -(CComplex c) { return CComplex(this->real - c.real, this->imag - c.imag); } void CComplex::ShowComplex(CComplex c) { cout << "(" << c.real << "," << c.imag << "i" << ")" << endl; }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:程序主文件。 */ #include "CComplex.h" int main() { CComplex a(1, 2), b(3, 4); CComplex::ShowComplex(a + b); CComplex::ShowComplex(a - b); return 0; }
-
-
运算符重载为类的友元函数,此时,它可以自由地访问该类的任何数据成员,其操作数需要通过函数的形参表来传递,操作数的顺序是按照形参中的参数从左到右来决定;
-
对于单目运算符 X {\rm X} X,如:" + + +"(正号)、" − - −"(负号)等,用来实现 X o p e r a n d {\rm X\ operand} X operand,其中 o p e r a n d {\rm operand} operand为类 C {\rm C} C的对象,则 X {\rm X} X就需要重载为 C {\rm C} C的友元函数,函数的形参为 o p e r a n d {\rm operand} operand,当使用 X o p e r a n d {\rm X\ operand} X operand的运算式时,相当于调用 o p e r a t o r X ( o p e r a n d ) {\rm operator\ X(operand)} operator X(operand);
-
对于双目运算符 Y {\rm Y} Y,如:" + + +"(加号)、" − - −"(减号)等,用来实现 o p e r a n d 1 Y o p e r a n d 2 {\rm operand1\ Y\ operand2} operand1 Y operand2,其中 o p e r a n d 1 {\rm operand1} operand1和 o p e r a n d 2 {\rm operand2} operand2为类 C {\rm C} C的对象,则 Y {\rm Y} Y需要重载为 C {\rm C} C的友元函数,函数有两个参数,当使用 o p e r a n d 1 Y o p e r a n d 2 {\rm operand1\ Y\ operand2} operand1 Y operand2的运算式时,相当于调用 o p e r a n d 1 Y ( o p e r a n d 1 , o p e r a n d 2 ) {\rm operand1\ Y(operand1,operand2)} operand1 Y(operand1,operand2);
-
运算符重载实例 ( O v e r l o a d 2 ) ({\rm Overload2}) (Overload2)项目:
-
项目需求:利用运算符重载来实现复数的加、减运算。
-
复数类定义头文件代码 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):
C++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CComplex { private: double real; // 复数的实部; double imag; // 复数的虚部; public: CComplex(double pr = 0.0, double pi = 0.0) { real = pr; imag = pi; }; virtual ~CComplex(); friend CComplex operator +(CComplex c1, CComplex c2); friend CComplex operator -(CComplex c1, CComplex c2); static void ShowComplex(CComplex c); };
-
复数类实现文件代码 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类实现文件。 */ #include "CComplex.h" CComplex::~CComplex() { } CComplex operator +(CComplex c1, CComplex c2) { return CComplex(c1.real + c2.real, c1.imag + c2.imag); } CComplex operator -(CComplex c1, CComplex c2) { return CComplex(c1.real - c2.real, c1.imag - c2.imag); } void CComplex::ShowComplex(CComplex c) { cout << "(" << c.real << "," << c.imag << "i" << ")" << endl; }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:程序主文件。 */ #include "CComplex.h" int main() { CComplex a(1, 2), b(3, 4); CComplex::ShowComplex(a + b); CComplex::ShowComplex(a - b); return 0; }
-
-
增量运算符分为前增量和后增量,前增量的返回是引用返回,后增量是值返回;使用前增量时,先对对象进行修改,然后返回该对象,因此,对于前增量运算,参数和返回的是同一个对象;使用后增量时,先返回对象原有的值,然后对对象进行修改,因此,需要创建一个临时对象存放原有的对象,保存对象的原有值以便返回;后增量返回的是原有对象的一个临时副本;
-
增量运算实例 ( O v e r l o a d 3 ) ({\rm Overload3}) (Overload3)项目:
-
项目需求:利用运算符重载实现复数类的增量运算。
-
复数类定义头文件代码 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CComplex { private: double real; // 复数的实部; double imag; // 复数的虚部; public: CComplex(double pr = 0.0, double pi = 0.0) { real = pr; imag = pi; }; virtual ~CComplex(); friend CComplex operator +(CComplex c1, CComplex c2); friend CComplex operator -(CComplex c1, CComplex c2); CComplex& operator ++(); // 前增量; CComplex operator ++(int); // 后增量; static void ShowComplex(CComplex c); };
-
复数类实现文件代码 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类实现文件。 */ #include "CComplex.h" CComplex::~CComplex() { } CComplex operator +(CComplex c1, CComplex c2) { return CComplex(c1.real + c2.real, c1.imag + c2.imag); } CComplex operator -(CComplex c1, CComplex c2) { return CComplex(c1.real - c2.real, c1.imag - c2.imag); } CComplex& CComplex::operator ++() { real = (long)(real) + 1; // 先进行增量运算; imag = (long)(imag) + 1; // 有风险; return *this; // 返回原来的对象(已经完成增量运算); } CComplex CComplex::operator ++(int) { CComplex temp(*this); // 先保存原有对象; real = (long)(real) + 1; // 再进行增量运算; imag = (long)(real) + 1; return temp; // 返回未进行增量运算的对象; } void CComplex::ShowComplex(CComplex c) { cout << "(" << c.real << ")" << "+" << "(" << c.imag << "i" << ")" << endl; }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:程序主文件。 */ #include "CComplex.h" int main() { CComplex a(1, 2), b(3, 4); CComplex::ShowComplex(a++); CComplex::ShowComplex(a); CComplex::ShowComplex(++b); return 0; }
-
-
增量运算实例 ( O v e r l o a d 4 ) ({\rm Overload4}) (Overload4)项目:
-
项目需求:用友元函数实现增量运算符重载。
-
复数类定义头文件代码 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CComplex { private: double real; // 复数的实部; double imag; // 复数的虚部; public: CComplex(double pr = 0.0, double pi = 0.0) { real = pr; imag = pi; }; virtual ~CComplex(); CComplex operator +(CComplex c); CComplex operator -(CComplex c); friend CComplex& operator ++(CComplex&); // 前增量; friend CComplex operator ++(CComplex&, int); // 后增量; static void ShowComplex(CComplex c); };
-
复数类实现文件代码 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:复数类实现文件。 */ #include "CComplex.h" CComplex::~CComplex() { } // 通过友元实现前增量重载; CComplex& operator ++(CComplex& c) { c.real = (long)(c.real) + 1; c.imag = (long)(c.imag) + 1; return c; } // 通过友元实现后增量重载; CComplex operator ++(CComplex& c, int) { CComplex temp(c); c.real = (long)(c.real) + 1; c.imag = (long)(c.imag) + 1; return temp; } void CComplex::ShowComplex(CComplex c) { cout << "(" << c.real << ")" << "+" << "(" << c.imag << "i" << ")" << endl; }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/15 * 描述:程序主文件。 */ #include "CComplex.h" int main() { CComplex a(1, 2), b(3, 4); CComplex::ShowComplex(a++); CComplex::ShowComplex(a); CComplex::ShowComplex(++b); return 0; }
-
-
转换运算符,即强制类型转换运算符,可以把一种类的对象转换为其他类的对象或内部类型的对象,这种运算符必须是一个非 s t a t i c {\rm static} static成员函数,且不能是友元函数;
-
转换运算符声明的语法格式为:
c++operator 类型名();
-
转换运算符实例 ( O v e r l o a d 5 ) ({\rm Overload5}) (Overload5)项目:
-
项目需求:重载强制转换运算符实现将复数转换为实数,公式: 实数 = 复数实 部 2 + 复数虚 部 2 实数=\sqrt{复数实部^2+复数虚部^2} 实数=复数实部2+复数虚部2 ;
-
复数类定义头文件代码 ( C C o m p l e x . h ) ({\rm CComplex.h}) (CComplex.h):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:复数类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CComplex { private: double real; double imag; public: CComplex(double pr=0.0,double pi=0.0){real=pr;imag=pi;}; virtual ~CComplex(); static void ShowComplex(CComplex c); operator double(); // 强制转换运算符重载; };
-
复数类实现文件代码 ( C C o m p l e x . c p p ) ({\rm CComplex.cpp}) (CComplex.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:复数类实现文件。 */ #include "CComplex.h" #include <math.h> CComplex::~CComplex() { } void CComplex::ShowComplex(CComplex c) { cout << "(" << c.real << "," << c.imag << "i" << ")" << endl; } // 强制转换运算符重载实现; CComplex::operator double() { return sqrt(real * real + imag * imag); }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:程序主文件。 */ #include "CComplex.h" int main() { CComplex a(1, 2), b(3, 4); double c = (double)a; // 显式转换; cout << "c = " << c << endl; c = a + b; // 隐式转换; cout << "c = " << c << endl; return 0; }
-
-
对于强制转换运算符的重载,需要注意转换二义性的问题,即同一类型提供了多个转换路径,会导致编译出错;
-
赋值运算符重载实例:
-
项目需求:定义一个描述手机的短信息类,重载赋值运算符。
-
核心代码:
c++class CMsg { private: char *buffer; public: CMsg(){buffer = new char('\0');}; ~CMsg(){delete[] buffer;}; void display(){ cout << buffer << '\n'; }; void set(char *string){ delete[] buffer; buffer = new char[strlen(string) + 1]; strcpy(buffer, string); }; operator = (const CMsg& msg){ delete[] buffer; buffer = new char[strlen(msg.buffer) + 1]; strcpy(buffer, msg.buffer); }; };
-
7.3 项目实战
项目需求 :自定义一个字符类,进行下标运算符重载。
代码实现:
-
字符类定义头文件代码 ( C C h a r A r r a y . h ) ({\rm CCharArray.h}) (CCharArray.h):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:字符类定义头文件。 */ #include <iostream> #include <string> using namespace std; class CCharArray { private: int Length; // 字符串长度; char *Buff; // 字符串指针; public: CCharArray(int l) { // 构造函数; Length = l; Buff = new char[Length]; // 分配字符存储空间; }; ~CCharArray() { // 析构函数; delete Buff; }; int GetLength() { return Length; // 取得字符长度; }; char& operator[](int i); // 重载数组下标运算符; };
-
字符类实现文件代码 ( C C h a r A r r a y . c p p ) ({\rm CCharArray.cpp}) (CCharArray.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:字符类实现文件。 */ #include "CCharArray.h" char& CCharArray::operator[](int i) { static char ch = 0; if (i < Length && i >= 0) { return Buff[i]; } else { cout << "访问溢出."; return ch; } }
-
程序主文件代码 ( m a i n . c p p ) ({\rm main.cpp}) (main.cpp):
c++/** * 作者:罗思维 * 时间:2024/03/16 * 描述:程序主文件。 */ #include "CCharArray.h" int main() { int cnt; CCharArray string1(6); char *string2 = (char*)"string"; for (cnt = 0; cnt < 6; cnt++) { string1[cnt] = string2[cnt]; // 将string2的字符逐个复制到string1中; } for (cnt = 0; cnt < 8; cnt++) { cout << string1[cnt]; // 当cnt为7时,访问出现溢出; } cout << endl; cout << string1.GetLength() << endl; return 0; }