海外免费网站推广,临沂企业网站建站模板,遵义网站制作的网站,天津企业网站建设一般多少钱运算符重载的基本概念
与函数重载相似#xff0c;运算符也存在重载问题。C为了解决一些实际问题#xff0c;允许重载现有的大多数运算符#xff0c;即允许给已有的运算符赋予新的含义#xff0c;从而提高C的可扩展性#xff0c;针对同样的操作#xff0c;使用重载运算符…运算符重载的基本概念
与函数重载相似运算符也存在重载问题。C为了解决一些实际问题允许重载现有的大多数运算符即允许给已有的运算符赋予新的含义从而提高C的可扩展性针对同样的操作使用重载运算符比函数的显示调用更能提高程序的可读性。 C对运算符重载进行了一下规定限制
只能重载C中原先已定义的运算符。不能自己“创造”新的运算符进行重载。并不是所有的运算符都可以进行重载。不能进行重载的运算符有“·”“.*”“::”“?:”。不能改变运算符原有的优先级和结合性。C已预先规定了每个运算符的优先级和结合性已决定运算次序。不能改变其运算次序若确实需要改变只能采用加“()”的方法。不能改变运算符对预定义类型数据的操作方式但是可以根据实际需要对原有运算符进行是当地改造与扩充。运算符重载有两种方式即重载为类的成员函数和重载为类的友元函数。
成员函数重载运算符
C允许重载大多数已经定义的运算符。运算符重载函数既可以是类的成员函数也可以是类的友元函数。 在类的内部定义成员函数重载运算符的格式为class 类名{返回类型 operator 运算符(形参表)}; 在类的外部定义成员函数重载运算符的格式为返回类型 类名::operator 运算符(形参表){//函数体} 说明返回类型是指运算符重载函数的运算结果类型operator是定义运算符重载的关键字运算符是要重载的运算符名称形参表中给出了重载运算符所需要的参数和类型。
重载单目运算符
用成员函数重载运算符时若运算符时单目的则参数表为空因为这是操作数访问该重载运算符对象本身的数据该对象有this指针指向所以参数表为空。 单目运算符的调用有两种即显式调用和隐式调用。 显示调用对象名.operator 运算符() 隐式调用重载的运算符 对象名 例如重载“-”负号运算符。
#include iostream
using namespace std;
class Point
{
public:Point(int i 0, int j 0) :x(i), y(j) {};Point operator -() {x -x;y -y;return *this;}void Print();
private:int x, y;
};int main() {Point ob(1, 2);cout ob:;ob.Print();cout endl;cout -ob:;ob ob.operator-(); //显式调用//ob -ob; //隐式调用ob.Print();cout endl;return 0;
}
void Point::Print()
{cout x y endl;
}重载双目运算符
重载双目运算符时左边操作数是访问改重载运算符对象本身的数据有this指针指向右边操作数通过重载运算符的成员函数的参数指出所以成员函数只有一个函数。 双目运算符的调用有两种方式即显式调用和隐式调用。 显式调用对象名.operator 运算符(参数) 隐式调用对象名 重载的运算符 参数 例如重载“”运算符以实现连个字符串相加。
#include iostream
#include string.h
using namespace std;
const int MAX 20;
class String
{
public:String(const char *instr NULL);String operator(const char *astr);void ShowString();private:char buffer[MAX];int length;
};int main()
{String title(C/C);//定义对象时,调用构造函数给成员buffer赋值title title Program;//隐式调用title title.operator(!);//显式调用title.ShowString();return 0;
}String::String(const char* instr)
{if (instr ! NULL){strcpy_s(buffer, strlen(instr) 1, instr);length strlen(buffer);}
}String String::operator(const char* astr)
{String temp;int templen;templen strlen(buffer) strlen(astr);if (templen 1 MAX){cout String is too large! endl;strcpy_s(temp.buffer, strlen(buffer) 1, buffer);//相当于strcpy_s(temp.buffer, strlen(this-buffer) 1, this-buffer);return temp;}length templen;strcpy_s(temp.buffer, strlen(buffer) 1, buffer);strcat_s(temp.buffer, templen 1, astr);return temp;
}void String::ShowString()
{cout buffer endl;
}例如用成员函数重载运算符实现复数的加、减运算。
#include iostream
using namespace std;class Complex
{
public:Complex(double r 0.0, double i 0.0) :real(r), imag(i) {};void Print();Complex operator(Complex c);Complex operator-(Complex c);
private:double real;double imag;
};int main()
{Complex com1(1.1, 2.2), com2(3.3, 4.4), total;total com1 com2;total.Print();total com1 - com2;total.Print();return 0;
}void Complex::Print()
{cout real;if (imag 0) cout ;if (imag ! 0) cout imag i endl;
}Complex Complex::operator(Complex c)
{Complex temp;temp.real real c.real;temp.imag imag c.imag;return temp;
}Complex Complex::operator-(Complex c)
{Complex temp;temp.real real - c.real;temp.imag imag - c.imag;return temp;
}重载、- -运算符
在C中经常使用前缀和后缀的、- -运算符若将(或- -)运算符置于变量前则C在引用变量前先加1或先减1若将(或- -)运算符置于变量后则C在引用变量后使变量加1或使变量减1。使用格式 类名 operator () //前缀方式 类名 operator (int)//后缀方式 类名 operator --()//前缀方式 类名 operator --(int)//后缀方式 例如为类Point重载运算符
#include iostream
using namespace std;
class Point
{
public:Point(int i 0, int j 0) :x(i), y(j) {};Point operator();Point operator(int);void Print();
private:int x, y;
};int main()
{Point ob1(1, 2), ob;ob1.Print();ob ob1;ob.Print();ob ob1;ob.Print();ob1.Print();return 0;
}Point Point::operator()
{x;y;return *this;
}Point Point::operator(int)
{Point temp *this;//确保原对象值x;y;return temp;
}void Point::Print()
{cout ( x , y ) endl;
}重载赋值运算符
在C中对于任何一个类若没有用户自定义的赋值运算符函数系统就会自动为其生成一个默认的赋值运算符函数以完成数据成员之间的逐位复制。通常默认的赋值运算符函数可以完成赋值任务但在某些特殊情况下若类中有指针类形式就不能进行直接的相互赋值。 例如指针悬挂问题。
#include iostream
#include string.h
#include iomanip
using namespace std;
class Student
{
public:Student(const char *na, int sco);Student operator(const Student);~Student();void Print();
private:char* name;int score;
};Student::Student(const char* na, int sco)
{name new char[strlen(na) 1];strcpy_s(name, strlen(na) 1, na);score sco;
}Student Student::operator(const Student p)//定义赋值运算符重载函数
{if (this p) return *this;//避免pp的赋值delete[]name;//释放空间name new char[strlen(p.name) 1];//分配新空间strcpy_s(name, strlen(p.name) 1, p.name);//字符串拷贝score p.score;return *this;
}Student::~Student()
{delete[]name;
}
void Student::Print()
{cout name setw(6) score endl;
}
int main()
{Student s1(李明, 60);Student s2(李华, 80);s2.Print();s2 s1;s2.Print();return 0;
}注意1.赋值运算符不能重载为友元函数只能重载为一个非静态成员函数2.赋值运算符重载函数不能被继承。
重载下标运算符
当程序变得复杂时有时需要必须重载数组下标运算符[]。C重载数组运算符时认为其是双目运算符因此重载数组下标运算符时运算符成员函数的格式为返回类型 类名::operator[](形参){//函数体} 例如重载下标运算符应用用一维数组实现一个三维向量类。
#include iostream
#include string.h
#include iomanip
using namespace std;
class Vector
{
public:Vector(int a1,int a2,int a3);int operator[](int b);
private:int v[3];
};Vector::Vector(int a1, int a2, int a3)
{v[0] a1;v[1] a2;v[2] a3;
}int Vector::operator[](int b)
{if (b 0 || b 3){cout Bad subscript! endl;exit(1);}return v[b];
}int main()
{Vector v(1, 3, 5);cout 修改前;for (int i 0; i 3; i){cout v[i] setw(4);}cout endl;cout 修改后;for (int i 0; i 3; i){v[i] 2 * i;}for (int i 0; i 3; i){cout v[i] setw(4);}cout endl;return 0;
}注意1.重载下标运算符的优点是可以增加C中数组检索的安全性2.重载下标运算符时返回一个int的引用。可使重载的[]运算符用在赋值语句的左边因而在main()中v[i]可以出现在赋值运算符的任何一边使编制程序更灵活了。
重载函数调用运算符
重载函数调用运算符()时并不是创建新的调用函数的方法而是创建了可传递任意数目参数的运算符函数。通常重载函数调用运算符时定义了传递给重载函数的参数。重载函数调用运算符成员函数的格式为返回类型 类名::operator()(形参){//函数体} 例如重载函数调用运算符
#include iostream
using namespace std;
class Matrix
{
public:Matrix(int, int);int operator()(int, int);
private:int* m;int row, col;
};Matrix::Matrix(int r, int c)
{row r;col c;m new int[row * col];for (int i 0; i row * col; i)*(m i) i;
}int Matrix::operator()(int r, int c)
{return (*(m r * col c));
}int main()
{Matrix m(10, 10);cout m(3, 4) endl;m(3, 4) 35;cout m(3, 4) endl;return 0;
}说明程序中m(10,10)相当于一个10行10列的二维矩阵。在执行语句cout (3,4) endl;时编译器将m(3,4)解释为m.operator()(3,4)从而调用运算符重载函数operator()(int r,int c)然后返回矩阵第3行第4列的元素值语句m(3,4)35修改矩阵第3行第4列的元素值之所以能够这样写是因为operator()是一个返回引用类型int。
友元函数重载运算符
在大多数情况下用友元函数或成员函数重载运算符在功能上没有差别。用友元函数重载运算符时因为友元函数没有this指针所以若运算符是单目的则参数表中有一个操作数若运算符是双目的则参数表中有两个操作数。友元函数重载运算符的格式为 friend函数类型operator重载的运算符(形参){}//单目运算符重载 friend函数类型operator重载的运算符(形参1, 形参2){}//双目运算符重载 例如用友元函数重载运算符实现复数的加、减运算。
#include iostream
using namespace std;class Complex
{
public:Complex(double r 0.0, double i 0.0) :real(r), imag(i) {};void Print();friend Complex operator(Complex a, Complex b);friend Complex operator-(Complex a, Complex b);
private:double real;double imag;
};int main()
{Complex com1(1.1, 2.2), com2(3.3, 4.4), total;total com1 com2;total.Print();total com1 - com2;total.Print();return 0;
}void Complex::Print()
{cout real;if (imag 0) cout ;if (imag ! 0) cout imag i endl;
}Complex operator(Complex a, Complex b)
{Complex temp;temp.real a.real b.real;temp.imag a.imag b.imag;return temp;
}Complex operator-(Complex a, Complex b)
{Complex temp;temp.real a.real - b.real;temp.imag a.imag - b.imag;return temp;
}成员函数与友元函数重载运算符的比较
在进行运算符重载时既可以用成员函数重载也可以用友元函数重载。下面是它们的比较。
对于双目运算符用成员函数重载时参数表中有一个参数而用友元函数重载时参数表中有两个参数对于单目运算符用成员函数重载时参数表没有参数而用友元函数重载时参数表中有一个参数。双目运算符一般可以用友元函数重载或用成员函数重载下面的情况必须使用友元函数重载。 例如用成员函数重载“运算符Complex Complex::operator(int c)
{Complex temp;temp.real real c;temp.imag imag c;return temp;
}若类Complex的对象com要做赋值运算和加法运算则下面的语句是正确的comcom10;这是因为对象com是“”运算符的左操作数在调用重载“”运算符的函数时this指针指向com。因此语句temp.realreala相当于remp.realthis-reala;而下面的语句就不正确了com10com;这是因为左操作数是一个整数而整数是一个内部数据类型不能产生对成员运算符函数的调用。 解决这类问题的方法采用两个友元函数来重载“”运算符从而消除由于“”运算符的左操作数为内部数据类型带来的问题。 C中的不部分运算符既可以用成员函数重载也可以用友元函数重载。在选择时主要取决于实际情况和使用者的习惯。运算符重载规则如下 1.对于双目运算符用友元函数重载比用成员函数重载更便于使用。若一个运算符的操作需要修改类对象的状态建议使用成员函数重载若运算符所需的操作数尤其是第一个操作数虚妄有隐式类型转换则运算符必须用友元函数重载。 2.对于赋值“” 、函数调用“()”、下标“[]”、指针成员引用“-”等运算符必须用成员函数重载否则将导致编译错误。 3.对于流插入“”和流提取“”运算符必须用友元函数重载。 4.对于单目运算符如取负“-”、指针“*”、取地址“”自增“”、自减“--”等运算符建议选择成员函数重载。 5.对于复合赋值运算符、-、/、*、、!、~、%、、建议用成员函数重载。 6.对于算术运算符、关系运算符、逻辑运算符、位运算符等建议用友元函数重载。
类型转换
系统预定义类型之间的转换
C规定当不同类型的数据进行运算时需先将数据转换成同一类型然后才可以进行运算。数据的类型转换可以通过两种转换形式完成一种是隐式类型转换另一种是显式类型转换。
隐式类型转换 当执行赋值表达式VE时若V和E的类型不一致则将E先转换为V的类型后再赋值。 与C语言一样C中规定数据类型级别从高到低的次序是double -float-long-int-short/char。 当两个操作数类型不一致时运算之前将级别低的自动转换为级别高的然后再进行运算。显式类型转换 显示类型转换有以下两种方式。1. 强制转换法(类型名)表达式2.函数法类型名(表达式)。 以上的种种是一般数据类型之间的转换那该如何实现用户自定义类型与其他数据类型的转换呢通常采用两种方法用构造函数实现类型转换和用运算符转换函数实现类型转换。
用构造函数实现类型转换
用构造函数实现类型转换类内至少定义一个只带一个参数没有其他参数或其他参数都是默认值的构造函数。这样当进行类型转换时系统会自动调用该构造函数创建该类的一个临时对象该对象由被转换的值初始化从而让实现类型转换。 例如将一个char型数据转换为String类型的数据
#include iostream
#include string.h
using namespace std;
class String
{
public:String(const char* ch NULL);~String();void Print();
private:char* str;int length;
};String::String(const char* ch)
{if (ch ! NULL){length strlen(ch);str new char[strlen(ch) 1];strcpy_s(str, strlen(ch) 1, ch);}
}String::~String()
{delete[]str;
}void String::Print()
{cout str endl;
}int main()
{String s C/C program!;s.Print();return 0;
}说明语句“String(const char* ch NULL);”声明了一个转换构造函数。该构造函数可以用来进行类型转换。主函数中再执行语句“String s C/C program!;”时编译器首先调用构造函数建立包含“C/C program!”的一个临时String类的对象通过转换构造函数将一个char*字符串转换为String的对象然后再将该临时String类的对象赋给对象s。使用这种转换构造函数意味着不用再为字符串赋给String类对象提供重载的赋值运算符。任何只带一个参数或其他参数都带有默认值的构造函数都可以认为是一种转换构造函数。
用运算符转换函数实现类型转换
用构造函数可以实现类型转换但是其所完成的类型转换功能具有一定的局限性。由于无法为系统预定义类型定义构造函数因此不能利用构造函数把自定义类型的数据转换为系统预定义类型的数据只能实现系统预定义类型向自定义类型的类型转换。 为了解决上述问题C允许用户在类中定义成员函数从而得到要转换的类型。 运算符转换函数定义格式为
class 类名
{operator目的类型(){return 目的类型的数据;}
};其中目的类型为要转换成的类型它既可以是用户自定义的类型也可以是系统的预定义类型。 在使用运算符转换函数时需要注意以下三个问题
运算符转换函数只能定义为一个类的成员函数而不能定义为类的额友元函数运算符转换函数既没有参数也不显式地给出返回类型运算符转换函数中必须有“return 目的类型的数据;”这样的语句即必须返回目的类型数据作为函数的返回值。 例如自定义类型向预定义类型的转换。
#include iostream
using namespace std;
class Complex
{
public:Complex(double r 0.0, double i 0.0) :real(r), imag(i) {};operator float();operator int();void Print();
private:double real;double imag;
};int main()
{Complex com1(2.2, 4.4);cout com1;com1.Print();cout Type changed to float... endl;cout float(com1)*0.5;cout float(com1) * 0.5 endl;Complex com2(4.7, 6);cout com2;com2.Print();cout Type changed to int... endl;cout int(com2)*2;cout int(com2) * 2 endl;return 0;
}Complex::operator float()
{return real;
}Complex::operator int()
{return int(real);
}void Complex::Print()
{cout real;if (imag 0) cout ;if (imag ! 0) cout imag i endl;
}