文档章节

c++能重载的操作符和不能重载的操作符

稀饭桶子
 稀饭桶子
发布于 2013/08/13 15:02
字数 2078
阅读 49
收藏 2
点赞 0
评论 0

I.可以重载的操作符

+ - * / %
^ & | ~ !
= > < += -=
*= /= %= ^= &=
|= >> << >>= <<=
== != >= <= &&
|| ++ -- ->* ,
-> [] () operator new operator new[]
operator delete operator delete []

II.不能重载的操作符

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

III.基本规则

  1. 一元操作符可以是不带参数的成员函数[1] 或带一个参数的非成员函数[1]

  2. 二元操作符可以是带一个参数的成员函数[1]或带两个参数的 非成员函数[1]

  3. operator=、operator[]、operator()、operator->只能定义为成员函数[1]

  4. operator->的返回值必须是一个指针或能使用->的对象。

  5. 重载 operator++ 和 operator-- 时带一个 int 参数表示后缀,不带参数表示前缀。

  6. 除 operator new 和 operator delete 外,重载的操作符参数中至少要有一个非内建数据类型。

  7. x@y  搜索范围为:x 成员函数--> 全局函数/X所在名字空间中的函数/Y所在名字空间中的函数/X的友元函数/Y的友元函数。

  8. 重载的的操作符应尽量模拟操作符对内建类型的行为。

二、使用重载

I.操作符重载的一些建议

  1. 只将会改变第一个参数的值的操作符(如: +=)定义为成员函数,而将返回一个新对象的操作符(如: +)定义为非成员函数(并使用 += 来实现)。

  2. 只有非成员函数才能在左参数上实施性别转换,如果需要进行转换则应将操作符定义为非成员函数。

  3. 对一元操作符, 为避免隐式转换最好将其重载为成员函数。

  4. 对二元操作符, 为能在左操作数上能进行和右操作数一样的隐式转换, 最好将其重载为非成员函数。

  5. 为了遵照使用习惯,operator>>、operator<< 应定义为非成员函数。

  6. 重载 operator[] 之类的操作符, 应尽量提供 const 版本和非 const 版本。

  7. 关于将操作符定义为成员或非成员可参考以下建议:

    操作符 建议
    所有一元操作符 成员
    = () [] -> 必须为成员
    += -= /= *= ^= &= != %= >>= <<= 成员
    其它二元操作符 非成员
  8. 如果默认操作符已经可以施用于你的型别上, 则应尽量避免重载此操作符. 如 operator, 、operator&(取地址) 等等.

II. 重载 operator new

  1. 为什么要重载 operator new ?

    [效率问题] 通常系统默认提供的分配器速度极慢, 而且分配小型对象时空间浪费严重.
    [改变行为] 默认的分配器失败时会抛出异常, 或许你想改变这种行为.
     

  2. perator new 的行为

    [区分三个不同的 new]

    new 操作符(new 表达式, new operator, new expression): 通常我们调用 X * pX = new X 时使用的就是这个操作符, 它由语言内建, 不能重载, 不能改变其行为. 它包括分配内存的 operator new 和调用构造函数的 placement new.

    operator new :opeator new 是一个函数, void * operator new(size_t size) . 它分配指定大小的内存, 可以被重载, 可以添加额外的参数, 但第一个参数必须为 size_t. operator new 除了被 new operator 调用外也可以直接被调用: void * rawMem = operator new(sizeof(X)).

    placement new : placement new 在一块指定的内存上使用构造函数, 包含头文件 <new> 之后也可以直接使用 placement new: X * pX = new (rawMem) X. [2]

    与 new operator 类似, 对于 delete operator, 也存在 operator delete: void operator delete(void *), 析构方法 pX->~X().

    [operator new 的错误处理]

    默认的 operator new 在内存分配失败时将会抛出 std::bad_alloc 异常; nothrow new[3] 
    (X * pX = new (nothrow) X) 在内存分配失败时将会返回 0 . 这种行为可以通过设置 new-handler 来改变. new-handler 是一个回调函数指针, typedef void(*new_handler)(void). 通过 set_new_handler(new_handler) 函数设置回调句柄后, 如果分配内存失败, operator new 将会不断的调用 new-handler 函数, 直到找到足够的内存为止. 为了避免死循环, new-handler 函数必须具备以下行为之一:

    1. 找到可用的内存.

    2. 安装其它的 new-handler 函数.

    3. 卸除 new-handler, 即 set_new_hanlder(0), 这样下此循环将恢复默认行为抛出异常或返回 0.

    4. 抛出异常.

    5. 保存错误日志, 退出程序.

  3. 准备重载 operator new

    重载 operator new 时需要兼容默认的 operator new 错误处理方式. 另外, C++ Standard 规定当要求的内存为 0 byte 时也应该返回有效的内存地址. 所以 operator new 的重载实现应大致如下:

    重载 operator delete 简单许多, 只需注意 C++ Standard 要求删除一个 NULL 是安全的即可.

    1. void * ... operator  new(size_t  size ... )  

    2. {  

    3.     if (size == 0)  

    4.              size = 1;   

    5.   

    6.     while (1)  

    7.     {   

    8.         ...  // allocate memery  

    9.         if (allocate sucessfull)  

    10.             return  ... // return the pointer.  

    11.   

    12.         new_handler curhandler = set_new_handler(0);  

    13.         set_new_handler(curhandler);     // get current new handler  

    14.   

    15.         if (curhandler != 0)  

    16.             (*curhandler)();  

    17.         else   

    18.              throw std::bad_alloc();  

    19.     }  

    20. }  

  4. 重载 operator new

    opeator new 的重载和其它操作符大不相同. 首先, 即使你不重载, 默认的 operator new 也可施用于你的自定义型别上(operator, 也具有此特性), 而其它操作符如果不进行重载就无法使用. 其次, 其它重载其它操作符时参数个数都是固定的, 而 operator new 的参数个数是可以任意的, 只需要保证第一个参数为 size_t, 返回类型为 void * 即可, 而且其重载的参数类型也不必包含自定义类型. 更一般的说, operator new 的重载更像是一个函数的重载, 而不是一个操作符的重载.

    [★ 用不同的参数重载 operator new]

    通过使用不同的参数类型, 可以重载 operator new, 例如 :

    你还可以为 operator new 的重载使用默认值, 其原则和普通函数重载一样, 只要不造成和已存在的形式发生冲突即可. 可能你已经想到了, 你甚至还可以在 operator new 中使用不定参数, 如果你真的需要的话.

    在全局空间中也可直接重载 void * operator new(size_t size) 函数, 这将改变所有默认的 new 操作符的行为, 不建议使用.

    [★ 重载 class 专属的 operator new]

    为某个 class 重载 operator new 时必须定义为类的静态函数[4], 因为 operator new 会在类的对象被构建出来之前调用. 即是说调用 operator new 的时候还不存在 this 指针, 因此重载的 operator new 必须为静态的. 当然在类中重载 operator new 也可以添加额外的参数, 并可以使用默认值.另外, 和普通函数一样, operator new 也是可以继承的.

    1. class X{  

    2. ...  

    3. static  void * operator  new(size_t  size);    // ... (1)   

    4. static  void * operator new (size_t size, int);   // ... (2)  

    5. };  

    6.   

    7. class Y :  public X{  

    8. ...  

    9. };  

    10.   

    11. class Z :  public X{  

    12. ...  

    13. static void  * operator new(size_t  size);    // ... (3)  

    14. };  

    15.   

    16. X * pX1 = new X;      // call (1)  

    17. X * pX2 = ::new  X;  // call default operator new   

    18. X * pX3 = new  (0) X;    // call (2)  

    19.   

    20. Y * pY1 =  new Y;     // call (1)   

    21.   

    22. Z * pZ1 =  new Z;     // call (3)   

    23. Z * pZ2 = ::new  Z; // call default operator new  

    24. Z * pZ3 = X::new Z;  // error, no way to call (1)  

    25. Z * pZ4 = new (0) Z;  // error, no way to call (2)  

    26. void  * operator newsize_t size, int  x, int y = 0, int z = 0)  

    27. {  

    28.     ...  

    29. }  

    30.   

    31. X * pX = new (10) X;  

    32. Y * pY = new  (10, 10) Y;  

    33. Z * pZ = new (10, 10, 10) Z;  

    34.   

    35. ...  

    36. void * operator  new(size_t size, ...)  

    37. ...  

    38. void * operator  new(size_t  size, int x,  int y, int  z)  

    39. {  

    40.     ...  

    41. }  

    42.   

    43. X * pX = new  (1, 2, 3) X;  

    1. 重载 operator delete

      如果你重载了一个 operator new, 记得一定要在相同的范围内重载 operator delete. 因为你分配出来的内存只有你自己才知道如何释放. 如果你忘记了, 编译器不会给你任何提示, 它将会使用默认的 operator delete 来释放内存. 这种忘记的代价是惨重的, 你得时刻在写下 operator new 的同时写下 operator delete.

      如果在类中使用 operator delete, 也必须将其声明为静态函数. 因为调用 operator delete 时对象已经被析构掉了. operator delete 的重载可以有两种形式:

      并且这两种形式的 operator delete 可以同时存在, 当调用 delete px 时, 如果 (1) 式存在的话将调用 (1) 式. 只有在 (1) 式不存在时才会调用 (2) 式. 对第 (2) 种形式的 operator delete, 如果用基类指针删除派生类对象, 而基类的析构函数没有虚拟的时候, size 的值可能是错误的.


      1. void operator delete(void * mem)

      2. void operator delete(void * mem, size_t size)

    本文转载自:http://www.adintr.com/myarticle/operator.html

    共有 人打赏支持
    稀饭桶子
    粉丝 10
    博文 34
    码字总数 6033
    作品 0
    漳州
    C++雾中风景6:拷贝构造函数与赋值函数

    在进行C++类编写的过程之中,通常会涉及到类的拷贝构造函数与类的赋值函数。初涉类编写的代码,对于两类函数的用法一直是挺让人困惑的内容。这篇文章我们会详细来梳理拷贝构造函数与赋值函数...

    LeeHappen ⋅ 01/17 ⋅ 0

    C语言/C++编程学习,轻松解决C++函数重载

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

    小辰带你看世界 ⋅ 05/12 ⋅ 0

    new与malloc比较,QQ农场与大草原?

    前几天看到微信群里有人在讨论new与malloc的不同之处,看到有人说malloc不如new,细细看他所列举的为什么new比malloc好的原因,感觉很有道理,但是转念一想,突然间我又觉得语言这种东西为什...

    loving_forever_ ⋅ 2016/06/04 ⋅ 0

    【C++】C++运算符重载的规则

    本篇博客讲解: 运算符重载的规则,以及实例 运算符重载的规则 被重载的运算符必须是已经存在的C++运算符,不能重载自己创建的运算符。 运算符被重载之后,原有功能仍然保留。只是扩展了原有功...

    qq_26525215 ⋅ 2017/09/22 ⋅ 0

    在学校和老师学习C/C++你学到了什么?

    计算机行业在未来是一个具有无限潜力的行业,但同样行业竞争力也是十分强烈,同样事靠计算机吃饭的,你是职业叫码农,人家的职业叫程序员,大牛的职业是架构师、分析师,你甘心成为一个日夜加...

    悟空_b201 ⋅ 04/10 ⋅ 0

    malloc和new有什么区别

    malloc和new有以下不同: new、delete是操作符,可以重载,只能在c++中使用。 malloc、free是函数,可以覆盖,c、c++中都可以使用。 new可以调用对象的构造函数,对应的delete调用相应的析构...

    夏雪冬日 ⋅ 2012/12/11 ⋅ 0

    [C++再学习系列] 隐式类型转换与转换操作符

      C++标准允许隐式类型转换,即对特定的类,在特定条件下,某些参数或变量将隐形转换成类对象(创建临时对象)。如果这种转换代价很大(调用类的构造函数),隐式转换将影响性能。隐式转换的发...

    技术小美 ⋅ 2017/11/05 ⋅ 0

    const与#define

    C++中const符号表原理图 const int a=10; 当遇见常量声明时,在符号表中放入常量 编译过程中若发现使用常量则直接以符号表中的值替换 编译过程中若发现对const使用了entern或者&操作符,则给...

    lybnt ⋅ 02/19 ⋅ 1

    Cpp-友元知识补充

    Cpp-友元知识补充 在有些情况必须得允许非成员函数访问一个类的私有成员,同时可以组织一般的访问。比如重载操作符,输入或者输出操作符,经常在类中需要访问类的私有成员。友元这个机制允许...

    googler_offer ⋅ 01/28 ⋅ 0

    【Java】疯狂Java基础(一)——面向对象的特征:继承、封装和多态

    一、前言 小编记得,刚接触计算机相关的课程的时候,接触的是c++,c++的老师一上来就说c++是面向对象的,c语言是面向过程的。面向对象比面向过程厉害,是从面向过程发展过来的的。 当时有一个...

    kisscatforever ⋅ 03/28 ⋅ 0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    下一页

    【每天一个JQuery特效】根据可见状态确定是否显示或隐藏元素(3)

    效果图示: 主要代码: <!DOCTYPE html><html><head><meta charset="UTF-8"><title>根据可见状态确定 是否显示或隐藏元素</title><script src="js/jquery-3.3.1.min.js" ty......

    Rhymo-Wu ⋅ 27分钟前 ⋅ 0

    OSChina 周四乱弹 —— 初中我身体就已经垮了,不知道为什么

    Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @加油东溪少年 :下完这场雨 后弦 《下完这场雨》- 后弦 手机党少年们想听歌,请使劲儿戳(这里) @马丁的代码 :买了日本 日本果然赢了 翻了...

    小小编辑 ⋅ 45分钟前 ⋅ 7

    浅谈springboot Web模式下的线程安全问题

    我们在@RestController下,一般都是@AutoWired一些Service,由于这些Service都是单例,所以并不存在线程安全问题。 由于Controller本身是单例模式 (非线程安全的), 这意味着每个request过来,...

    算法之名 ⋅ 今天 ⋅ 0

    知乎Java数据结构

    作者:匿名用户 链接:https://www.zhihu.com/question/35947829/answer/66113038 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 感觉知乎上嘲讽题主简...

    颖伙虫 ⋅ 今天 ⋅ 0

    Confluence 6 恢复一个站点有关使用站点导出为备份的说明

    推荐使用生产备份策略。我们推荐你针对你的生产环境中使用的 Confluence 参考 Production Backup Strategy 页面中的内容进行备份和恢复(这个需要你备份你的数据库和 home 目录)。XML 导出备...

    honeymose ⋅ 今天 ⋅ 0

    JavaScript零基础入门——(九)JavaScript的函数

    JavaScript零基础入门——(九)JavaScript的函数 欢迎回到我们的JavaScript零基础入门,上一节课我们了解了有关JS中数组的相关知识点,不知道大家有没有自己去敲一敲,消化一下?这一节课,...

    JandenMa ⋅ 今天 ⋅ 0

    火狐浏览器各版本下载及插件httprequest

    各版本下载地址:http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/ httprequest插件截至57版本可用

    xiaoge2016 ⋅ 今天 ⋅ 0

    Docker系列教程28-实战:使用Docker Compose运行ELK

    原文:http://www.itmuch.com/docker/28-docker-compose-in-action-elk/,转载请说明出处。 ElasticSearch【存储】 Logtash【日志聚合器】 Kibana【界面】 答案: version: '2'services: ...

    周立_ITMuch ⋅ 今天 ⋅ 0

    使用快嘉sdkg极速搭建接口模拟系统

    在具体项目研发过程中,一旦前后端双方约定好接口,前端和app同事就会希望后台同事可以尽快提供可供对接的接口方便调试,而对后台同事来说定好接口还仅是个开始、设计流程,实现业务逻辑,编...

    fastjrun ⋅ 今天 ⋅ 0

    PXE/KickStart 无人值守安装

    导言 作为中小公司的运维,经常会遇到一些机械式的重复工作,例如:有时公司同时上线几十甚至上百台服务器,而且需要我们在短时间内完成系统安装。 常规的办法有什么? 光盘安装系统 ===> 一...

    kangvcar ⋅ 昨天 ⋅ 0

    没有更多内容

    加载失败,请刷新页面

    加载更多

    下一页

    返回顶部
    顶部