版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
C++特殊操作符的重载C++特殊操作符赋值操作符“=”访问数组元素操作符“[]”类成员访问操作符“->”函数调用操作符“()”动态对象创建与撤销操作符new与delete类型转换操作符赋值操作符“=”的重载同一个类的两个对象如何赋值?Aa,b;......a=b;//?C++编译程序会为每个类定义一个隐式的赋值操作,其行为是:逐个成员进行赋值操作。对于普通成员,它采用常规的赋值操作。对于成员对象,则调用该成员对象类的赋值操作进行成员对象的赋值操作。隐式的赋值操作有时不能满足要求!例如,对下面String类的对象进行赋值操作时,系统提供的隐式赋值操作存在问题:classString{ intlen; char*str; public: String(char*s) {len=strlen(s); str=newchar[len+1]; strcpy(str,s); }
~String(){delete[]str;str=NULL;}};......Strings1("xyz"),s2("abcdefg");.......s1=s2;s1.str原来所指向的空间成了“孤儿”(内存泄露)s1和s2互相干扰s1和s2消亡时,"abcdefg"所在的空间将会被释放两次!lenstrlenstrs1s2xyz37abcdefg7解决上面问题的办法是自己定义赋值操作符重载函数:classString{......String&operator=(constString&s){ if(&s==this)return*this;//防止自身赋值:a=a
delete[]str; str=newchar[s.len+1]; strcpy(str,s.str);len=s.len; return*this;}};上面的返回值类型能是void吗?为什么是String并且是它的引用?为了允许下面的操作:
a=b=c和(a=b)=c自定义的赋值操作符重载函数不会自动地去进行成员对象的赋值操作,必须要在自定义的赋值操作符重载函数中显式地给出,例如:classA{......};classB{ Aa; intx,y; public: ...... B&operator=(constB&b) { if(&b
==this)return*this;a=b.a;//调用A类的赋值操作符重载函数来实现成员对象的赋值。
x=b.x; y=b.y; return*this; }};
一般来讲,需要自定义拷贝构造函数的类通常也需要自定义赋值操作符重载函数。
注意:要区别下面两个“=”的不同含义。Aa;Ab=a;//初始化,等价于:Ab(a);,调用拷贝构造函数。......b=a;//赋值,调用赋值操作符=重载函数。下面对b2.a进行初始化的区别是什么?classA{......};classB{Aa;
intx,y;
......
B(){......}
B(constB&b):a(b.a)
B(constB&b)
{x=b.x;
y=b.y;
a=b.a;
}};Bb1;Bb2(b1);//这里还要调用A的默认构造函数对a初始化!//这里是调用A的赋值操作给a赋值访问数组元素操作符“[]”的重载
对于由具有线性关系的元素所构成的对象,可通过重载下标操作符“[]”来实现对其元素的访问。例如:classString{ intlen; char*str;
public:
...... char&operator[](inti){ returnstr[i];} charoperator[](inti)const{ returnstr[i];}//用于常量对象};......Strings("abcd");...s[2]...//访问s中的'c'再例如:classVector//向量类{ int*p_data; intnum; public: ...... int&operator[](inti)//访问向量第i个元素。
{ returnp_data[i]; }};......Vectorv(10);.........v[2]...//访问向量第三个元素对于常量对象的场合要加上另一个重载函数:intoperator[](inti)const{ returnp_data[i];}classMatrix//矩阵类{
?p_data;//表示矩阵数据
introw,col;//表示矩阵的行数和列数
public:
Matrix(intr,intc){
?
} ~Matrix(){?
}
Vector
&operator[](inti)//访问矩阵元素。
{return?;}
......};......Matrixm(10,20);.........m[2][3]...//访问矩阵第三行、第四列元素。如何能这样使用?重载间接类成员访问操作符“->”
--智能指针“->”为一个双目操作符:第一个操作数为一个指向类或结构的指针。第二个操作数为第一个操作数所指向的类或结构的成员。Aa,*p=&a;p->f();//假设f是A中的一个成员函数针对某个类重载“->”操作符,可以实现一种智能指针(smartpointers):对该类的对象实施“->”操作能访问它“指向”的另一个对象的成员。通过该对象去访问它指向的对象的成员之前能做一些额外的事情。(智能)Bb(&a);b->f();//b是个智能指针对象,访问对象a的f例:在程序执行的某个时刻获取某个对象被访问的次数classA{ intx,y; public: voidf(); voidg();};voidfunc(A*p){......p->f();
......p->g();......}......Aa;func(&a);......//调用完func后,如何知道在func中访问了a多少次?一种解决方案:在类A中加一个计数器countclassA{ intx,y; intcount;public: A(){count=0;...} voidf(){count++;...} voidg(){count++;...} intnum_of_access()const{returncount;}};voidfunc(A*p)
{......p->f();......p->g();......}......Aa;func(&a);...a.num_of_access()...//获得对a的访问次数问题:要修改类A,每个成员函数都要加上:count++;如果类A中有外界可访问的数据成员,无法对其访问计数!p->z;}intz;更好的解决方案:定义一个智能指针类classB//智能指针类{ A*p_a;//指向A类对象的指针 intcount; public: B(A*p) { p_a=p;count=0; }
A*operator->()//操作符“->”的重载函数,按单目操作符重载!
{ count++;
returnp_a; } intnum_of_a_access()const { returncount; }};voidfunc(B&p)//注意:p是个B类对象!{...p->f();...p->g();...}不修改类AclassA{ intx,y; public: voidf(); voidg();};Aa;A*p=&a;//p是普通的指针p->f();//访问a的成员fBb(&a);//b为一个智能指针,
//它指向了ab->f();//访问a的成员f,等价于//b.operator->()->f();func(b);//把b传给func...b.num_of_a_access()... //获得对a的访问次数p->z;intz;//也能对数据成员的访问进行计数!为了完全模拟普通的指针功能,针对智能指针类,还可以重载“*”(对象间接访问)、“[]”、“+”、“-”、“++”、“--”、......等操作符:classB//智能指针类{ A*p_a;public:
B(A*p){p_a=p;} A*operator->(){returnp_a;}
A&operator*()
{return*p_a; } A&operator[](inti)
{returnp_a[i]; }
......};Aa[10];A*p=&a[0];//普通指针p->f();//a[0].f();(*p).f();//a[0].f();p[2].f();//a[2].f();p=p+1;p++;......
Bb(&a[0]);//智能指针 //可写成:Bb=&a[0];b->f();//a[0].f();(*b).f();//a[0].f();b[2].f();//a[2].f();b=b+1;b++......重载函数调用操作符“()”
--函数对象在C++中,把函数调用也作为一种操作符来看待:操作符为(),操作数为函数名及各个实参,结果为函数返回值z=f(x,y);重载函数调用操作符之后,相应类的对象就可当作函数来使用了。例如:classA{ intvalue; public: A(){value=0;} A(inti){value=i;} intoperator()(intx)//函数调用操作符()的重载函数
{returnx+value; }};......Aa(3);//a是个对象,但可以当函数来使用它cout<<a(10)<<endl;//a(10)将会去调用A中的函数调用操作符
//重载函数,10作为实参,返回13函数调用操作符重载主要用于具有函数性质的对象(函数对象,functor),该对象通常只有一个操作,它除了具有一般函数的行为外,它还可以拥有状态。例如,下面是一个能产生随机数的对象:classRandomNum{ unsignedintseed;//状态 public: RandomNum(unsignedinti){seed=i;}unsignedintoperator()()//函数调用操作符重载
{seed=(25173*seed+13849)%65536; returnseed;}};......RandomNumrandom_num(1);//创建一个函数对象...random_num()...//利用函数对象产生一个随机数在C++中,λ表达式是通过函数对象来实现的。对于下面的λ表达式: [...](intx)->int{.......}首先,隐式定义一个类:数据成员对应λ表达式中用到的环境变量,用构造函数对它们进行初始化。按相应λ表达式的功能重载了函数调用操作符。然后,创建上述类的一个临时对象(设为obj)最后,在使用上述λ表达式的地方用该对象来替代:作用于实参进行函数调用cout<<[...](intx)->int{.......}(3);cout<<obj(3);传给其它函数f([...](intx)->int{.......});f(obj);操作符new与delete的重载操作符new和delete用于创建和撤销动态对象:A*p=newA;......deletep;操作符new有两个功能:为动态对象分配空间调用对象类的构造函数操作符delete也有两个功能:调用对象类的析构函数释放动态对象的空间系统提供的new和delete操作所涉及的空间分配和释放是通过系统的堆区管理系统来进行的,它要考虑各种大小的空间分配与释放,对某个类而言,效率常常不高。可以针对某个类重载操作符new和delete,使得该类能以自己的方式来实现动态对象空间的分配和释放功能。注意:重载操作符new和delete时,重载的是它们的分配空间和释放空间的功能,不影响对构造函数和析构函数的调用。重载操作符new操作符new必须作为静态的成员函数来重载(static说明可以不写),其格式为:void*operatornew(size_tsize);
返回类型必须为void*参数size表示对象所需空间的大小,其类型为size_t(unsignedint)例:把动态对象初始化为全‘0’#include<cstring>classA{ intx,y; public: void*operatornew(size_tsize) { void*p=malloc(size);//调用系统堆空间分配操作。
memset(p,0,size);//把申请到的堆空间初始化为全“0”。
returnp; } ......};上面重载的new与系统提供的差别在于:它可以为一个没有定义任何构造函数的动态对象提供初始化!重载的new操作符的使用格式与系统提供的基本相同。例如(假设A中重载了new):A*p=newA;自动计算A的大小,把它作为参数(size)去调用new的重载函数。调用A类默认构造函数。A*q=newA(...);自动计算A的大小,把它作为参数(size)去调用new的重载函数。调用A类带参数(...)的构造函数。重载new时,除了对象空间大小参数以外,也可以带有其它参数,void*operatornew(size_tsize,…);如果重载的new包含其它参数,其使用格式为:p=new(...)A(...);...
表示提供给new重载函数的其它参数...
表示提供给A类构造函数的参数例:在非“堆区”为动态对象分配空间#include<cstring>classA{ intx,y; public: A(inti,intj){x=i;y=j;} void*operatornew(size_tsize,void*p) {returnp; }};......charbuf[sizeof(A)];A*p=new(buf)A(1,2);//动态对象的空间分配为buf......p->~A();//使得p所指向的对象消亡。不能用系统的delete重载delete一般来说,如果对某个类重载了操作符new,则相应地也要重载操作符delete。操作符delete也必须作为静态的成员函数来重载(static说明可以不写),其格式为:
voidoperatordelete(void*p,size_tsize);返回类型必须为void。第一个参数类型为void*,指向对象的内存空间。第二个参数可有可无,如果有,则必须是size_t类型。
重载后,操作符delete的使用格式与未重载的相同。classA{intx,y;public:
void*operatornew(size_tsize){void*p=malloc(size);//申请堆空间memset(p,0,size);returnp;}voidoperatordelete(void*p)
{free(p);}......};......A*ptr=newA;......deleteptr;//调用delete重载函数,把ptr传给p例:重载操作符new与delete来管理程序中某类动态对象的堆空间。
就某一个类的动态对象而言,系统的堆空间管理效率不高,系统要考虑程序中各种大小的堆空间申请和释放问题。另外,系统的堆空间管理会面临“碎片”问题:在下面的情况下,再创建一个A类对象,这时,每个可分配空间都不够,但可分配空间总和是够的。CABBCBBC对于某个类的动态对象,它可以自己管理空间的分配和释放,以提高效率。重载new第一次创建该类的动态对象时,先从系统管理的堆区中申请一块大的空间,然后把上述大空间分成若干小块,每个小块的大小为该类一个对象的大小,用链表来管理这些小块;在上述链表中为该类动态对象分配空间。重载delete该类的一个动态对象消亡时,该对象的空间归还到new操作中申请到的大空间(链表)中,而不是归还到系统的堆区中。#include<cstring>classA{ ......//类A的已有成员说明。
public: staticvoid*operatornew(size_tsize); staticvoidoperatordelete(void*p); private:
A*next;//用于组织A类对象自由空间结点的链表。 staticA*p_free;//用于指向A类对象的自由空间链表头。};A*A::p_free=NULL;constintNUM=32;void*A::operator
new(size_tsize){ if(p_free==NULL) { //申请NUM个A类对象的大空间。 p_free=(A*)malloc(size*NUM);//一个动态数组 //在大空间上建立自由结点链表。
for(inti=0;i<NUM-1;i++)
p_free[i].next=&p_free[i+1];
p_free[NUM-1].next=NULL; }//从链表中给当前对象分配空间 A*p=p_free; p_free=p_free->next; memset(p,0,size);//可由构造函数完成 returnp;}p_free:NULLq1:A*q1=newA;voidA::operatordelete(void*p){ ((A*)p)->next=p_free; p_free=(A*)p;}A*q1=newA;A*q2=newA;deleteq1;p_free:NULLq1q2(p)p_freeNULL在创建动态对象时,一大块用完了,怎么办?将会申请第二大块,并再次构建链表:NULLp_freeNULL经过若干次分配、归还之后,可能会出现下面的格局:所有A类对象消亡后,如何归还所有的大块?动态对象数组的创建与撤销A*p=newA[10];...p[0]、p[1]、...、p[9]...delete[]p;//“[]”不能漏掉!上述操作将调用下面的重载函数(隐式或自定义):void*operatornew[](size_tsize);voidoperatordelete[](void*p);注意:当类(或基类和成员对象类)中有析构函数时,传给函数new[]的参数size的实际值会比对象数组需要的空间多4个字节,用于存储元素个数!例如,假设类A有析构函数,则A*p=newA[10];
//size:sizeof(A)*10+4......delete[]p;//会根据存储的元素个数调用每个元素的析构函数元素个数p自定义类型转换操作符
类中带一个参数的构造函数可以用作从其它类型到该类的转换。例如:classComplex{ doublereal,imag; public: Complex(){real=0;imag=0;}
Complex(doubler){ real=r;imag=0;} Complex(doubler,doublei){real
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 腹腔镜手术配套设备考试题库及答案
- 建筑施工危险作业安全须知培训
- 2026年中医耳鼻喉科瘀血阻络鼻衄辨证测试卷及答案
- 电气装置的防火、灭火与防爆培训
- 顶管工程安全措施和操作规程培训
- 储罐安装安全交底培训课件
- 高速公路架桥机安全技术交底培训
- 煤矿巷道挑顶安全技术措施培训
- 安全阀常见故障分析与处理培训
- 轨行区作业安全方案培训
- 2025-2026年四川省法律职业资格考试客观题专项习题
- 2024-2025学年广东广州番禺区七年级(下)期末数学试卷及答案
- 2026福建海峡科化股份有限公司社会招聘15人笔试备考题库及答案详解
- 2026北京语言大学新编长聘人员招聘9人(第三批)考试备考题库及答案详解
- 2026秋北京版小学数学二年级上册教学计划
- 2026年下半年中小学教师资格笔试考前押题试卷(完整版)
- 消化内镜操作规范
- DL∕T 640-2019 高压交流跌落式熔断器
- JJF(苏)228-2019 电磁流量计在线校准规范-(现行有效)
- 颅内压增高和脑疝课件-3
- 临床生物化学参考范围行业标准解读
评论
0/150
提交评论