C++operator重载问题

operator是C++的关键字,它和运算符一起使用,表示一个运算符函数,理解时应将operator=整体上视为一个函数名。

   这是C++扩展运算符功能的方法,虽然样子古怪,但也可以理解:一方面要使运算符的使用方法与其原来一致,另一方面扩展其功能只能通过函数的方式(c++中,“功能”都是由函数实现的)。

 

 一、为什么使用操作符重载?
对于系统的所有操作符,一般情况下,只支持基本数据类型和标准库中提供的class,对于用户自己定义的class,如果想支持基本操作,比如比较大小,判断是否相等,等等,则需要用户自己来定义关于这个操作符的具体实现。比如,判断两个人是否一样大,我们默认的规则是按照其年龄来比较,所以,在设计person 这个class的时候,我们需要考虑操作符==,而且,根据刚才的分析,比较的依据应该是age。那么为什么叫重载呢?这是因为,在编译器实现的时候,已经为我们提供了这个操作符的基本数据类型实现版本,但是现在他的操作数变成了用户定义的数据类型class,所以,需要用户自己来提供该参数版本的实现。

 

二、如何声明一个重载的操作符?
A:  操作符重载实现为类成员函数
重载的操作符在类体中被声明,声明方式如同普通成员函数一样,只不过他的名字包含关键字operator,以及紧跟其后的一个c++预定义的操作符。
可以用如下的方式来声明一个预定义的==操作符:
class person{
private:
    int age;
    public:
    person(int a){
       this->age=a;
    }
   inline bool operator == (const person &ps) const;
};
实现方式如下:
inline bool person::operator==(const person &ps) const
{

     if (this->age==ps.age)
        return true;
     return false;
}
调用方式如下:
#include
using namespace std;
int main()
{

  person p1(10);
  person p2(20);
  if(p1==p2) cout<<”the age is equal!”< return 0;
}
这里,因为operator ==是class person的一个成员函数,所以对象p1,p2都可以调用该函数,上面的if语句中,相当于p1调用函数==,把p2作为该函数的一个参数传递给该函数,从而实现了两个对象的比较。

B:操作符重载实现为非类成员函数(全局函数)
对于全局重载操作符,代表左操作数的参数必须被显式指定。例如:
#include
#include
using namespace std;
class person
{
public:
int age;
public:
};

bool operator==(person const &p1 ,person const & p2)

//满足要求,做操作数的类型被显示指定
{
if(p1.age==p2.age)
return true;
return false;
}
int main()
{
person rose;
person jack;
rose.age=18;
jack.age=23;
if(rose==jack)
cout<<"ok"< return 0;
}

 

 

C:如何决定把一个操作符重载为类成员函数还是全局名字空间的成员呢?
①如果一个重载操作符是类成员,那么只有当与他一起使用的左操作数是该类的对象时,该操作符才会被调用。如果该操作符的左操作数必须是其他的类型,则操作符必须被重载为全局名字空间的成员。
②C++要求赋值=,下标[],调用(), 和成员指向-> 操作符必须被定义为类成员操作符。任何把这些操作符定义为名字空间成员的定义都会被标记为编译时刻错误。
③如果有一个操作数是类类型如string类的情形那么对于对称操作符比如等于操作符最好定义为全局名字空间成员。

 


D:重载操作符具有以下限制:

(1) 只有C++预定义的操作符集中的操作符才可以被重载;

重载不能改变操作符的优先级

 

如果一个内建操作符是一元的,那么所有对它的重载仍是一元的。如果是二元的重载后也是二元的

 

 下面看一个有代表性的例子::

头文件Complex.h:

#include
using namespace std;

class Complex {
public:
   Complex();
   Complex(double);
   Complex(double,double);

   void write() const;
   Complex operator +(const Complex &) const;
   Complex operator -(const Complex &) const;
   Complex operator *(const Complex &) const;
   Complex operator /(const Complex &) const;
private:
   double real;
   double imag;
};

Complex::Complex(){
   real = imag = 0.0;
}

Complex::Complex(double re) {
   real = re;
   imag = 0.0;
};

Complex::Complex(double re,double im) {
   real = re;
   imag = im;
}

void Complex::write() const {
   cout << real << " + " << imag << 'i';
};

Complex Complex::operator + (const Complex &u) const {
   Complex v(real+u.real,imag+u.imag);
   return v;
}

Complex Complex::operator - (const Complex &u) const {
   Complex v(real-u.real,imag-u.imag);
   return v;
}

Complex Complex::operator* (const Complex &u) const {
   Complex v(real * u.real - imag * u.imag,real * u.imag + imag * u.real);
   return v;
}

Complex Complex::operator / (const Complex &u) const {
   double temp = u.real * u.real + u.imag * u.imag;
   Complex v( (real * u.real + imag * u.imag ) / temp, ( imag * u.real - real * u.imag ) / temp);
   return v;
}

 

测试文件:Complex.cpp和测试结果:

 

一个被重载的操作符,就是一个用户自定义的函数,只不过它可以享受操作符语法所带来的便利

 

除了内存管理操作符new、new[]、delete、delete[]之外,一个以顶层函数形式被重载的操作符必须在它的参数列表中包含一个类的对象

下标操作符[]、赋值操作符=、函数调用操作符()和指针操作符->必须以类成员函数的形式进行重载(这样可以保证第一个操作数是类的对象,不然9[x]、6.32=x,不被接受)

操作符(如%)要么以成员函数被重载,要么以顶层函数被重载。对于后者将至少带一个对象参数,这是因为操作符以顶层函数实现时,如果连一个参数都没有,那么对如下表达式   x % y  编译系统就不能区分%是内建的,还是用户的。如果操作符%是类成员函数,或是有一个类对象参数的顶层函数,编译系统就能够根据特定的上下文决定调用哪一个%操作符

 

顶层函数重载操作符,类名和域解析符都没有了,因为它不是一个类成员函数

 

被重载的操作符,要么是一个类成员函数,要么在它的参数列表中包含一个类成员!!

 

下标操作符[]和函数调用操作符()只能以成员函数的形式被重载,不能成为顶层函数!!

eg:

 

修正后:

 

在上面那种情况,如果想要第二个表达式成功的话,就需要定义一个顶层函数重载+了

 

有一点要明白,当定义顶层函数时,函数里面不能含有对象的私有成员操作,否则编译通不过,下面有三种方法可以解决:

1,将私有成员设计为public成员,但是这种方法违背了类的信息隐藏原则

2,在Complex中加入用于访问real和imag的公有成员函数,但是容易造成接口混淆

3,将操作符重载函数声明为类的friend,但是不符合面向对象原则,应少用,建议仅在操作符重载时使用

 

类的私有成员只能被该类的成员函数和该类的friend函数访问

类的保护成员只能被该类的或其派生类的成员函数和该类的friend函数访问

 

class C {

     //....

  friend int f();

  //...

};

该声明赋予f访问C的私有和保护成员的权力,因为f不是成员函数,该声明可以放在C中的private、protected或public的任意部分,不受访问控制符的影响、限制

 

程序员可对操作符>>进行重载,以支持用户自定义数据类型。>>的第一个操作数是系统类的对象(如cin是系统类istream的对象),而这些重载函数时以类成员函数的形式实现的。

如果在对用户自定义的类型重载>>时,就必须对系统类的源代码进行修改,而这显然是非常不明智的做法,因此只能将>>重载函数设计为顶层函数

 

输入流对象总是以引用方式传递,这是因为系统为了接受输入数据,需要更新输入流对象的某些信息

如果被重载>>的函数中和类的私有数据或者保护数据打交道,则需要将重载操作符声明为friend

 

拷贝构造函数和赋值操作符(=),都是用来拷贝一个类的对象给另一个相同类型的对象。

拷贝构造函数将一个对象拷贝到另一个新的对象(因为拷贝-构造);赋值操作符将一个对象拷贝到另一个已经存在的对象

 

如果类的作者没有提供拷贝构造函数,也没有重载赋值操作符,编译器将会给这个类提供一个拷贝构造函数和一个赋值操作符。编译器提供的拷贝构造函数和赋值操作符的运转机制是:将源对象中的每个数据成员拷贝到目标对象相应的数据成员中

 

 

看例子:

 

如果类的作者定义了指针成员,且该指针指向一块动态分配的存储空间,就应该为这个类设计拷贝构造函数,并重载赋值操作符

注意不要返回临时变量,重载里面错综复杂,小心一点

 

下标操作符[]必须要以成员函数的形式进行重载!

 class C {         //可以修改对象

returntype & operator[] (paramtype);

};

或者:

class C {            //不能修改对象

const returntypr & operator[] (paramtype) const;

};

例子:

头文件:test.h

 

测试文件和结果:

 

上面可以不提供const版本的重载,但是就不能处理非const的对象,所以,有时候要考虑这一点,加上const重载!

 

 

函数调用操作符()必须要以成员函数的形式重载。

class C {

returntype operator()(paramtypes);

};

 

看例子:

头文件inttwoarray.h

#include
#include
using namespace std;

class intTwoArray {
public:
   int & operator()(int,int);
   const int & operator() (int,int) const;
   intTwoArray(int,int);
   int get_size1() const { return size1; }
   int get_size2() const { return size2; }
private:
   int size1;
   int size2;
   int *a;
};

int & intTwoArray::operator() (int i,int j) {
   if( i < 0 || i >= size1 )
      throw string("FirstOutOfBounds");
   if( j < 0 || j >= size2 )
      throw string("SecondOutOfBounds");
   return a[i * size2 + j];
}

const int & intTwoArray::operator() (int i,int j) const {
   if(i < 0 || i >= size1)
      throw  string("FirstOutOfBounds");
   if(j < 0 || j >= size2)
      throw string("SecondOutOfBounds");
   return a[i * size2 + j];
}

intTwoArray::intTwoArray(int s1,int s2) {
   int size = s1 * s2;
   try{
      a = new int[size];
   }
   catch(bad_alloc) {
      cerr << "Can't allocate storage for intTwoArray\n";
      throw;
   }
   size1 = s1;
   size2 = s2;
}

测试程序和结果:

 

自增,自减也可以重载,但是前置,后置一共四种,前后置通过一个int参数区分,但是这个参数没有什么实际用途,仅仅起到区分的作用

例子:

 头文件clock.h         (需要注意一点,这个地方注意注释部分中间的代码,一定要加上否则。。。。。)

#include
#include
using namespace std;

/
class Entry;
class Dict;
ostream & operator<<(ostream &,const Entry &);
ostream & operator<<(ostream &,const Dict &);

 

class Entry {
public:
   Entry() {flag = false;}
   void add(const string &,const string &);
   bool match(const string &) const;
   void operator=(const string &);
   void operator=(const char *);
   friend ostream & operator<<(ostream &,const Entry &);
   bool valid() const { return flag; }
   void del() { flag = false; }
private:
   string word;
   string def;
   bool flag;
};

 

void Entry::operator=(const string &str) {
   def = str;
   flag = true;
}

 

void Entry::operator=(const char * str) {
   def = str;
   flag = true;
}

 

ostream & operator<<(ostream &out,const Entry &e) {
   out << e.word << "  defined as: " << e.def ;
   return out;
}

 

void Entry::add(const string &w,const string &d) {
   word = w;
   def = d;
}

 

bool Entry::match(const string &key) const {
   return key == word;
}

 

enum {MaxEntries = 100};

 

class Dict {
public: 
   friend ostream & operator<<(ostream &,const Dict &);
   void remove(const string &w);
   Entry & operator[] (const string &);
   Entry & operator[] (const char *);
private:
   Entry entries[MaxEntries+1];

 

};

 

ostream & operator<<(ostream &out,const Dict &d) {
   for(int i = 0 ;i < MaxEntries; i++)
      if(d.entries[i].valid())
         out << d.entries[i] << endl;
   return out;
}

 

Entry &Dict::operator[](const string &k) {                  //返回引用是该程序的技巧。因为程序通过这个地方,和前面重载的=操作符填充字典返回因为是为了修改对应的值,即赋值
   for(int i = 0;  i < MaxEntries && entries[i].valid(); i ++)
      if(entries[i].match(k))
         return entries[i];
   string not_found = "***not in dictionary";
   entries[i].add(k,not_found);
   return entries[i];
}

 

Entry & Dict::operator[](const char *k) {
   string s = k;
   return operator[](s);
}

 

void Dict::remove(const string &w)
{
   int i,j;
   for(i = 0 ;i< MaxEntries; i ++)
      if(entries[i].valid() && entries[i].match(w))
         break;
   if(i == MaxEntries)
      return ;
   for(j = i + 1; j < MaxEntries && entries[j].valid() ; j++)
      entries[j-1] = entries[j];
   entries[j-1].del();                    //最后一个失灵,即最后一个标志位设置为false,因为已经移到前面了
}

 

 

测试程序和结果:

 

 

 

内存管理操作符new、new[]、delete和delete[]既可以用成员函数也可以用顶层函数重载

嵌入式内存有限,应用程序经常需要直接管理内存

 

new操作符重载:

void * C::operator new(size_t size) {

  //...

}

void *  operator new(size_t  size) {

     //...

}

返回值void *

new和new[]操作符重载函数的第一个参数必须是size_t类型,其数值等于被创建对象大小,其他参数是可选的

 

 

delete操作符重载:

void C::operator delete( void * objPtr) {

  //...

}

void operator delete(void *objPtr) {

   //...

}

 

delete和delete[]操作符重载函数的第一个参数必须是void*类型,返回值必须是void,而其他参数则是可选的

看例子:

头文件frame.h:

#include
#include
using namespace std;

const int  MaxFrames = 4;
const int  DataSize = 128;

class Frame {
public:
   Frame() { name = "NoName"; print(); }
   Frame(const char *n) { name = n; print(); }
   Frame(const string &n) { name = n ; print(); }
   Frame(const string&,const void *,unsigned);
   void print() const;
   void *operator new(size_t);

   void delete(void *);
private:
   string name;
   unsigned char data[DataSize];
};

Frame * allFrames = 0;
unsigned char framePool[MaxFrames * sizeof(Frame)];
bool alloc[MaxFrames];

Frame::Frame(const string &n,const void *d, unsigned bsize) {
   name = n;
   memcpy(data,d,bsize);
   print();
}

void Frame::print() const {
   cout << name << "  created.\n";
}

void * Frame::operator new(size_t size) {
   if(size != sizeof(Frame))
      throw string("Not a Frame");
   if(allFrames == 0) {
      allFrames = reinterpret_cast



  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值