32
第第第 第第第第第 第第第 第第第第第 第第第第第第第第第第第第第第

第八章 运算符重载

  • Upload
    kaylee

  • View
    129

  • Download
    0

Embed Size (px)

DESCRIPTION

第八章 运算符重载.  用户定义类型用运算符表示操作. 8.1 运算符重载 运算符 —— 一种函数;对于类对象,用户可重新定义运算符函数。 class Complex { private: double rpart; double ipart; public: - PowerPoint PPT Presentation

Citation preview

Page 1: 第八章    运算符重载

第八章 运算符重载第八章 运算符重载

� 用户定义类型用运算符表示操作

Page 2: 第八章    运算符重载

8.1 运算符重载运算符——一种函数;对于类对象,用户可重新定义运算符函数。 class Complex { private: double rpart; double ipart; public: Complex ( ) { rpart=ipart=0.0; } Complex(double rp, double ip) { rpart=rp; ipart=ip; } Complex add(const Complex &com) { Complex temp; temp.rpart=com.rpart+rpart; temp.ipart=com.ip+ipart; return temp; } };

Page 3: 第八章    运算符重载

Complex a(10,7), b(3,5);

Complex c=a.add(b); 希望 c=a+b;

复数加法运算重载符函数: class Complex { //…… public: Complex operator+(Complex &com) { Complex temp(rpart+com.rpart, ipart+com.ipart); return temp; } //…… }; Complex a(10,7), b(3,5), c;

c=a+b;

赋值运算符一般用预定义的

a.operator(b)

公有成员函数

Page 4: 第八章    运算符重载

单目运算符重载: Complex Complex:: operator - ( ) { Complex temp; temp.rpart=-rpart; temp.ipart)=-ipart; return temp; }

单目无参数

双目一个参数

Complex c=-a+b;

不能重载的运算符: . , * , :: , ? , sizeof

(a.operator-( )).operator+(b)

重载应注意的问题:

1 、优先级和结合顺序不变

2 、不能改变目数

3 、类外定义至少有一个相应类的参数

Page 5: 第八章    运算符重载

class x { public: int i; };

X operator+(const X& x1, const X& x2) { X temp; temp.i=x1.i+x2.i; return temp; }

外部函数,若写为:X operator +(int i1,int i2)内部类型,用户不能改变

运算符成员函数——调用成员函数的对象隐式成为表达式的第一个运算数

外部运算符函数——其第一个参数直接对应表达式的第一个运算数;

x1=x2+x3; operator+(x2, x3);

Page 6: 第八章    运算符重载

4 、双目运算符 @ :成员函数只带一个参数;外部函数带两个参数 aa@bb

aa@bb

5 、单目前缀运算符 @ :成员函数不带参;外部函数带一个参数 @aa

@aa

aa.operator@(bb)

operator@(aa, bb)

aa.operator@( )

operator@(aa)

6 、单目后缀运算符 @ :成员函数带一个 int 参数;外部函数带二个参数 aa@ aa@

aa.operator( int )

operator(aa, int)

7 、只能重载已有的运算符,不能创造新的

Page 7: 第八章    运算符重载

8 、成员和外部运算符函数可重载(多个同名运算符函数) class x { public: double operator+(double) { //…… } double operator+(int) { //…… } }; float operator+(X, int) { //…… }

X x1, x2, x3; x2+3.0; x2+3;

Page 8: 第八章    运算符重载

9 、编译器预定义了“ =”, “&”, “,( 顺序 )”, 三种运算符,不必重载可直接使用 class x { //…… private: void operator= (const X&); void operator& ( ); void operator, (const X&); //…… };

void f(X a, X b) { a=b; X *p=&a; }

说明为私有限制对它们的使用

Page 9: 第八章    运算符重载

8.2 算术运算符、赋值运算符和逻辑运算符的重载

8.2.1 重载算术运算符 class Complex { //…… public: Complex operator+(double) { //…… } //…… };

Complex a, b;

a=b+10.0;

a=10.0+b;

b.operator(10.0)

10.0.operator(b)

Page 10: 第八章    运算符重载

解决此问题——非成员函数重载:

Complex operator+(double d, const Complex &com) { Complex temp; temp.rpart=d+rpart; temp.ipart=com.ipart; return temp; }

问题 ?—— 解决方法?

Page 11: 第八章    运算符重载

用于 Complex 的其它算术运算符重载: class Complex { //…… friend Complex operator *(double, Complex com); public: Complex operator*(double d) { //…… } //…… }; Comple operator *(double d, Complex com) { //…… } Complex a, b, c; a=3*b; a=b+c*3*5;

Page 12: 第八章    运算符重载

8.2.2 重载赋值运算符 编译器缺省的赋值运算符是逐位把源对象拷贝到目标对象。 Complex c1, c2; c1=c2;

类 Cmessage 的问题:

#include<iostream.h> extern “C” { #include<string.h> }

Page 13: 第八章    运算符重载

class CMessage { private: char *buffer; public: CMessage( ) { buffer=new char(‘\0’); } ~CMessage( ) { delete [ ] buffer; } void Display( ) { cout<<buffer<<endl; } void Set(char *string) { delete [ ] buffer; buffer= new char[strlen(string)+1]; strcpy(buffer, string); } }; CMessage c1, c2; c1=c2; c2.Set(“newstring”);

解决方法——赋值运算符重载

c1.buffer

c2.buffer

c1.buffer

c2.buffer

c1.buffer

c2.buffer

Page 14: 第八章    运算符重载

class CMessage { //…… public: void operator=(const CMessage& Message) { delete [ ] buffer; buffer= new char[strlen(Message.buffer)+1]; strcpy(buffer, Message.buffer); } //…… };

void main( ) { CMessage c1; c1.Set(“initial c1 message”); Message1.Display( ); CMessage c2; c2.Set(“initial c2 message”); c2.Display( );

c1=c2; c1.Display( ); }

c1.buffer

c2.buffer

c1.buffer

c2.buffer Initial c

2

message

Page 15: 第八章    运算符重载

注意: = 运算符重载必须用成员函数,不能非成员友元形式; 连串赋值要返回对象的引用 Cmessage& CMessage::operator=(const CMessage& Message) { delete [ ] buffer; buffer= new char[strlen(Message.buffer)+1]; strcpy(buffer, Message.buffer); return *this; } void main( ) { CMessage Message1; CMessage Message2; CMessage Message3; Message1=Message2=Message3; }问题 : Cmessage& CMessage::operator=(const CMessage& Message) { if( &Message==this) return *this; delete [ ] buffer; buffer= new char[strlen(Message.buffer)+1]; strcpy(buffer, Message.buffer); return *this; }

Page 16: 第八章    运算符重载

8.2.3 复制构造函数(带引用此类型的单参数的构造函数) class x { //…… public: X( ) { //…… }; X(X& xx) { //…… } //…… }; X x1; X x2(x1); X x3=x1;若未定义,编译器生成缺省的复制构造函数(逐位拷贝)。除此之外,下两种情况编译器自动调用复制构造函数: 1 、类对象作参数传递; 2 、返回一个类对象。 Complex operator+(Complex com1, Complex com2) { return Complex(com1.rpart+com2.rpart, com1.ipart+com2.ipart); }

复制构造函数 ,初始化对象时调用

等价,用 x1初始化 x2 和 x3,= 并不调用赋值运算符

Page 17: 第八章    运算符重载

8.2.4 重载逻辑运算符 class Complex { public: int operator==(const Complex &c) { return (rpart==c.rpart) && (ipart=c.ipart); } int operator==(const double &d) { return (rpart==d) && (ipart==0); } friend int operator ==(const double &d, const Complex &com); //…… }; int operator ==(const double &d, const Complex &com) { return (com.rpart==d) && (ipart==0); } Complex c1, c2; //…… if(c1==c2) c1=c1+c2; else if(c1==3) c1=c2; //……

Page 18: 第八章    运算符重载

8.3 用户定义的转换8.3.1 转换构造函数带一个参数的构造函数,参数不是该类型的引用,而是其他类型。

别的类型 这个类的类型 Complex:: Complex(const double &d) { rpart=d; ipart=0.0; }

class Complex { friend int operator >(const Complex &c1, const Complex &c2); //…… };

int operator >(const Complex &c1, const Complex &c2) { return sqrt(c1.rpart*c1.rpart+c1.ipart*c1.ipart) > sqrt(c2.rpart*c2.rpart+c2.ipart*c2.ipart); }注意:多种类型进行混合运算的运算符成员函数一般采用外部友员函数的形式。其他用到转换构造函数的地方: Complex f( ) { return 1.0; }

Page 19: 第八章    运算符重载

8.3.2 转换运算符类对象类型 (X) 别的类型 (T)

转换运算符(成员函数)无参数、无返回值 X:: operator T( ) { //…… }

class integer { int i; public: integer(int a) { i=a; } operator int( ) { return i; }; integer i1(10), i2(20); int a=i1; i1=a; i2=10+i1*2可采用强制(显式)类型转换形式: int i; integer ir(10); i=int(ir); ir=integer(i);

类型

Page 20: 第八章    运算符重载

8.3.3 二义性同时定义转换运算符和转换构造函数: class X { int i; public: friend X operator+(const X&, const X&); X(int ii) { i=ii; } operator int( ) { return i; } }; X operator +(const X& x1, const X& x2) { return X(x1.i+x2.i); } X x1(10); int i=x1+12; i=12+x1;

Page 21: 第八章    运算符重载

8.5 重载增量和减量运算符

class integer { int i; public: integer( int ii) { i=ii; } integer operator++( ) { ++i; return *this; } integer operator++(int) { i++; return *this; } }; integer a(1), b(1); b=a++; b=++a;

运算符名 成员函数形式 外部函数形式 调用形式 等价调用前缀 ++ X operator++( ) X operator++( X&) ++a a.operator++( )

后缀 ++ X operator++(int ) X operator++( X&,int) a++ a.operator++(0)

前缀 -- X operator--( ) X operator--( X&) - -a a.operator—( )

后缀 -- X operator--(int ) X operator--( X&,int) a- - a.operator—(0)

integer integer:: operator++( ) { integer temp=*this; ++i; return temp; }

Page 22: 第八章    运算符重载

8.6 重载下标运算符数组名 [ 下标 ]: 双目运算符。注意:下标运算符函数不能用外部函数定义,只能非静态成员函数一般形式: T1 T::operator[ ](T2);

class ainteger { int *a; int sz; public: ainteger( int size) { sz=size; a=new int[size]; } int & operator[ ](int i ) { if(i<0 || i>=sz) { cout<<“error”<<endl; exit(1); } return a[i]; } ~ainteger ( ) { delete [ ]a; } };

数组运算结果 下标

ainteger ai(10); ai[2]=3; int i=ai[2];

ai.operator[](2)返回 ai::a[2] 的引用

Page 23: 第八章    运算符重载

举例:文件数组

class FilrRef { private: class File &f; char buf[1]; unsigned long ix; public: FilrRef( File &ff, unsigned long i):f(ff), ix(i) { } FileRef & operator=(char c) operator char( ); };

class File { friend class FileRef; public: File(const char *name) { fd=open(name, O_RDWR|O_CREAT,0664); } ~File( ) { close(fd); } FileRef operator[ ](unsigned long ix) { return FileRef(*this,ix); } private: int fd; };

Page 24: 第八章    运算符重载

FileRef& FileRef:: operator=(char) { lseek(f.fd, ix, 0); write(f.fd, &c, 1); return *this; )

FileRef:: operator char( ) { lseek(f.fd, ix, 0); read(f.fd, buf, 1); return buf[0]; )

int main( ) { File foo(“foo”); foo[5]=‘5’; foo[10]=‘1’; char c=foo[5]; cout<<“c=“<<c<<endl; )

foo.operator[ ](5) 产生 FileRef 的 temptemp.operator=(‘5’) 执行写操作foo.operator[ ](5) 产生 FileRef 的 temp调用 FileRef 中的转换运算符执行读操作

Page 25: 第八章    运算符重载

举例:非整型数组下标(通过某个对象能唯一地确定另一个对象) struct pair { char *name; int val; };

class assoc { pair *vec; int max; int free; assoc(const assoc &); assoc & operator=(const assoc &); public: assoc(int); int & operator[ ](const char*); void print_all( ); };

int & assoc::operator[ ](const char* p) { register pair* pp; for(pp=&vec[free-1]; vec<=pp; pp--) if(strcmp(p, pp->name)==0) return pp->val; if(free==max) { pair * nvec=new pair[max*2]; for(int i=0; i<max; i++) nvec[i]=vec[i]; delete [ ]vec; vec=nvec; max=2*max; }

name

valS T R I N \0

name

vala a \0

name

valb b \0

name

val

name

val

name

val

vec

free

max

Page 26: 第八章    运算符重载

pp=&vec[free++]; pp->name=new char[strlen(p)+1]; strcpy(pp->name, p); pp->val=0; return pp->val; } assoc::assoc(int ) { max=(s<16)? 16: s; free=0; vec=new pai[max]; }

void assoc::print_all( ) { for(int i=0; i<free; i++) cout<<vec[i].name<<“:”<<vec[i].val<<endl; }

void main( ) { const MAX=256; char buf[MAX]; assoc vec(512); while(cin>>buf) vec[buf]++; vec.print_all( ); }

assoc vec(512); //…… cout<<vec[“string”]<<endl; vec[“string”]=12;

输入: aa bb bb aa aa bb aa aa输出: aa:5 bb:3

Page 27: 第八章    运算符重载

8.7 重载函数调用运算符x(arg1, arg2,arg3) x.operator( ) (arg1,arg2,arg3)

举例: assoc 类的 print_all( ) 成员函数用于输出数组,现在希望外部函数来实现。 class assoc { friend class assoc_iterator; //…… }; class assoc_iterator { const assoc* cs; int i; public: assoc_iterator(const assoc& s) { cs=&s; i=0; } pair * operator( ) ( ) { return (i<cs->free? &cs->vec[i++]:0; } }; void main( ) { const MAX=256; char buf[MAX]; assoc vec(512); while(cin>>buf) vec[buf]++; assoc_iterator next(vec); pir *p; while(p=next( )) cout<<p->name<<“:”<<p->val<<endl; }

指向要访问的数组,下标从 0开始

Page 28: 第八章    运算符重载

8.8 重载递引用运算符-> 运算符函数返回是中间结果,在此结果上施加内定义的 -> 运算。 class B; class A { public: B* operator->( ); //…… }; class B { //…… };

void main( ) { A a; …a->b… }举例: class A { private: char *cp; public: void a( ); void b( ); void c( ); A(char *r) { cp=new char[strlen(r)+1]; strcpy(cp,r); } };

1 、在对象 a上执行 A::operator->( )

2 、返回值放入类型为 B* 的临时对象 x 中

3、求 x->b 得结果 A 的运算符函数返回 B的指针,再通过内定义函数访问 B的成员(a.operator->( ))->b

Page 29: 第八章    运算符重载

class B { private: A *delegate; public: void a( ) { delegate->a( ); } void b( ) { delegate->b( ); } void c( ) { delegate->c( ); } void d( ); B(char *r=0) { delegate=new A(r); } };解决方法(适用非运算符重载函数)—— B 中重载 -> 运算符: class B { private: A *delegate; public: void d( ); A* operator->( ) { return delegate; } B(char *r=0) { delegate=new A(r); } }; B b; b.d( ); b->a( ); b->b( ); b->c( );

注意: b 并不是指针

仅为了重用 A中的方法,B要重新定义函数

Page 30: 第八章    运算符重载

习题:1 、写出程序执行结果: #include<iostream.h> class myclass { int i; float j; public: myclass(int x=0, float y=0) { i=x; j=y; } myclass operator( ) (int k, float m); void display( ) { cout<<i<<“ “<<j<<endl; }

}; myclass myclass::operator( ) (int k, float m) { i=k+10; j=m+10.5; return *this; }

void main( ) { myclass obj1(10, 11.5); obj1.display( ); obj1(100, 6.9); obj1.display( ); }

10 11.5

110 17.4

Page 31: 第八章    运算符重载

2 、填空: #include<iostream.h> #include<string.h> class Name { public: Name( ) { pName=0; } Name(char *pn) { copyName(pn); } Name(Name &s) { copyName(s.pName); } ~Name( ) { deleteName( ); } Name =( Name &s ) { deleteName( ); copyName(s.pName); return ; } void display( ) { cout<<pName<<endl; } protected: void copyName(char *pN); void ; char *pName; }; void copyName(char * pN) { pName=new ; ; } void deleteName( ) { delete ; }

& operator

*this

deleteName( )

Name::

Name::

char [strlen(pN)+1]strcpy(pName, pN)

pNmae

Page 32: 第八章    运算符重载

3 、下面是一个用一维数组表示的矩阵类: class Matrix { private: double * elem; int row, col; public: Matrix( int row1, int col1 ); ~Matrix( ); }; (1) 完成构造和析构函数的定义。(2) 要实现用 m(i,j)存取矩阵的元素,需增加什么运算符重载,写出此运算符重载 函数的定义。

double & operator( ) (int x, int y);

Matrix:: Matrix(int row1, int col1) { row=row1; col=col1; elem=new double[row*col]; } Matix::~Matrix( ) { delete [ ] elem; } double& Matrix:: operator( ) (int x, int y) { return elem[col*(x-1)+y-1]; }