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是面向对象的,现在的程序开发几乎都是以面向对象为基础的 ...
随机推荐
- 【原】iOS学习之XMPP环境搭建
XMPP环境搭建 1> 搭建XMPP环境需要几个辅助工具: Java Openfire 采用Java开发,因此我们需要先安装Java环境 XAMPP XAMPP(Apache+MySQL+PHP ...
- ACM: POJ 3259 Wormholes - SPFA负环判定
POJ 3259 Wormholes Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu ...
- jQuery自定义滚动条样式插件mCustomScrollbar
如果你构建一个很有特色和创意的网页,那么肯定希望定义网页中的滚动条样式,这方面的 jQuery 插件比较不错的,有两个:jScrollPane 和 mCustomScrollbar. 关于 jScro ...
- [深入浅出Windows 10]模拟实现微信的彩蛋动画
9.7 模拟实现微信的彩蛋动画 大家在玩微信的时候有没有发现节日的时候发一些节日问候语句如“情人节快乐”,这时候会出现很多爱心形状从屏幕上面飘落下来,我们这小节就是要模拟实现这样的一种动画效果.可能微 ...
- BZOJ 1034 题解
1034: [ZJOI2008]泡泡堂BNB Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2613 Solved: 1334[Submit][St ...
- 解决clang: error: no such file or directory: such file or directory:的问题
一,详细问题描述 clang: error: no such file or directory: 'xxx/src/GGBaCollectionViewCell.m' clang: error: n ...
- 理解钩子Hook以及在Thinkphp下利用钩子使用行为扩展
什么是钩子函数 个人理解:钩子就像一个”陷阱”.”监听器”,当A发送一个消息到B时,当消息还未到达目的地B时,被钩子拦截调出一部分代码做处理,这部分代码也叫钩子函数或者回调函数 参考网上说法 譬如我们 ...
- GridView合并表头、多重表头(转)
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e) { switch (e.Row.RowType) ...
- Erlang及Rabbitmq安装
1. 下载erlang源代码及RabbitMQ rpm安装包 $ wget http://www.erlang.org/download/otp_src_R16B02.tar.gz $ wg ...
- jquery中prop()方法和attr()方法的区别浅析
官方例举的例子感觉和attr()差不多,也不知道有什么区别,既然有了prop()这个新方法,不可能没用吧,那什么时候该用attr(),什么时候该用prop()呢 jquery1.6中新加了一个方法pr ...