C++程序设计(三)
1. 运算符重载
目的:对抽象数据类型也能够直接使用C++提供的运算符。使得程序更简洁,代码更容易理解。
运算符重载的实质是函数重载
返回值类型 operator 运算符(形参表) { …… }
运算符可以被重载为普通函数(友元函数),也可以被重载为类的成员函数
// 运算符重载为普通函数(友元函数) class Complex { public: Complex(double r = 0.0, double i = 0.0){ real = r; imaginary = i; } friend Complex operator+ (const Complex & a, const Complex & b); private: double real; // real part double imaginary; // imaginary part }; Complex operator+ (const Complex & a, const Complex & b) { return Complex(a.real + b.real, a.imaginary + b.imaginary); } // “类名(参数表)” 就代表一个对象 Complex a(, ), b(, ), c; c = a + b;
重载为普通函数时, 参数个数为运算符目数.
// 运算符重载为成员函数 class Complex2 { public: Complex2(double r = 0.0, double m = 0.0) : real(r), imaginary(m) { } // constructor Complex2 operator+ (const Complex2 &); // addition Complex2 operator- (const Complex2 &); // subtraction private: double real; // real part double imaginary; // imaginary part }; // Overloaded addition operator Complex2 Complex2::operator+(const Complex2 & operand2) { return Complex2(real + operand2.real, imaginary + operand2.imaginary); } // Overloaded subtraction operator Complex2 Complex2::operator- (const Complex2 & operand2){ return Complex2(real - operand2.real, imaginary - operand2.imaginary); } Complex2 a(, ), b(, ), c; c = a + b; c = b - a;
重载为成员函数时, 参数个数为运算符目数减一
- 一般情况下,单目运算符最好重载为类的成员函数,双目运算符则最好重载为类的友元函数;
- 双目运算符"=、()、[]、->"不能重载为类的成员函数。
- 类型转换函数只能定义为一个类的成员函数。 C++提供4个类型转换函数:reinterpret_cast(在编译期间实现转换)、const_cast(在编译期间实现转换)、stactic_cast(在编译期间实现转换)、dynamic_cast(在运行期间实现转换,并可以返回转换成功与否的标志)。
- 若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
- 若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。
- 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。
- 当需要重载运算符具有可交换性时,选择重载为友元函数。
- 除了类属关系运算符”.“、成员指针运算符”.*“、作用域运算符”::“、sizeof运算符和三目运算符”?:“以外,C++中的所有运算符都可以重载。
- 重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
- 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
- 重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
- 运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
- 运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
以赋值运算符重载为例
class String{ private: char *str; public: String() : str(NULL){} const char * c_str(){ return str; } char * operator=(const char *s); ~String(); }; char * String::operator = (const char * s){ if (str) delete[] str; if (s) { //s不为NULL才会执行拷贝 str = ]; strcpy(str, s); } else str = NULL; return str; } String::~String(){ if (str) delete[] str; }; int main(){ String s; s = "Good Luck"; cout << s.c_str() << endl; //String s2 = "hello!"; //这条语句要是不注释掉就会出错 s = "Shenzhou8!"; cout << s.c_str() << endl; ; }
这里注意一点:非const 变量不能赋值给const,const指针不能赋值给非const指针。
浅复制和深复制
浅复制:执行逐个字节的复制工作,默认复制构造函数,会导致复制结果为两个都指向同一片内存。
深复制:将一个对象中指针变量指向的内容,复制到另一个对象中指针成员对象指向的地方
String & operator = (const String & s) { if(str) delete [] str; str= ]; strcpy(str, s.str); return * this; }
为避免自身复制中出现的问题
String & String::operator = (const String & s){ if(str== s.str)return * this; if(str) delete [] str; if(s.str) {//s.str不为NULL才会执行拷贝 str= ]; strcpy( str,s.str); } else str= NULL; return * this; }
2. 可变长数组
int main() { //要编写可变长整型数组类,使之能如下使用: CArray a; //开始里的数组是空的 ; i < ; ++i) a.push_back(i); CArray a2, a3; // 要用动态分配的内存来存放数组元素,需要一个指针成员变量 a2 = a; // 要重载“=” ; i < a.length(); ++i) cout << a2[i] << " "; // 要重载“[ ]” a2 = a3; //a2是空的 ; i < a2.length(); ++i) //a2.length()返回0 cout << a2[i] << " "; cout << endl; a[] = ; CArray a4(a); // 要自己写复制构造函数 ; i < a4.length(); ++i) cout << a4[i] << " "; ; }
class CArray { int size; //数组元素的个数 int *ptr; //指向动态分配的数组 public: CArray(); //s代表数组元素的个数 CArray(CArray & a); ~CArray(); void push_back(int v); //用于在数组尾部添加一个元素v CArray & operator=(const CArray & a); //用于数组对象间的赋值 int length() { return size; } //返回数组元素个数 int& CArray::operator[](int i) //要实现a[i] = 4,只有返回值为引用类型的函数才可以作为左值使用 {//用以支持根据下标访问数组元素, // 如n = a[i] 和a[i] = 4; 这样的语句 return ptr[i]; } }; CArray::CArray(int s) :size(s) { ) ptr = NULL; else ptr = new int[s]; } CArray::CArray(CArray & a) {// 深复制 if (!a.ptr) { ptr = NULL; size = ; return; } ptr = new int[a.size]; memcpy(ptr, a.ptr, sizeof(int) * a.size); size = a.size; } CArray::~CArray() { if (ptr) delete[] ptr; } CArray & CArray::operator=(const CArray & a) { //赋值号的作用是使“=”左边对象里存放的数组,大小和内容都和右边的对象一样 if (ptr == a.ptr) //防止a=a这样的赋值导致出错 return *this; if (a.ptr == NULL) { //如果a里面的数组是空的 if (ptr) delete[] ptr; ptr = NULL; size = ; return *this; } if (size < a.size) { //如果原有空间够大,就不用分配新的空间 if (ptr) delete[] ptr; ptr = new int[a.size]; } memcpy(ptr, a.ptr, sizeof(int)*a.size); size = a.size; return *this; } // CArray & CArray::operator=( const CArray & a) void CArray::push_back(int v) { //在数组尾部添加一个元素 if (ptr) { ]; //重新分配空间 memcpy(tmpPtr, ptr, sizeof(int)*size); //拷贝原数组内容 delete[] ptr; ptr = tmpPtr; } else //数组本来是空的 ptr = ]; ptr[size++] = v; //加入新的数组元素 }
3. 流插入运算符和流提取运算符的重载
cout 是在 iostream 中定义的,ostream 类的对象。
cin 是在 iostream 中定义的,istream 类的对象。
class CStudent{ public: int nAge; }; ostream & operator<<( ostream & o,const CStudent & s){ o << s.nAge ; return o; } int main(){ CStudent s ; s.nAge = ; cout << s <<"hello"; ; }
4. 自加/自减运算符的重载
自加++/自减--运算符有前置/后置之分
前置运算符作为一元运算符重载
重载为成员函数:
T &operator++();
T &operator--();
重载为全局函数:
T &operator++(T &);
T &operator—(T &);
后置运算符作为二元运算符重载
重载为成员函数:
T operator++(int);
T operator--(int);
重载为全局函数:
T operator++(T &, int);
T operator--(T &, int);
int main(){ CDemo d(); cout << (d++) << ","; //等价于d.operator++(0); cout << d << ","; cout << (++d) << ","; //等价于d.operator++(); cout << d << endl; cout << (d--) << ","; //等价于operator--(d,0); cout << d << ","; cout << (--d) << ","; //等价于operator--(d); cout << d << endl; ; }
class CDemo{ private: int n; public: CDemo() :n(i) { } CDemo& operator++(); //用于前置++形式 CDemo operator++(int); //用于后置++形式 operator int() { return n; } friend CDemo& operator--(CDemo&); //用于前置--形式 friend CDemo operator--(CDemo&, int); //用于后置--形式
operator int() { return n; }// 类型强制转换运算符,不能写返回值类型 }; CDemo& CDemo::operator++() { //前置++ n++; return *this; } CDemo CDemo::operator++(int k) { //后置++ CDemo tmp(*this); //记录修改前的对象 n++; return tmp; //返回修改前的对象 } CDemo& operator--(CDemo& d) { //前置-- d.n--; return d; } CDemo operator--(CDemo& d, int) { //后置-- CDemo tmp(d); d.n--; return tmp; }
C++程序设计(三)的更多相关文章
- CUDA程序设计(三)
算法设计:基数排序 CUDA程序里应当尽量避免递归,因而在迭代排序算法里,基数排序通常作为首选. 1.1 串行算法实现 十进制位的基数排序需要考虑数位对齐问题,比较麻烦.通常实现的是二进制位的基数排序 ...
- JavaScript高级程序设计(三):基本概念:数据类型
特别注意:ECMAScript是区分大小写的. 一.变量 1.ECMAScript的变量是松散型的.所谓松散型就是可以用来保存任何类型的数据.即每个变量仅仅是一个用于保存值的占位符而已.定义变量时要使 ...
- javascript 高级程序设计 三
Sorry,前两张介绍的主题还是JavaScript,而第一章介绍了JavaScript和ECMAScript区别,所以前两章介绍的主题应该改为ECMAScript,但是 标题就不改了因为现在人们习惯 ...
- 20145308刘昊阳 《Java程序设计》第2周学习总结
20145308刘昊阳 <Java程序设计>第2周学习总结 教材学习内容总结 第三章 基础语法 3.1 类型.变量与运算符 类型 基本类型 整数(short/int/long) short ...
- 2018-2019-2 20175227张雪莹《Java程序设计》 实验二《Java面向对象程序设计》
2018-2019-2 20175227张雪莹<Java程序设计> 实验二<Java面向对象程序设计> 实验报告封面 课程:Java程序设计 班级:1752班 姓名:张雪莹 学 ...
- 054 Python程序设计思维
目录 一.单元开篇 二.计算思维与程序设计 2.1 计算思维 2.1.1 第3种人类思维特征 2.1.2 抽象和自动化 2.1.3 计数求和:计算1-100的计数和 2.1.4 圆周率的计算 2.1. ...
- 【C语言】第5章 循环结构程序设计
第5章 循环结构程序设计 三种基本循环控制结构 使用while语句实现循环 先判断条件表达式,后执行循环体语句 while (循环条件表达式) { 循环体 } 用do-while语句实现循环 先无条件 ...
- web前端开发必懂之一:JS继承和继承基础总结
首先,推荐一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的链接全是精华, 一般人我不告诉他; 我们会先从JS的基本的设计模 ...
- 自学java坎坷之路——20155312张竞予
20155312 2006-2007-2 <Java程序设计>第一周学习总结 教材学习内容总结 第一周并没有在课堂上对教材内容进行学习,学习内容概括如下 课程分数构成,其中包括课堂测验(每 ...
- Java学习笔记之面向对象、static关键字
一周Java学习总结 今天就总结理清一下关于面向对象和面向过程的程序设计的一些不同特点,以及讲下static关键字. 面向对象 现在接触的Java是面向对象的,现在的程序开发几乎都是以面向对象为基础的 ...
随机推荐
- Javascript中大括号“{}”的多义性
摘要:本文主要介绍JavaScript中大括号有四种语义作用. JS中大括号有四种语义作用 语义1,组织复合语句,这是最常见的 if( condition ) { //... }else { //.. ...
- Codeforces Round #196 (Div. 2) B. Routine Problem
screen 尺寸为a:b video 尺寸为 c:d 如果a == c 则 面积比为 cd/ab=ad/cb (ad < cb) 如果b == d 则 面积比为 cd/ab=cb/ad (c ...
- 关于scrollbar-face-color只支持ie的解决办法!
关于scrollbar-face-color只支持ie的解决方法!!今天突然有人问我滚动条css自定义的方法,我发现用scrollbar-base-color这种方法只有ie支持,查了半天资料总结如下 ...
- cocos2dx中设置横竖版
IOS目录中RootViewController.mm//显示竖屏- (BOOL) shouldAutorotate { return NO;} //显示横屏- (BOOL) shouldAutoro ...
- 全浏览器收藏网站javascript
function MyFavorite(sURL, sTitle) { var ctrl = (navigator.userAgent.toLowerCase()).indexOf('mac') != ...
- [CareerCup] 18.11 Maximum Subsquare 最大子方形
18.11 Imagine you have a square matrix, where each cell (pixel) is either black or white. Design an ...
- Hive_进阶
回顾: hive 优点 1. 类sql语句靠近关系型数据库,可自定义函数,增加了扩展性,易于开发,减少mapreduce学习成本 2. hive转换sql语句为mapreduce程序以mapreduc ...
- Linux_文件及文件夹[创建][复制][移动][删除][重命名]
一.文件/文件夹创建 1.文件的创建 touch , vi/vim/nano , ... 语 法: touch [-acfm][-d <日期时间>][-r <参考文件或目 录&g ...
- Odoo 路线规则实现机制浅析
事情是这个样子的:项目在实施过程中,碰到A仓库向B仓库供货的情况,心想这还不简单,老老实实地建多个仓库并将B仓库的供货仓库选为A仓库,再设置好产品的再订购规则,万事大吉了.然而,事情并非想象的那么简单 ...
- jq制作博客已存在多少天
function current(){ var d=new Date(),str=''; var date=((d.getMonth()+1)*30+(d.getFullYear())*365+d.g ...