Chinaunix首页 | 论坛 | 博客
  • 博客访问: 81916
  • 博文数量: 21
  • 博客积分: 371
  • 博客等级: 一等列兵
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-15 21:32
文章分类

全部博文(21)

文章存档

2013年(5)

2012年(16)

我的朋友

分类: C/C++

2013-02-03 23:12:44

1.       重载操作符需要注意的问题:

  不能通过连接其他合法符号来创建任何的操作符.

  用于内置类型的操作符,其含义不能改变.

  重载操作符必须具有至少一个类类型或者枚举类型的操作数.这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含义

  优先级和结合性是固定的.除了函数调用操作符operator()之外,重载操作符时使用默认实参是非法的.

  不再具有短路求值特性(尤其是and,or和逗号表达式)

  大多数重载操作符可以定义为普通非成员函数或类的成员函数

  一般将算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员.(复合赋值返回一个引用而加操作符返回一个对象)

  操作符定义为非成员函数时,通常必须将他们设置为所操作类的友元(因为很可能需要访问类的私有成员:例如输出输入流)

2.       重载操作符的设计:

不要重载具有内置含义的操作符;(重载逗号,取地址,逻辑与,逻辑或等操作符通常不是好的做法.这些操作符具有有用的内置含义,如果我们使用的自己的版本,就不能再使用这些内置含义

大多数操作符对类对象没有意义(为类设计操作符,最好的方式是首先设计类的公共接口.定义了接口之后就可以考虑应将哪些操作符定义为重载操作符.那些逻辑上可以映射到某个操作符的操作可以考虑作为候选的重载操作符

         如果一个类有算术操作符或者位操作符,那么提供相应的复合赋值操作符一般是一个好的做法

         当一个重载操作符的含义不明显时,给操作取一个名字更好.对于很少用的操作,使用命名函数通常比用操作符更好

  将要用作关联容器键类型的类应该定义<操作符.因为许多算法假定这些操作存在

  为类设计重载操作符的时候,必须选择是将操作符设置为类成员还是普通非成员函数,具体规则有:

n  赋值,下标([]),调用和成员访问箭头(->)一般将定义为成员

n  像赋值一样,复合操作符通常应定义为类的成员

n  改变对象状态或者给定类型紧密联系的其他一些操作符(如自增自减和解引用),通常应该定义为成员

n  对称的操作符(如算术,相等,关系和位)最好定义为普通的非成员函数

3.       对输出操作符?的重载,操作符应接受ostream&作为第一个形参,对类类型const对象的引用作为第二个参数,并返回对ostream的引用.

4.       输出操作符通常所做格式化应尽量少

5.       I/O操作符必须为非成员函数

6.       输入和输出操作符有如下区别:输出操作符必须处理错误文件结束的可能性

7.       设计输出操作符是,如果可能,要确定错误恢复措施,这非常重要

8.       除了处理可能发生的任何错误之外,输入操作符还可能需要设置输入形参的条件状态.

9.       为了与内置操作符保持一致,加法返回一个右值,而非引用

关联容器以及某些算法,使用默<操作符,一般而言,关系操作符.诸如相等操作符,应定义为非成员函数

10.   一般而言,赋值操作符与复合赋值操作符应该返回左操作数的引用(因此赋值必须返回对*this的引用)

11.   类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回const成员的引用,另一个为const成员并返回const成员引用

12.   重载箭头操作符必须返回指向类类型的指针,或者返回定义了自己的箭头操作符的类类型对象

13.   为了与内置类型一致,前缀式操作符应该返回被增量或被减量的引用.后缀操作符函数接受一个额外的int型形参.使用后缀式操作符时,编译器提供0作为这个形参的实参

14.   函数调用操作符必须声明为成员函数.一个类可以定义函数调用操作符的多个版本,有形参的数目或者类型加以区别.

15.   标准库提供了一组函数适配器用于特化和拓展一元和二元函数对象.分为以下两类:

a)         绑定器:他通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象(bind1stbind2nd)每个绑定器

b)         求反器:它将谓词函数对象的真值求反(not1not2)

16.   转换操作符是一种特殊的类成员函数.它定义将类类型值转变为其他类型值的转换.转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型

转换函数必须是成员函数,不能指定返回类型.并且形参表必须为空(虽然转换函数不能指定返回类型,但是每个转换函数必须显式返回一个指定类型的值)

转换函数一般不应该改变被转换的对象.因此,转换操作符通常应定义为const成员

常用的转换有:

  使用类类型转换:只要存在转化,编译器将在可以使用内置转换的地方自动

  调用它类类型转换和标准转换

  只能应用一个类类型转换(类类型转换之后不能在跟另一个类类型转化.如果需要多个类类型转换,则代码将出错

  标准转换可放在类类型转换之前

例如:

17.   如果小心使用,类类型转化可以大大简化类代码和用户代码.如果使用的太过自由,类类型转换会产生令人迷惑的编译时错误,这些错误难以理解和避免

一般而言,给出一个类与两个内置类型之间的转换是不好的做法

解析:此次使用extend_compute函数是错误的,原因是因为存在二义性.

当两个构造函数定义的转换都可以使用时,如果存在构造函数实参所需的标准转换,就用该标准转换的类别选择最佳匹配

避免二义性最好的方法是比避免编写互相提供隐式转换的成对的类

18.   函数重载确定的三个步骤:

a)         确定候选函数集合:这些是与被调用函数同名的函数

b)         选择可行的函数:这些是形参数目和类型

c)         选择最佳匹配的函数.为了确定最佳匹配,对将实参转换对应形参所需的类型转换进行分类.对于类类型的实参和形参.可能的转换集合包括类类型转换

在调用重载函数时,需要使用构造函数或者强制类型转换来转换实参,这是设计拙劣的表现.

1.       减少二义性的经验:

a)         不要定义相互转换的类,即如果类FOO具有接受类Bar的对象的构造函数,就不要在为Bar定义到FOO累的转换符

b)         避免到内置算术类型的转换

                         i.              不要定义接受算术类型的操作符的重载版本

                       ii.              不要定义转换到一个以上算术类型的转换

最简单的规则:对于哪些明显正确的,应该避免定义转换函数并限制非显式构造函数


14.1 重载操作符的定义

    操作符(+ ,- , * , / , = , < , >)可以被内置类型使用,比如两 个整数相加或相减,两个字符串相加,两个数组比较大小等等。自定义类默认是不能使用大多数操作符的。自定义类是复合类型,相加或想减或比较大小并没有相应 的规则匹配:两个类相加等于什么? 两个类如何确定谁大谁小?  C++允许我们通过重载运算符的技术让自定义对象支持这些操作。我们可以定义重载规则。

    操作符重载语法很简单: 关键字 operator 后接操作符 比如 operator+


    可以重载的操作符:

   

    不能重载的操作符:

:: . .* ?  :
sizeof typeid new delete
static_cast dynamic_cast const_cast reinterpret_cast
   

 

    重载操作符可以定义成类成员函数,也可以定义成非成员函数

复制代码
class myclass
{
   public:
      myclass() : age(0){};

      // 成员函数定义

      myclass operator+(const myclass &obj) const
      {
           myclass cls;
           cls.age = age + obj.age;
           return cls;
       };

   
       int age;
}

// 等价的非成员函数定义

myclass operator+(const myclass &obj1, const myclass &obj2)
{
    myclass cls;
    cls.age = obj1.age + obj2.age;
    return cls;
}
复制代码

  

    成员函数定义看起来少了一个参数,实际上语法将this限定为第一个操作数。大部分操作符允许定义为成员或非成员函数,具体如何定义看个人喜好。


    + 有些操作符只能定义为成员,如果定义成非成员会产生编译错误 赋值= 下标[] 调用() 箭头访问-> 都不允许定义成非成员函数。
    + 有些操作符只能定义为非成员,如 输入 << 输出 >> 操作符
    + 改变自身状态的建议定义为成员函数,例如 自增++ 自减-- 解引 复合操作+= -=
    + 对称操作符建议定义为非成员函数,例如加减 + - 比较 < == >
    + 成员函数中可以使用 this 而非成员函数中无法使用,因为函数不属于任何对象。

    重载操作符至少要包含一个类类型操作数

    myclass operator+(myclass *obj1, const myclass *obj2) // 操作数至少要包含一个类类型,防止用户修改内置类型的操作符,如果用户定义 int operator+(int a,int b) 意味着用户要修改int类型的加法操作符。 

 

    输入操作符:

&ostream operator<<(ostream &temp, const myclass &obj) // 第二个形参一般都作const限定因为它只做读取操作    
{
    temp << obj.size;
    return temp;
}   

 

    输出操作符:

&istream operator<<(istream &temp, const myclass &obj) // 第二个形参不能const限定,因为需要输入内容到该参数中
{
    tmp >> obj.size;
    return temp;
}
   

 

    输入输出操作符返回都必须是引用,且第一个形参也是引用,前面章节中已说明 IO对象无法复制或者赋值。
    这两个操作符只能定义成非成员函数,原因在于第一形参必须是IO对象引用,而定义为成员函数时第一个参数被省略且被限定为 this 所以只能定义成非成员函数。

  

    算数运算符:

复制代码
myclass operator+(const myclass &obj1, const myclass &obj2)
{
    myclass cls;
    cls.age = obj1.age + obj2.age;
    return cls;
}

myclass operator-(const myclass &obj1, const myclass &obj2)
{
    myclass cls;
    cls.age = obj1.age - obj2.age;
    return cls;
}

bool operator==(const myclass &obj1, const myclass &obj2)
{
    return obj1.age == obj2.age;
}

bool operator>(const myclass &obj1, const myclass &obj2)
{
    return obj1.age > obj2.age;
}
复制代码

 

    赋值=操作符:

myclass& operator=(const myclass &obj)  // 赋值符左操作数是this 指向对象(当前类对象),赋值符号右操作数是形参obj 返回值必须是*this的引用
{
    age = obj.age; 
    return *this
};


    下标操作符:

复制代码
string operator[] (const size_t index) // 返回左值(常量) 下标操作只读
stringoperator[] (const size_t index) // 返回左值(引用) 下标操作可读可写

下标操作可实现const重载 
stringoperator[] (const size_t index) // 下标操作可读可写
const stringoperator[] (const size_t index) const // 下标操作只读

调用规则是 const对象调用const版,非const调用非const版
myclass a ;
a[1] ; // 调用非const版

const myclass b ;
b[1] ; // 调用const版
复制代码

 

    解引操作符:

复制代码
class cls
{
    public:
        void show(int p){cout << p << endl;};
}

class myclass
{
    public:
        myclass:sp(p) (cls *p)
        cls &operator*(){return *sp;}; // 解引返回具体类
        cls *operator->(){return sp;}; // 箭头返回类指针,实际使用时返回的指针会立刻再做系统的箭头操作

        const cls &operator*() const {return *sp;}; // const重载版本
        const cls *operator->() const {return sp;}; // const重载版本
    private:
        cls *sp;
}

cls a;

myclass t(&a) ;

cls b = *t ; // 调用解引操作符,返回cls类对象

t->show(3) ; // 调用箭头操作符,调用cls对象的show()方法

此时t是对象而不是指针,如果t是指针,则会调用系统解引和箭头操作:

cls *j = &t;

*j ; // 返回 myclass对象

j->show(3) ; // 调用myclass对象的show方法,本例中myclass没有定义方法,运行时报错
复制代码

 

    自增操作符:

复制代码
class myclass
{
    public:
        int ls[4];
        int *cur;
        myclass:ls{2,3,1,5},cur(ls){};
        myclass &operator++(){ cur ++; return *this;}; // ++myclass重载,返回引用或对象
        myclass operator++(int){ myclass tmp(*this); cur ++; return tmp;}; // myclass++重载,只能返回对象(不允许返回局部对象的引用)
}

myclass++重载多了形参int,只起到标识作用。
使用和内置类自增没审美区别,自减和自增类似。
复制代码

 

    调用操作符:

复制代码
class myclass
{
    public:
        int operator() (int i){ return i + 2;};
}

定义了调用操作符的类对象叫做 函数对象 ,因为他们的对象行为类似函数
myclass obj;
int j = obj(5) ; // 使用调用操作符重载函数 j = 7
复制代码

 

    转换操作符:

复制代码
class myclass
{
    public:
        operator int() const (){ return a;};
    private:
        int a;
}
复制代码

    在下列情况下函数执行:

    1. 表达式中  1 + obj; 2 < obj;

    2. 条件中 if(obj)  先转换成int 在转成bool

    3. 传参或返回值时



阅读(1977) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~