操作符重载
来源:互联网 发布:手机mac过滤怎么设置 编辑:程序博客网 时间:2024/06/11 15:31
1.c++中运算符可以被重载在c++中,几乎所有的运算符都可以被重载。2.不可以被重载的运算符有6个运算符是不能被重载的,它们分别是。::? :..*sizeof###3. 重载的格式返回值 operator被重载的符号(参数){ 重载的内容;} (1)如果重载是字母字符,例如new delete,那么operator与字幕字符之间至少留 一个空格,其它的可以留也可以不留。(2)运算符被从重载后,原有的功能依然存在。(3)运算符重载不是发明新的运算符,也不能修改运算符的优先级和操作数的个数。4. 重载举例(1)对<进行重载举例#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Student{ string name; int age;public: Student(const string name=" ", int age=0):name(name), age(age) {} bool operator<(const Student &stu) { return (this->age < stu.get_age()); } int get_age() const { return age; }}; int main(void){ Student stu1("zhangsan", 24); Student stu2("wangwu", 20); if(stu1 < stu2) { cout << "zhangsan' age < wangwu'age" << endl; } else { cout << "zhangsan' age > wangwu'age" << endl; } return 0;}运行结果:zhangsan' age > wangwu'age 例子分析:在本例中,对<进行了重载,所谓重载就是写一个重载函数,对符号的含义进行重新定义,比如这个例子中按照年龄进行比较对象的大小,如果没有对<进行重载的话,由于<默认的含义是只能对基本类型的数据进行比较,那么比较的代码就应该这样写, if(stu1.get_age() < stu2.get_age()) { cout << "zhangsan' age < wangwu'age" << endl; } else { cout << "zhangsan' age > wangwu'age" << endl; }显然可以很明显的看出这样写很繁琐,如果对象的成员要是在复杂点的话,这样写很不简洁,如果要是能够直接写成stu1 < stu2 不就很直观吗,但问题是<只能对基本类型的数据进行比较,不能直接对对象进行比较,因此我们就需要重载<符号,当我们按照stu1<stu2进行比较时,它们能够自动的是使用年龄进行比较。当<被重载后,执行stu1<stu2时,其实际的含义就是调用stu1的<重载函数,并将stu2作为参数传递过去,也就是说stu1<stu2该成下面的调用形式完全是可以的。 stu1.operator<(stu2);需要注意一点的是,由于重载函数中的参数是const的,当该参数调用get_age()函数时,get_age()函数必须是const的。(2)重载时需要注意的一些问题 (1)为了提高传参的效率,尽量传递引用或者指针(2)使用引用或者指针时,如果不用修改内容的话,尽量使用const修饰(3)尽量将运算符的重载写成内联函数形式,因为内联函数的效率会搞很多,所以很多时候 都是将重载函数直接写在类里面,编译器会自动对齐进行甄别,需不需要编译成为内 联函数。(4)除非该类是某个友元类的私有类,可以将其设置为private,否者一般情况下,我们都将 该类的重载函数需要声明为public。(3)例子2接着上面的例子,定义一个学生对象数组,找出年龄最大的学生。类的定义保持不变。主函数内容改为如下内容。int main(void){ Student *stu[10]; for(int i=0; i<sizeof(stu)/sizeof(Student *); i++) { char I = i+'0'; stu[i] = new Student(name+I, i+20); } int max = 0; for(int i=0; i<sizeof(stu)/sizeof(Student *); i++) { if(*stu[max] < *stu[i]) { max = i; } } cout << stu[max]->get_name() << " "; cout << stu[max]->get_age() << endl; return 0;} 5. 全局重载函数 前面的例子中,<的重载函数是Student的成员函数,但是实际上我们完全可以将重载 函数定义为全局重载函数,只是相对于成员函数来说, (1)对于二元操作符来说,全局的重载函数需要传递两个参数 (2)如果希望编译为内联函数,必须显式的声明为inline,否者编译器只会将其编 译为普通函数。从前面知道,成员函数是不需要显式写出的,当然显示写出 也不会错。 如果将前面的<的重载函数重载为全局函数的格式如下, bool operator<(const Student &stu1, const Student &stu2) { return (stu1.get_age() < stu2.get_age()); } 调用的时候,stu1<stu2与operator<(*stu[max], *stu[i])是完全等价的。 那么什么时候会用到全局的符号重载呢?这个在后面将会讲到。 6.重载运算符全部功能 (1)什么是运算符的全功能 以上面重载<运算符为例来说,其完整的功能应该有如下: (1)可以对基本类型数值进行比较:12.5 < 32,这个是<原本就有的功能,无需重载 (2)左操作数和右操作数都是对象:stu1 < stu2,这个需要重载是实现 (3)左操作数为对象,右操作数为数值:stu < 34.0,这个需要重载是实现 (4)左操作数为数值,右操作数为对象:43 < stu,这个也需要重载实现(2)重载时选择成员函数实现呢?还是选择外部函数实现呢?对于左操作数为对象时,选择成员函数或者外函数实现都可以,之所以可以选择成员函数,是因为左操作数为对象时,可以直接调用成员函数。以stu < 34.0为例来说,等价于stu.operator<(34.0);反过来,如果左操作数是数值右操作数是对象时,必须使用外部函数重载,43 < stu,如果将其实现为成员函数重载时,就等价于43.operator<(stu),这样的写法完全错误的。总结起来就是,如果左操作数是对象,重载函数可以是成员函数,也可以是外部函数,但是如果左操作数是数值,如果需要重载的话,必须使用外部函数进行重载。(3)<全功重载例子#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Student{ string name; int age;public: Student(const string name=" ", int age=0):name(name), age(age) { } int get_age() const { return age; } bool operator<(const Student &stu) { return (this->age < stu.get_age()); } bool operator<(double age) { return (this->age < age); } string get_name() const { return name; }}; bool operator<(double age, const Student &stu) { return (age < stu.get_age());}int main(void){ Student stu1("aaa", 20); Student stu2("bbb", 15); if(stu1 < stu2) { printf("stu1 < stu2\n"); } else { printf("stu1 > stu2\n"); } if(stu1 < 34) { printf("stu1 < 34\n"); } else { printf("stu1 > 34\n"); } if(25 < stu2) { printf("25 < stu2\n"); } else { printf("25 > stu2\n"); } return 0;}例子分析:在本例子中实现了<号的全部比较功能。7. 重载小总结(1)二元操作数的重载(1)如果左操作数是对象,使用成员函数或者全局函数进行重载都可以 (1)如果成员函数是对二元操作符进行的重载,只需要一个传参,这个传参 就是二元操作符的右操作数(2)如果左操作数不是对象而需要被重载的话,只能使用全局函数重载 (1)成员函数对二元操作符进行的重载时,必须传递两个参数,即运算符的左 操作数和右操作数。(3)二元操作数的重载函数的返回值 (1)如果是关系运算或者逻辑运算的话,返回值为bool型 (2)如果是算数运算,比如*和+时候,返回值视具体情况而定 (2)一元操作符的重载(1)一元操作符只有一个操作数(2)如果操作的数值是基本类型没有必要重载,因为一元操作符本来就支持 基本类型数据的操作。(3)如果操作数是对象时,使用成员函数和全局函数重载都可以。 (1)使用成员函数重载时,除了++和--外,都不需要传递参数 (2)如果是全局函数重载时,将一元操作符的操作数传递过去 (3)返回值的类型视情况而定8. 赋值运算符=的重载说到赋值运算符,实际上除了=之外,还有+=/*=/-=等等,这些都可以被重载,这里就以=为例讲解赋值运算符在重载的过程中遇到的一些问题。=实际上有两个方面的功能,一个是初始化,一个是赋值,比如: Student stu1; Student stu2 = stu1;//等价于Student stu2(stu1);这就是初始化。 Student stu1; Student stu2; stu2 = stu1;这就是赋值。这里讲的对=的重载,指的是对=的赋值功能的重载。(1)默认=重载函数如果我们自己不给类重载一个赋值运算符的话,编译器会自动的提供一个默认的operator=()。编译器提供的默认的=重载运算符对于一些普通的对象赋值来说是没有问题的,比如还是以Student类为例。例子: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <string> #include <cassert> #include <errno.h> using namespace std; class Student { public: string name; int age; Student(const string name=" ", int age=0):name(name), age(age) {} }; int main(void) { Student stu1("aaa", 20); Student stu2; stu2 = stu1; cout << "stu1的信息"<<stu1.name <<" "<<stu1.age<<endl; cout << "stu1的信息"<<stu2.name <<" "<<stu2.age<<endl; cout<<"\n修改了stu2的信息后"<<endl; stu2.name="www"; stu2.age = 25; cout << "stu1的信息"<<stu1.name <<" "<<stu1.age<<endl; cout << "stu1的信息"<<stu2.name <<" "<<stu2.age<<endl; return 0; } 运行结果: stu1的信息aaa 20 stu1的信息aaa 20 修改了stu2的信息后 stu1的信息aaa 20 stu1的信息www 25 例子分析: 例子中,将stu1的内容通过=赋值给stu2,在没有修改stu2之前,stu1和stu2 的内容是一致的,当stu2的内容修改后,它们的信息不同了,说明stu2空间 与stu1是完全分离的,说明这个默认的=的重载函数起作用了。 如果将这个默认的=的重载函数显式的写出的话,应该是这样的。 Student &operator=(const Student &stu) { this->name = stu.name; this->age = stu.age; return *this; } 当然打印的结果是一致的。(2)编译器提供的默认的=重载函数存在的问题(1)=重载函数存在的问题 实际上默认的=重载函数是存在很大问题的,那就是如果类中包含动态分配 内存的成员的话,对于这样的成员=复制的只是指向动态内存的地址,这会 导致两个对象的动态内存成员空间是共享的,这其实就是浅复制和深复制的 问题。(2)副本构造函数与=重载函数区别 上面提到=重载函数的问题时,估计都会联想到副本构造函数,因为副本构造 函数在面对动态分配内存的成员时,会出现深拷贝和浅拷贝的问题,这一点 和=重载函数的浅复制和深赋值的问题的根本原理的是一样的。 但是副本构造函数与=重载函数总是有区别的吧,那么它们的区别是什么。 如果我们不弄清它们的区别,我们非常容易将他们进行混淆。 (1)副本构造函数会被调用的情况 (1)使用一个对象直接去初始化另一个对象时,例如: Student stu1("aaa", 20); Student stu2(stu1); 或者 Student stu2 = stu1; 以上两种情况是等价的。 (2)传参时,间接实现一个对象直接去初始化另一个对象时 void show(const Student stu) { ...... } Student stu("aaa", 20); show(stu) 注意:如果函数直接传递的是引用的话,是不会掉用副本 构造函数的,当然=重载函数更不会调用了。 (2)=重载函数被调用的情况 Student stu1("aaa", 20); Student stu2; stu2 = stu1; 因为stu2 = stu1;执行的是赋值操作,因此会调用=重载函数。(2)例子 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <string> #include <cassert> #include <errno.h> using namespace std; class Student { public: string name; int age; Student(const string name=" ", int age=0):name(name), age(age) {} Student &operator=(const Student &stu) { printf("=重载函数\n"); this->name = stu.name; this->age = stu.age; return *this; } Student(Student &stu) { printf("副本构造函数\n"); this->name = stu.name; this->age = stu.age; } }; void show(const Student stu) { } int main(void) { Student stu1("aaa", 20); Student stu2=stu1;//调用副本构造函数 cout<<endl; show(stu2);//调用副本构造函数 cout<<endl; stu2 = stu1;//调用=重载函数 return 0; }(3)一种特殊情举例 任然上面的例子为例,但是将=重载函数的参数从引用改为对象值传递: Student &operator=(const Student stu) { 内容不变 } main函数的内容改为: int main(void) { Student stu1("aaa", 20); Student stu2; stu2 = stu1; return 0; } 运行结果: 副本构造函数 =重载函数 例子分析: 这个例子的打印结果很是让人迷惑,因为main函数的如下语句, Student stu1("aaa", 20); Student stu2; stu2 = stu1; 似乎很明显,只是进行了赋值操作,但是为什么即调用了副本构造函数,也调用 了=重载函数呢? 其实原因处在=重载函数的形参写法上, Student &operator=(const Student stu) { ...... } 注意,形参写的不是引用,所以当使用=进行复制操作而调用=重载函数时,首先 会调用副本构造函数使用实参初始化形参stu,因此这里即调用了副本构造函数 也调用了=的重载函数,这样的结果一点也不奇怪。(3)显式的写=重载函数,实现深复制前面说过,如果类中包含动态分配的成员时,默认的=重载函数已经不够用了,这个时候我们需要自己重载=,还是以Student类为例,给它定义一个需要动态分配的Birthday成员。例子: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <string> #include <cassert> #include <errno.h> using namespace std; class Birthday { public: string year; string month; string day; Birthday(const string year="0", const string month="0", const string day="0") :year(year), month(month), day(day) { } }; class Student { public: string name; Birthday *brhtday; Student(const string name=" ", const string year=" ", const string month=" ", const string day=" ") :name(name), brhtday(new Birthday(year, month, day)) {} }; int main(void) { Student stu1("zhangsan", "2003", "12", "1"); Student stu2; stu2 = stu1; cout<<"stu1的信息:"<<stu1.name<<" "<<stu1.brhtday->year<<" "<< stu1.brhtday->month<<" "<<stu1.brhtday->day<<" "<<endl; cout<<"stu2的信息:"<<stu2.name<<" "<<stu2.brhtday->year<<" " <<stu2.brhtday->month<<" "<<stu2.brhtday->day<<" "<<endl; cout<<"\n修改stu2的生日信息后"<<endl; stu1.brhtday->year="2009"; stu1.brhtday->month="9"; stu1.brhtday->day="2"; cout<<"stu1的信息:"<<stu1.name<<" "<<stu1.brhtday->year<<" "<< stu1.brhtday->month<<" "<<stu1.brhtday->day<<" "<<endl; cout<<"stu2的信息:"<<stu2.name<<" "<<stu2.brhtday->year<<" " <<stu2.brhtday->month<<" "<<stu2.brhtday->day<<" "<<endl; return 0; }运行结果: stu1的信息:zhangsan 2003 12 1 stu2的信息:zhangsan 2003 12 1 修改stu2的生日信息后 stu1的信息:zhangsan 2009 9 2 stu2的信息:zhangsan 2009 9 2 例子分析:从打印的结果可以看出,stu2的生日修改后,stu1的生日也修改了,原因是它们各自的brthday成员都指向了同一个动态分配空间,因为=进行复制时,只复制了地址,也就是说只进行了浅复制。如果要改进这个问题,我们只需要添加自定义=的重载函数,其实现如下: Student &operator=(const Student &stu) { if(this == &stu) return *this; this->name = stu.name; this->brhtday = new Birthday(); this->brhtday->year = stu.brhtday->year; this->brhtday->month = stu.brhtday->month; this->brhtday->day = stu.brhtday->day; return *this; } 重新运行的结果: =重载函数 stu1的信息:zhangsan 2003 12 1 stu2的信息: 修改stu2的生日信息后 stu1的信息:zhangsan 2009 9 2 stu2的信息: 分析:显然,这个结果已经表明,自定义的=重载函数已经实现了深复制。之所以能够实现深拷贝的原因是因为,在=重载函数中,对this指向的对象中的brthday成员开辟了新的空间,然后再将被赋值对象的birthday指向空间的内容复制过来,如此便实现了深拷贝。(4)自定义=重载函数两个疑惑(1)疑惑1: 为什么要在=重载函数中加如下这句话。 if(this == &stu) return *this; 这句话主要是为防止如下这样的赋值语句, stu1 = stu1; 这条赋值语句实际上没什么实际意义,但是它的确是能够通过编译,如果出现 这种情况时,显然只需要直接return *this;返回即可。(2)疑惑2: 为什么返回*this。 很多时候会出现连续赋值的情况,比如: stu3=stu2=stu1; 将这话改为它的等价形式: stu3.operator=(stu2.operator=(stu1)); 显然从上面连续赋值中看出,operator=()的返回值在这种情况下会成为其它 operator=()的参数,而operator=()参数要求是一个引用,因此需要返回*this。9. 算数运算符重载(1)+重载的例子#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Box{public: int length; int witdh; int height; Box(int length=1, int witdh=1, int height=1): length(length), witdh(witdh), height(height) { } Box& operator=(const Box &box) { if(this == &box) return *this; this->length = box.length; this->witdh = box.witdh; this->height = box.height; return *this; } Box operator+(const Box &box) const { Box tmpbox((this->length+box.length), (this->witdh+box.witdh), (this->height+box.height)); return tmpbox; } /*这么写是错误的 Box & operator+(const Box &box) const { Box tmpbox((this->length+box.length), (this->witdh+box.witdh), (this->height+box.height)); return tmpbox; } */ };void show(const Box &box) { cout<<"length:"<<box.length<<" witdh:"<<box.witdh<<" height:"<<box.height<<endl; cout<<"体积="<<box.length*box.witdh*box.height<<endl;}int main(void){ Box box1 = Box(12, 33, 45); Box box2 = Box(43, 23, 14); Box box3 = box1+box2; show(box3); return 0;}运行结果:length:55 witdh:56 height:59体积=181720例子分析:例子中,定义了两个盒子box1和box2,然后将两个盒子对象相加,因此需要重载+。(2)+号重载需要注意的地方上面重载+的例子中,可以发现,+的重载需要注意如下问题。(1)为了提高效率,最好传递引用,并指定为const(因为右操作数并不会被修改)(2)由于+的左操作数也不会被修改,因此我们将operator+()函数也定义为const(3)对于其返回值为返回动态空间或者返回引用的方式,都不是很可取。 返回动态分配空间的地址,不利于管理内存。而box因为是自动局部对象 又不能返回其引用,如果将+重载函数中的box定义为staitc的话,是可以返回 其引用的。(能不能返回局部的变量,完全看变量生存期,只要分析这个就可以。)(4)因为+运算符返回值涉及赋值操作,准确的说是初始化,用一个对象初始化另一个独象,所以 返回函数内的临时对象调用的是复制构造函数。并不是等号复制运算。(3)+=的重载如果重载了+=,并可以使用+=快速重载+。+=比较特殊,它含有算数运算符的功能,同时又含有赋值运算符的功能,这一点需要注意,正是由于它含有赋值运算符的功能,因此重载函数需要像重载"="时一样返回*this。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <string> #include <cassert> #include <errno.h> using namespace std; class Box { public: int length; int witdh; int height; Box(int length=1, int witdh=1, int height=1): length(length), witdh(witdh), height(height) { } Box &operator+=(const Box &box) { this->length += box.length; this->witdh += box.length; this->height += box.height; return *this; } Box operator+(const Box &box) { return Box(*this)+=box; } }; void show(const Box &box) { cout<<"length:"<<box.length<<" witdh:"<<box.witdh<<" height:"<<box.height<<endl; cout<<"体积="<<box.length*box.witdh*box.height<<endl; } int main(void) { Box box1 = Box(12, 33, 45); Box box2 = Box(43, 23, 14); Box box3(box2); Box box4=box1+=box2+=box3; Box box5 = box4+box1; show(box5); return 0; } 例子分析: 本例子中重载了+=,然后借助于+=的重载函数,快速实现了+的重载。 对于main函数中Box box4=box1+=box2+=box3;语句需要注意,第一个=不能 改为+=,因为第一个是初始化而不是赋值,换句话说Box box4=box1+=box2+=box3; 等价于如下形式: Box box4(box1+=box2+=box3); box4还没有初始化不能参加+=运算。
(4)-/-=,/=等算数符号的重载
这些符号的重载与+/+=完全类似,请自己完成。
10. 下标运算符的重载
(1)[]与左值和右值
上一章链表的例子:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Studata{public: int num; string name; Studata(int num=0, const string name=" "):num(num), name(name) {} int get_num() const{ return num; } string get_name() const{ return name; } void set_num(int num) { this->num = num; } void set_name(string name) { this->name = name; }};class Stunode{ Stunode *prev; Stunode *next; Studata *studata;public: Stunode(int num=0, const string name=" ", Stunode *prev=NULL, \ Stunode *next=NULL):studata(new Studata(num, name)) {} Stunode(const Studata &studata) { this->studata = new Studata(); this->studata->set_num(studata.get_num()); this->studata->set_name(studata.get_name()); prev = next = NULL; } ~Stunode() { delete studata; } /*获取器*/ Stunode *get_prev() const {return prev;} Stunode *get_next() const {return next;} Studata *get_studata() const {return studata;} /*设置器*/ void set_prev(Stunode *stunode) {this->prev = stunode;} void set_next(Stunode *stunode) {this->next = stunode;} void set_studata(Studata *studata) {this->studata = studata;}};class Dlist{ Stunode *hp; Stunode *current; Stunode *tmp;public: Dlist() { hp = current = new Stunode();//空头节点 hp->set_prev(hp); hp->set_prev(hp); tmp = NULL; } void err_fun(string filename, int line, string funname, int err_no) { cerr<<filename<<" "<<line<<" "<<funname<<" fail"<<":"<<strerror(err_no)<<endl; exit(-1); } void init_Dlist(string filename); void insert_tail(); void insert_head(); void display(); Studata &operator[](int i);};Studata &Dlist::operator[](int index){ if(index < 0) { cout<<"非法下标" << endl; return *(hp->get_studata()); } int i=0; for(current=hp->get_next(); ; current=current->get_next(), i++) { if(current == hp) { cout << "无此元素" << endl; return *(hp->get_studata()); } else if(i == index) return *(current->get_studata()); }}void Dlist::display(){ for(current=hp->get_next(); hp!=current; current=current->get_next()) { cout<<current->get_studata()->get_num()<<"\t"<<current->get_studata()->get_name()<<endl; }}void Dlist::insert_tail(){ hp->get_prev()->set_next(tmp); tmp->set_prev(hp->get_prev()); hp->set_prev(tmp); tmp->set_next(hp);}using namespace std;int main(void){ Studata data; Dlist dlist; dlist.init_Dlist("./stu.txt"); data = dlist[2]; cout << data.get_num()<<"\t"<<data.get_name() <<endl; Studata data1(12, "qqqq"); dlist[1] = data1; data = dlist[10]; cout << data.get_num()<<"\t"<<data.get_name() <<endl; return 0;}例子分析:为了让[]既能够做左值又能够做右值,需要将[]重载函数的返回值改为引用。[]重载函数具体实现如下: Studata &Dlist::operator[](int index) { if(index < 0) { cout<<"非法下标" << endl; return current->studatanul; } int i=0; for(current=hp->get_next(); ; current=current->get_next(), i++) { if(current == hp) { cout << "无此元素" << endl; return current->studatanul; } else if(i == index) return *(current->get_studata()); } }该重载函数先是判断下标的合法性,然后再去搜索链表,找到下标指定的链表节点,但是由于返回值是引用,引用的空间不能为空,但是当下标非法或者没有找到该下标对应的链表节点时,这个时候是没有有效节点的数据空间的,所以这个时候就将头节点无效数据空间返回。由于返回值是引用,因此在主函数中,[]既能作为左值也能作为右值。(2)const与[]重载函数有些时候我们是不允许对Studata的对象做修改的,但是有的时候又允许做修改,那什么时候会遇到这种情况呢?如果定义的是一个const的Dlist,那么就不允许[]重载函数作为左值,而且从前面的学习我们知道,const的对象只能调用const的函数。如果我们希望这两种情况都能存在,那怎么办呢?我们可以重载一个const的版本的[]重载函数,还是以上例为例,const版本的[]重载函数为: const Studata &Dlist::operator[](int index) const { if(index < 0) cout<<"非法下标" << endl; int i=0; for(current=hp->get_next(); ; current=current->get_next(), i++) { if(current == hp) { cout << "无此元素" << endl; return current->studatanul; } else if(i == index) return *(current->get_studata()); } }同时不要忘了在Dlist里面做如下声明, const Studata &operator[](int i) const;我们看,这个const版本的[]重载函数,返回值是const的引用,因此不能作为左值,同将函数也声明为了const。当我们使用的是const的Dlist类对象时,会自动调用const版本的[]重载函数,如果将其作为左值的话,将无法通过编译。11. 重载递增++递减--(1)++/--的两种情况 ++和--运算符的重载需要分为两种,一种是前++/--,一种是后++/--,针对 这两种不同的情况需要给出不同重载。 (1)前++/--含义 前++/--表示,先在给this对象做++/--运算,再将运算后的值返回,为了 提高效率往往返回的是引用。 (2)后++/--含义 后++/--表示,先返回一个this对象的副本,然后在对this对象做运算,为了 避免出现连续的+++++/----这种容易混淆的情况,我们要求都将++/--的重载 函数返回值进行const限制。(2)前++/--和后++/--的重载格式 这里只给出++的范例,--与之同。 (1)前++ const Object &operator++() { ...... } (2)后++ const Object operator++(int) { ...... } 注意:这里的int仅仅是为了区分这两个重载函数,没有具体含义,c++ 对于后++重载的格式就是这么规定的,如果经int写为其它类型是无 无法编译通过的。(3)例子 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <string> #include <cassert> #include <errno.h> using namespace std; class Box { public: int length; int witdh; int height; Box(int length=1, int witdh=1, int height=1): length(length), witdh(witdh), height(height) { } const Box &operator++() { ++this->length; ++this->witdh; ++this->height; return *this; } const Box operator++(int) { Box box(*this); this->length++; this->witdh++; this->height++; return box; } }; void show(const Box &box) { cout<<"length:"<<box.length<<" witdh:"<<box.witdh<<" height:"<<box.height<<endl; cout<<"体积="<<box.length*box.witdh*box.height<<endl; } int main(void) { Box box1 = Box(12, 33, 45); show(box1++); show(++box1); return 0; }12. 重载类型转换(1)类类型转换为其它类型我们知道正常情况下要将类类型转换为另一种类型是不行的,比如下面的例子无法通过编译,因为c++中不允许将类类型强制转换为其它类型。但是我们可以通过重载类型转换来实现这样的要求,当然另一种类型可以是基本类型,也可以是其它类类型。例子:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Box { public: int length; int witdh; int height; Box(int length=1, int witdh=1, int height=1) : length(length), witdh(witdh), height(height) { } }; int main(void){ Box box = Box(1, 1, 1); int vol = box; //或者int vol = static_cast<int> (box); cout<<"体积:"<< vol << endl; return 0;}例子分析:上面的例子是无法通过编译的,因为main函数中,试图将Box类型对象转换为int,默认是不支持的。(2)使用类型重载解决上例的问题(1)类型重载的格式 operator Type() { ...... } 格式中,没有表明返回值的类型,但是它默认的返回类型就是Type类型(2)在例子中的Box类型中添加int型类型重载函数 记得将函数放在public下面,具体函数如下: operator int() const { cout<< "类型重载函数" << endl; return (this->length*this->witdh*this->height); } 然后编译代码运行即可: 分析: 当执行int vol = box;时, operator int()函数就会被调用,就会将 计算得到的体积值返回给vol,当然 int vol = box; 也可以使用 int vol = static_cast<int> (box); 进行替换,这二者是等价的。12. 智能指针(*与->重载)智能指针并不是完全意义上的指针,它只是一个类对象,这个对象实现了对指针的封装,用于向前和向后索引(比如索引链表)。实现智能指针涉及到*和->的重载,迭代器就是智能指针,在实际开发中,我们很少会去自己去写智能指针,因为对于提高开发效率和代码运行效率的帮助并不是很大,为了使用效率,我们往往时直接使用c++提供的智能指针(迭代器)。但是我们需要了解到底什么是智能指针。智能指针主要用于遍历。例子:stulist.cpp#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <iostream>#include <string>#include <cassert>#include <errno.h>using namespace std;class Studata{public: int num; string name; Studata(int num=0, const string name=" "):num(num), name(name) {} int get_num() const{ return num; } string get_name() const{ return name; } void set_num(int num) { this->num = num; } void set_name(string name) { this->name = name; }};class Stunode{ Studata *studata; Stunode *prev; Stunode *next;public: Stunode(int num=0, const string name=" ", Stunode *prev=NULL, \ Stunode *next=NULL):studata(new Studata(num, name)) {} Stunode(const Studata &studata) { this->studata = new Studata(); this->studata->set_num(studata.get_num()); this->studata->set_name(studata.get_name()); prev = next = NULL; } ~Stunode() { delete studata; } /*获取器*/ Stunode *get_prev() const {return prev;} Stunode *get_next() const {return next;} Studata *get_studata() const {return studata;} /*设置器*/ void set_prev(Stunode *stunode) {this->prev = stunode;} void set_next(Stunode *stunode) {this->next = stunode;} void set_studata(Studata *studata) {this->studata = studata;}};class Dlist{ Stunode *hp; Stunode *current; Stunode *tmp;public: Dlist() { hp = current = new Stunode();//空头节点 bzero(hp, sizeof(Stunode)); hp->set_prev(hp); hp->set_prev(hp); tmp = NULL; } void err_fun(string filename, int line, string funname, int err_no) { cerr<<filename<<" "<<line<<" "<<funname<<" fail"<<":"<<strerror(err_no)<<endl; exit(-1); } Stunode *getfirsNode() { if(hp->get_next() == hp) return NULL; else return hp->get_next(); } void init_Dlist(string filename); void insert_tail();};void Dlist::insert_tail(){ hp->get_prev()->set_next(tmp); tmp->set_prev(hp->get_prev()); hp->set_prev(tmp); tmp->set_next(hp);}void Dlist::init_Dlist(string filename){ FILE *fp = NULL; fp = fopen(filename.c_str(), "r"); if(NULL == fp) err_fun(__FILE__, __LINE__, "fopen", errno); while(1) { int num; char name[40]; fscanf(fp, "%d %s\n", &num, name); //printf("%d %s\n", num, name); tmp = new Stunode(num, name); insert_tail(); if(1 == feof(fp)) break; }}/* 定义一个智能指针(迭代器) */class Dlistiterator {public: Dlistiterator(Dlist &dlist) { pStunode = dlist.getfirsNode(); } Studata &operator*() { return *(pStunode->get_studata()); } Studata *operator->() { return pStunode->get_studata(); } Studata *operator++() { pStunode = pStunode->get_next(); return pStunode->get_studata(); } Studata *operator++(int) { Studata *tmpnode = pStunode->get_studata(); pStunode = pStunode->get_next(); return tmpnode; } operator bool () { return(pStunode->get_studata()!=0); }private: Stunode *pStunode;};int main(void){ Studata data; Dlist dlist; dlist.init_Dlist("./stu.txt"); class Dlistiterator dliter = Dlistiterator(dlist); while(dliter) { cout << dliter->get_name() << " "<<dliter->get_num() << endl; dliter++; } return 0;}stu.txt1 aaa4 fff3 sss5 www2 vvv13. 重载new和delete没有必要,但是我们要知道这样是可以重载的,当你看到有地方重载它们时,不应该 感到惊讶。
阅读全文
0 0
- c++重载操作符
- C++ 操作符重载
- 操作符重载浅析
- 操作符重载
- 10.8 操作符重载
- 操作符重载
- 操作符重载手册
- C#操作符重载
- 操作符重载手册
- 操作符重载手册
- 六操作符重载
- 操作符的重载
- C#操作符重载
- 多态,操作符重载
- 操作符重载集锦
- 重载操作符
- 操作符重载
- 操作符重载手册
- redis集群--转
- 操作系统面试笔记
- logback去掉DubboMonitor烦人的INFO日志
- Docker学习总结(25)——阿里巴巴基于Java容器的多应用部署技术实践
- wireshark如何抓取本机包
- 操作符重载
- js-循环结构练习
- Qt Model/View 学习笔记 (二)——Qt Model/View模式举例
- 试一试
- 推荐算法
- Sublime Text 3 快捷键总结(拿走)
- H5面试---Doctype作用
- 图像处理学习笔记(五):Harris角点检测
- C++ 智能指针——简单实现以及循环引用问题