More Effective C++ ——操作符

焦点放在“重载操作符”被调用的时机,被调用的方法,它们的行为,它们应该如何与其他操作符发生关系以及如何夺取“重载操作符”的控制权。

一  对定制的“类型转换函数”保持警惕

 1 隐式类型转换的两种方式:

        单自变量constructors: 指能够以单一自变量成功调用的constructors(可能声明具有单一参数,也可能声明具有多个参数,数,并且除了第一参数之外都有默认值);

class Name{
public:
    Name(const string& s);     //可以将string转化为Name
    ...
};

       隐式类型转换:一个拥有奇怪类型的member function:关键词operator之后加上一个类型名称,不能为此函数指定返回值类型,因为返回值类型基本已经表现于函数名称上;

class Rational{
public:
 ...
 operator double() const;      //将Rational转换为double类型
};

//在以下情况下隐式类型转化函数会被默认调用
Rational r(1,2);
double d = 0.5 * r;                   //将r转化为double类型然后进行乘法运算

以上为隐式类型转换的两种方式,通常在没有任何外在踪迹的条件下被调用。(注意可能会遗失信息的隐式转换,如double->char)

 2 消除隐式类型转换函数

   原因:可能会导致错误的(非预期的)函数被调用

   解决办法:以另外一个功能对等的函数取代类型转换操作符。

class Rational{
public:
 ...
 double asDouble() const;      //将Rational转换为double类型
};

//在以下情况下隐式类型转化函数会被默认调用
Rational r(1,2);
cout << r << endl;                    //使用之前的方法将会把r隐式转换为double之后输出
                                      // 这种情况下,此语句错误,Rational没有operator<<函数
cout << r.asDouble() <<endl;          //以double的形式输出r

   调用类型转换函数虽然不如隐式类型转换方便,但却有效地避免了默认调用错误的函数;例:string object -> C-style char*:  c_str();

  3 消除单自变量constructor提供的隐式转换

     简易法:C++11   explicit    只要将构造函数声明为explicit,编译器便不能因隐式转换的需要调用constructor,不过仍然允许显示转换

    编译器不支持简易法时:没有一个转换程序可以内含一个以上的用户定制转换行为(即单自变量constructor或饮食类型转换操作符)

    例:

template<class T>
class Array{
public:
   class ArraySize{
      public:
          ArraySize(int num):theSize(num){}
          int size() const {return theSize;} 
      private:
          int theSize;
   };  
   Array(ArraySize size);
};

ArraySize是一个proxy classes,它的每一个对象都是为了其他对象而存在的。ArraySize对象作为int的替身完成了一次基于单自变量constructor的int的隐式转换,由于不允许第二次用户定制转换行为,避免了单自变量constructor提供的隐式转换

二  区别increment和decrement操作符的前置(prefix),后置(postfix)形式

    后置式有一个int自变量作为参数,并且在它被调用的时候,编译器默默地为该int指定一个0值

    前置式返回一个reference ,后置式返回一个const对象

const作用:

   1:维持与内建类型行为一致:i++++错误  但是 ++++i合法;

   2:i++++第二个operator++改变的对象是第一个operator++对象的返回值,而不是原对象;

//前置式:累加后取出
UPInt& UPInt::operator++()
{
    *this += 1;
    return *this;
}

//后置式:取出后累加
const UPInt::operator++(int)     //参数的目的只是区分前置式和后置式
                                 //为避免没有使用函数命名参数的警告,可以略去不打算使用的参数名称
{
   UPInt oldValue = *this;
   ++(*this);
   return oldValue;
} 

三  千万不要重载&&,||和,操作符

     若对&&,||进行重载,左右表达式都会作为操作符函数的表达式进行评估,且评估顺序未明确定义,这会破坏&&,||的“骤死性”;

    逗号表达式:逗号左侧的值先被评估,之后右侧的值被评估,整个表达式的结果以逗号右侧的值为代表,若进行重载,左右两侧的值均被作为参数,无法确定其评估顺序

 其他 不能重载的运算符:

    

.

.*

::

?:

new

delete

sizeof

typeid

static_cast

dynamic_cast

const_cast

reinterpret_cast

 


四  了解各种不同意义的new和delete

        new:   1 分配足够的内存,用来放置某类型的对象 ; 2 调用constructor,为分配内存中的对象设置初值;  (不能改变new的行为)

        operator new:  被new调用,执行必要的内存分配动作(可以被重载);

                 void* operator new(size_t size);

                 返回值类型为void*,指向一块原始的,未设初值的内存;

        placement new:(头文件 : #include<new>)

class Widget{
public:
   Widget(int widgetSize);
   ...
};
Widget* construtWidgetInBuffer(void* buffer,int widgetSize)
{
   return new (buffer) Widget(widgetSize);
}

删除和内存释放:

       为了避免资源泄露,每一个分配行为都必须匹配一个相应但相反的释放动作

        delete:调用析构函数,调用operator delete释放该对象占用的内存

数组:

new[]:调用operater new[] 分配足够的内存空间,对数组中的每一个对象调用一个constructor;

delete[]:针对数组中的每一个对象调用其destructor(与构造函数的调用顺序相反),然后再调用operator delete[]释放内存。

        

   

          

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值