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数据持久化文件读写之偏好设置
很多iOS应用都支持偏好设置,比如保存用户名.密码.字体大小等设置,iOS提供了一套标准的解决方案来为应用加入偏好设置功能.每个应用都有个NSUserDefaults实例,通过它来存取偏好设置.比如, ...
- Android权限(转)
http://www.cnblogs.com/classic/archive/2011/06/20/2085055.html 访问登记属性 android.permission.ACCESS_CHEC ...
- HDU - The number of divisors(约数) about Humble Numbers
Description A number whose only prime factors are 2,3,5 or 7 is called a humble number. The sequence ...
- [深入浅出Windows 10]不同平台设备的适配
2.3 不同平台设备的适配 Windows 10通用应用程序针对特定的平台还会有一个子API的集合,当我们要使用到某个平台的特定API的时候(比如手机相机硬件按钮触发事件),这时候就需要调用特定平台的 ...
- IOS开发常用设计模式
IOS开发常用设计模式 说起设计模式,感觉自己把握不了笔头,所以单拿出iOS开发中的几种常用设计模式谈一下. 单例模式(Singleton) 概念:整个应用或系统只能有该类的一个实例 在iOS开发我们 ...
- 基于SVG的JS地图插件
一:D3(Data-Driven Documents) 官网地址:http://d3js.org/ 功能非常强大(不支持IE8) D3 是最流行的可视化库之一,它被很多其他的表格插件所使用.它允许绑定 ...
- Solr资料
Apache Solr Reference GuideCovering Apache Solr 5.5 https://archive.apache.org/dist/lucene/solr/ref- ...
- php关于金额比较引发的问题(转)
做电子商务的时候一般会涉及到金额的比较,按正常的思路来看用><=这些个符号就可以了.可是要是到程序上来搞这个的话就出大事了.现在看下这段代码: $f = 0.07; var_dump($f ...
- 纪念逝去的岁月——C/C++二分查找
代码 #include <stdio.h> int binarySearch(int iList[], int iNum, int iX, int * pPos) { if(NULL == ...
- nmap的script参数列表
在新的nmap版本中,添加了script功能的使用.在nmap的安装目录的share/nmap/scripts中,已经有将61个写好的脚本提供. 具体的用法可以参考:http://nmap.org/b ...