运算符重载

内置的数据类型的运算符是不能重载的,重载的运算符只能属于自定义的数据类型。运算符就是一个函数,
函数名是operator加上运算符号,如operator+等。

class  integer
{
 int i;
public:
 integer( int n = 0 ) : i(n) { cout << "constructor" << endl; }
 ~integer( ) { cout << "destructor" << endl; }
 integer( const integer & other )  // 这里不存在拷贝构造函数的调用
 {
  i = other.i;
  cout << "copy constructor" << endl;
 }
 
 const integer operator+( const integer & other )
 {
  cout << "operator+" << endl;
  return integer( i + other.i ); // constructor,产生一个临时对象,在调用完+=后才销毁
 }

 integer & operator+=( const integer & other )
 {
  cout << "operator+=" << endl;
  i += other.i;
  return *this;
 }
};

void testInteger( )
{
 integer  I(1);  //constructor
 integer  J(2);  // constructor
 integer  K(3);  // constructor
 K += I + J;  // I + J会产生一个临时对象,这个对象在销毁前先要调用+=,这个对象作为参数
} // 四次destructor,包括临时对象的destructor


////////////////////////////运算符用全局函数的例子
class  integer
{
 long i;
 integer * This() {return this;}

public:
 integer( int n ) : i(n) {}

// friend const integer&  operator+( const integer & a );  // 编译没有通过??
//  friend const integer&  operator-( const integer & a );
  friend const integer  operator~( const integer & a );
  friend integer*  operator&( integer & a );
  friend int  operator!( const integer & a );
  friend const integer&  operator++( integer & a );  // prefix 前向自增 ++a,a加了之后返回a
  friend const integer  operator++( integer & a, int );  // postfix 后向自增 a++,返回a自增前的值
  friend const integer&  operator--( integer & a );
  friend const integer  operator--( integer & a, int );

 void  print(const char* msg = "")
 {
  if (msg)
   cout << msg << ": " << endl;
  cout << "i == " << i << endl;
 }
};

const integer operator~(const integer& a )
{
 cout << "~integer" << endl;
 integer ret(~a.i);
 return ret;
}
integer* operator&(integer& a )
{
 cout << "&integer" << endl;
 return a.This();
}

int operator!(const integer & a )
{
 cout << "!integer" << endl;
 return !a.i;
}
const integer& operator++(integer& a)
{
 cout << "++integer" << endl;
 a.i ++;
 return a;
}
const integer operator++(integer& a, int)
{
 cout << "integer++" << endl;
 integer ret(a.i);  // 临时对象是const的
 a.i ++;
 return ret;
}
const integer& operator--(integer& a)
{
 cout << "--integer" << endl;
 a.i --;
 return a;
}
const integer operator--(integer& a, int)
{
 cout << "integer--" << endl;
 integer ret(a.i);
 a.i--;
 return ret;
}

void testInteger( )
{
 integer a(10);

 integer ret1 = ~a;
 ret1.print("ret1");

 integer* pI = &a;

 cout << !a << endl;

 ++a;
 a.print("++a");

 integer ret2 = a++;
 ret2.print("ret2");
 a.print("after a++");
 
 --a;
 a.print("--a");

 integer ret3 = a--;
 ret3.print("ret3");
 a.print("after a--");
}

//////////////////////运算符使用成员函数的例子:
// 这里省略掉,可参考书本上的代码。


++a,表示前向自增,使用的全局函数是operator++(a),如果是使用成员函数则是a.operator++()。
a++,表示后向自增,使用的全局函数是operator++(a, int),如果是使用成员函数则a.operator++(int)。
这里的int参数是一个不会被使用的参数,但是它要占一个位置,以此来区别前向自增的函数名。

临时对象会被编译器自动标识为const。对于一个临时对象不应该再考虑修改它的属性,而只是考虑读取它的属性,
所以一个临时对象只调用const成员函数是合理的。

 

临时对象跟局部对象是两个不同的概念。
局部对象在离开作用域后立即销毁,而在销毁之前可能要调用拷贝构造函数把它的值拷贝到外部(调用者的)
存储单元中从而得到一个临时对象。
从下面的代码可以看出他们的区别:

#include <iostream>
#include <fstream>
using  namespace std;

ofstream   out( "aa.txt" );

class BLOCK
{
 int  data;
public:
 BLOCK( int n = 0 ): data( n ) { out << "constructor" << endl; }
 ~BLOCK( ) { out << "destructor" << endl; }
 BLOCK( const BLOCK & other )
 {
  data = other.data;
  out << "copy constructor" << endl;
 }

 void print( const char * msg = "" )
 {
  if ( msg )
   out << msg << endl;
  out << "data == " << data << endl;
 };
};


BLOCK f1( )
{
 return  BLOCK( 12 );  // 这里不会产生局部变量,而是直接在外部(调用者的)存储单元中产生临时对象,所以不会调用拷贝构造函数。
}

BLOCK f2( )
{
 BLOCK  tempBlock( 34 ); // 这里产生了一个局部对象。
 return tempBlock;
} // 局部对象会在这里销毁,但在销毁前要调用拷贝构造函数在外部存储空间内产生一个临时对象。


int main( )
{
 f1( ).print();
 out << "*********************" << endl;
 f2( ).print();
 return 1;
}
输出为:
constructor

data == 12
destructor
*********************
constructor
copy constructor
destructor

data == 34
destructor

可以看到,临时对象会在适当的时候进行销毁。另外,使用直接产生临时对象的方法显然比产生局部对象的方法效率要高,因为直接产生
临时对象的话不需要产生和销毁局部对象,也不需要调用拷贝构造函数(来产生临时对象)。


运算符[]和运算符()的函数必须是成员函数(还有=号及->也必须是成员函数)。operator[]必须带一个参数,对象就像数组。operator()允许有任意多的参数,对象就像函数名。
一个例子:
#include <iostream>
using  namespace std;

class int_container
{
 enum{ AMOUNT = 100 };
 int  ints[ AMOUNT ];
 int  index;

public:
 int_container( )
 {
  index = 0;
  memset( ints, 0, AMOUNT * sizeof( int ) );
 }
 ~int_container( ) {}

 void add( int newInt )
 {
  if ( index >= AMOUNT )
   return;
  ints[ index ++ ] = newInt;
 }

//  friend int operator[]( int_container& container, int nIndex )  // error,operator[]不允许是全局函数
//  {
//   if ( nIndex >= AMOUNT || nIndex < 0 )
//    return -99;
//   return container.ints[ nIndex ];
//  }
//  friend void operator()( int_container& container, int nIndex, int nIncrement ) // error,operator()不允许是全局函数
//  {
//   if ( index >= AMOUNT || nIndex < 0 )
//    return;
//   container.ints[ nIndex ] + nIncrement;
//  }

 int operator[]( int  nIndex )  // operator[],必带一个参数
 {
  if ( nIndex >= AMOUNT || nIndex < 0 )
   return -99;
  return ints[ nIndex ];
 }

 void operator()( int nIndex, int nIncrement )  // operator(),任意参数
 {
  if ( nIndex >= AMOUNT || nIndex < 0 )
   return;
  ints[ nIndex ] += nIncrement;
 }
};

// 测试代码
int main( )
{
 int_container intContainer;
 const  nAmount = 30;
 for ( int nIndex = 0; nIndex < nAmount; nIndex ++ )
 {
  intContainer.add( nIndex );
 }

 for ( int nPos = 0; nPos < nAmount; nPos ++ )
 {
  cout << intContainer[ nPos ] << " ";  // operator[],对象就如同数组的使用
 }

 for ( int nModPos = 0; nModPos < nAmount; nModPos ++ )
 {
  intContainer( nModPos, 10 );  // operator(),对象就如同函数名的使用
 }
 for ( int aa = 0; aa < nAmount; aa ++ )
 {
  cout << intContainer[ aa ] << " ";
 }

 return 1;
}


运算符,的一个例子:
#include <iostream>
using  namespace std;

class after
{
public:
 const after& operator,( const after& right ) const
 {
  cout << "after::operator,()" << endl;
  return *this;
 }
};

int main( )
{
 after a, b;  // 仅是声明
 a, b;  // 调用a.operator,(b)

 return 1;
}

 

书中一个迭代器的例子,重载了运算符->:
#include <iostream>
#include <cassert>
using  namespace std;

// 对象类
class obj
{
 static int i, j;
public:
 void f() { cout << i++ << endl; }
 void g() { cout << j++ << endl; }
};

int obj::i = 10;
int obj::j = 30;

// 对象容器类
class obj_container
{
 enum {AMOUNT = 100};
 obj * a[ AMOUNT ];
 int  index;

public:
 obj_container( )
 {
  index = 0;
  memset( a, 0, AMOUNT * sizeof( obj* ) );
 }
 ~obj_container( ) {}

 void add( obj* pObj )
 {
  if ( index >= AMOUNT )
   return;
  a[ index ++ ] = pObj;
 }

 friend class sp;  // 迭代器声明
};

// 迭代器类
class sp
{
 obj_container * oc;  // 迭代器关联的容器
 int  index;

public:
 sp( obj_container * pOC )
 {
  assert( pOC );
  oc = pOC;
  index = 0;
 }
 ~sp() {}

 int operator++( )  // prefix  ++sp
 {
  if ( index >= oc->AMOUNT )
   return 0;
  if ( oc->a[ ++ index ] == 0 )
   return 0;
  return 1;
 }

 int operator++( int )  // postfix  sp++
 {
  return operator++();
 }

 obj* operator->() // 重载->
 {
  if ( oc->a[ index ] )
   return oc->a[ index ];
  static  obj  dump;
  return &dump;
 }
};


int main( )
{
 const AMOUNT = 10;
 obj  o[ AMOUNT ];
 obj_container oc;

 for ( int nPos = 0; nPos < AMOUNT; nPos ++ )
  oc.add( &o[nPos] );

 sp SP( &oc );

 do
 {
  SP->f(); // 调用operator->()
  SP->g();
 } while( SP ++ ); // 调用operator++(int)

 return 1;
}

运算符集中不能重载的运算符有:
. 圆点运算符;
.* 成员指针的解引用运算符;
求幂运算符;

 

重载<<与>>的一个例子:

#include <iostream>
#include <strstream>
#include <cassert>
using namespace std;

class intarray
{
 enum {AMOUNT = 5};
 int  i[ AMOUNT ];
public:
 intarray( )
 {
  memset( i, 0, AMOUNT * sizeof(*i) );
 }
 ~intarray( ) {}

 int & operator[]( int  nIndex )
 {
  assert( nIndex >= 0 && nIndex < AMOUNT );
  return i[ nIndex ];
 }

 friend ostream& operator<<( ostream& os, const intarray&  ia );  // 一定要用全局函数
 friend istream& operator>>( istream& is, intarray&  ia );
};

ostream& operator<<( ostream& os, const intarray& ia )
{
 for ( int nPos = 0; nPos < ia.AMOUNT; nPos ++ )
 {
  os << ia.i[ nPos ];
  if ( nPos != ia.AMOUNT - 1 )
   os << ", ";
 }
 os << endl;

 return os;
}

istream& operator>>( istream& is, intarray& ia )
{
 for ( int nPos = 0; nPos < ia.AMOUNT; nPos ++ )
 {
  is >> ia.i[ nPos ];
 }
}


int main( )
{
 istrstream  input("12 34 56 67 243");
 intarray ia;
 input >> ia;
 ia[ 0 ] = -90;
 cout << ia;

 return 0;
}


对于一个对象(内存块)的初始化(非赋值),编译器会自动调用合适的构造函数:
#include <iostream>
using namespace std;

class fi
{
public:
 fi( ) { cout << "fi constructor" << endl; }
};

class fee
{
public:
 fee( int i ) { cout << "fee(int) constructor i == " << i << endl; }
 fee( const fi & fiRef ) { cout << "fee(const fi&) constructor" << endl; }
};


int main( )
{
 fi  Fi;
 fee Fee = 10;  // 编译为对象分配存储空间,并插入合适的构造函数:fee( int i )
 fee Fee2 = Fi; // 调用合适的构造函数:fee( const fi & fiRef )

 return 0;
}


有构造函数、析造函数、拷贝构造函数和赋值函数的一个例子:
class withPointer
{
 char * p;
 enum {blocksz = 100};
public:
 withPointer( )
 {
  p = (char*)malloc( blocksz );
  assert( p );
  memset( p, 0, blocksz );
 }

 ~withPointer( )
 {
  if ( p )
   free( p );
 }

 withPointer( const withPointer & other )
 {
  p = (char*)malloc( blocksz );
  assert( p );
  memset( p, 0, blocksz );
  if ( other.p )
   memcpy( p, other.p, blocksz );  // 拷贝的是指针指向的内容
 }

 withPointer& operator=( const withPointer& other )
 {
  if ( &other != this )  // 防止自赋值
   memcpy( p, other.p, blocksz );  // 拷贝的是指针指向的内容

  return *this;
 }
};


书中使用引用计数和写拷贝技术的一个例子:
#include <cstring> // for memset等

class counted
{
 class memblock
 {
  enum { SIZE = 126 };
  char  c[ SIZE ];
  int   refcount;

 public:
  memblock( )
  {
   memset( c, 0, SIZE );
   refcount = 1;
  }

  memblock( const memblock & other )
  {
   memcpy( c, other.c, SIZE );
   refcount = 1;
  }

  void attach( )
  {
   refcount ++;
  }

  void detach( )
  {
   refcount --;
   if ( refcount == 0 )
   {
    delete this;
   }
  }

  int count( ) const
  {
   return refcount;
  }

  void set( char x )
  {
   memset( c, x, SIZE );
  }

  memblock * unaliase( )
  {
   if ( refcount == 1 )
    return this;

   refcount --;

   return  new memblock( *this );  // 如果有别的对象占用则拷贝出新的memblock(拷贝构造函数被调用)
  }
 } *block;

public:
 counted( )
 {
  block = new memblock( );
 }
 ~counted( )
 {
  block->detach();
 }

 counted( const counted & other )
 {
  block = other.block;
  block->attach();
 }

 counted & operator=( const counted & other )
 {
  if ( &other == this )
   return *this;

  block->detach();
  block = other.block;
  block->attach();

  return *this;
 }

 void unaliase( )
 {
  block = block->unaliase();
 }

 void write( char x )
 {
  unaliase( );  // 更改合适的指针
  block->set( x );
 }
 
};

// 测试代码
int  main( )
{
 counted A, B;
 counted C( A );
 B = A;
 C.write( 'c' );

 return 0;
}

对于一个类如果没有显示的提供operator=函数,则编译器会提供一个默认的operator=函数,行为跟拷贝构造函数一样。如果一个对象里面包含了别的对象,则编译器会分别对这些对象调用它们的operator=函数。应该显示的定义合适的operator=函数,特别当有指针成员的时候。
这是一个例子:
#include <iostream>
using namespace std;

class bar
{
public:
 bar & operator=( const bar & other )
 {
  cout << "bar::operator=" << endl;
  return *this;
 }
};

class foo
{
 bar  m_bar;
};

int  main( )
{
 foo  a, b;
 a = b;  // 调用缺省的foo::operator=,这个函数里面又调用了bar::operator=

 return 0;
}


自动(隐式)类型转换,实现上有两种方式:一种是使用构造函数实现,一种是使用运算符operator来实现。
使用构造函数来实现隐式类型转换的一个例子:
#include <iostream>
using namespace std;

class one
{
public:
 one( ) {}
};

class two
{
public:
 two( const one & o )  // 这个构造函数能把one对象转换成two对象
 {
  cout << "two::two(one&)" << endl;
 }
};

void f( two ) {}

int main( )
{
 one  o;
 f( o ); // 编译器会搜索看有没有函数可能把one对象转换成two对象,two::two( const one & 0 )会被调用从而得到一个two对象再传递给函数。

 return 0;
}

explicit的作用:
#include <iostream>
using namespace std;

class one
{
public:
 one( ) {}
};

class two
{
public:
 explicit two( const one & o ) // 加上explicit表示这个构造函数只能被显示调用而不能隐式调用(explicit只用于构造函数)
 {
  cout << "two::two(one&)" << endl;
 }
};

void f( two ) {}

int main( )
{
 one  o;
// f( o );  // error,构造函数two::two( const one & 0 )不能被隐式调用(explicit限制了)。
 f( two(o) ); // ok,显示调用

 return 0;
}


使用运算符实现隐式转换的一个例子:
#include <iostream>
using namespace std;

class one
{
 int  o;
public:
 one( int i = 0, int n = 0 ): o(i) { cout << "one::one(int, int)" << endl; }
 int  num( ) { return o; }
};

class two
{
 int   t;
public:
 two( int x ): t(x) {}
 operator  one() const { return one(t); }  // 这一个运算符函数,没有返回类型,把two对象转换成one对象。
};

void f( one o ) { cout << o.num() << endl; }

int main( )
{
 two tw( 10 );
 f( tw ); // 隐式调用two::operator one() const,把two对象转换成one对象再传递给函数f。

 f( 20 );  // 隐式调用one::one(int, int)把int对象转换成one对象再传递给函数f。

 return 0;
}

使用运算符实现隐式转换的又一个例子:
#include <cstring>
#include <cassert>
#include <iostream>
using namespace std;

namespace  bs_classes
{
 class string  // 把char*封装起来
 {
  char * m_s;
 public:
  string( const char * s  = "" )
  {
   m_s = (char*)malloc( strlen(s) + 1 );
   assert( m_s );
   strcpy( m_s, s );
   m_s[ strlen(m_s) ] = 0;
  }
  
  ~string( )
  {
   free( m_s );
  }
  
  operator char*( ) const  // operator函数,把string转换成char*类型
  {
   return m_s;
  }
 };
}

int main( )
{
 bs_classes::string  s1;
 cout << (char*)s1;

 bs_classes::string s2( "have a fly " );
 cout << (char*)s2 << endl;

 bs_classes::string s3("hello"), s4("hi");
 cout << strcmp( s3, s4 ) << endl;  // 调用operator char*() const把string转换成char*

 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值