【C++的探索路9】<<、>>、++、--与 强制类型转换运算符

来源:互联网 发布:php注册短信代码 编辑:程序博客网 时间:2024/06/10 07:57
运算符重载部分所剩不多,本部分将对剩下的一些运算符如流插入\提取,自增,自减以及强制类型转换运算符的重载及运算符重载所需注意的事项进行依次介绍。

流插入\提取运算符重载

I/O的基本原理

声明头文件iostream后,C++中能够使用cin与cout与>>和<<分别配合,实现面向屏幕的输入、输出。
之所以能够实现打印,并不是因为<<或>>具备这种与生俱来的性质,而是与cin,cout配合的结果。
注意iostream这个单词,其可拆分为istream+ostream两个类,而cout为ostream的成员,与<<实现输出;cin为istream成员,与>>结合实现输入。
为了实现多种类型的IO,这两类对象的实现函数重载了多次,比如:
ostream&ostream::operator<<(const char*s) {...return*this;}ostream&ostream::operator<<(int n) {...return*this;}

cout<<"Star War"<<t
在运行过程中就等价于
(cout.operator<<("Star War")).operator<<(5);

Complex对象的IO过程

通过一个例子巩固一下:
功能:
一个Complex的对象c,要求可以键盘输入复数对象后可以执行输出复数对象的功能,并且保持输入输出的原有性质
int main() {Complex c;int n;cin >> c >> n;//cout << c << "," << n;return 0;/*输入13.2+133i 87输出一样的*/}

实现:

第一步:定义Complex类

class Complex {private:double real, imag;public:Complex() :real(0), imag(0) {}};

第二步:实现i功能(i:cin)

主程序第三行为cin>>c>>n;
等价于(cin.operator>>(Complex&c)).operator>>(int i),返回值为istream&,但在类内实现的重载只能在类之间使用,因此我们需要定义作用范围为全局的>>重载,又因为需要调用私有对象,所以我们需要把重载的>>定义为友元
可得基本框架为
class Complex {private:double real, imag;public:Complex() :real(0), imag(0) {}friend istream&operator>>(istream&is, Complex&c);};istream&operator>>(istream&is, Complex&c) {}
----------------------------------------------------------------------------------------------------------

Some of the Tips:

string类下的几个函数
C++中,能够连续输入的字符串类型,除了能为char*外,还可以为string。本例中将使用部分关于string类下面的函数对程序进行辅助操作作用
1, find()
find函数顾名思义就是寻找,找某个东西在哪个位置,将位置进行返回
2, c_str()
函数返回一个指向正规C字符串的指针常量,能将string类转化为const char*的形式
3, substr()
字符串中的子串,需要定义起始位置。
由于这部分还将在后续出现,因此本部分暂时只做了解;本部分的主要目的为:了解>>/<<是怎么进行重载的。
----------------------------------------------------------------------------------------------------------
输入以后要将输入的Complex对象分解赋值给real和imag;
首先定义一个临时的string对象s: string s;
然后就是寻找位置分解:通过find("+",0)确定+的位置在哪里。
接着string sTmp可以作为real部分的转接=s.substr(0,pos);
c.real=atof(sTmp.c_str());
再利用substr可以将剩下的部分传递给imag
程序如下:
istream&operator>>(istream&is, Complex&c) {string s;is >> s;//----找+的位置-----int pos = s.find("+", 0);//----real----string sTmp=s.substr(0,pos);c.real = atof(sTmp.c_str());//---imag---sTmp = s.substr(pos+1,s.length()-pos-2);c.imag = atof(sTmp.c_str());return is;}

第三步:实现o类

其主要思想就是:先将流信息输入string对象s,接着将s进行分割,最后返回对象s。
在istream的影响下,ostream对象可以编写为:
ostream&operator<<(ostream&o, Complex&c) {o << c.real << "+" << c.imag << "i";return o;}
流插入与流提取运算符在运算时要注意流的输入:is/os>>/<<对功能进行实现;而后将对象进行返回。

强制类型转换运算符重载

强制类型转换与运算符

我们在C语言的初期编程中,经常能够听到强制类型转换这个名词;其作用就是类型转换。我们经常通过在变量前面加类型名称的方法进行类型转换,具体例子如下:

double类型的a转为int类型:
int main() {double a = 3.1;cout << a << endl;cout << (int)a << endl;return 0;}

非常interesting的是,下面这么写也能正常运行;这种情况也通用于后续的强制类型转换符重载。
int main() {double a = 3.1;cout << a << endl;cout << int(a) << endl;return 0;}

程序之所以能够运行的原因是C++类型的名字(包括类名)也是一种运算符,从而对变量进行了操作。

强制类型转换运算符重载

强制类型转换运算符既然是运算符,就能进行重载。
其性质有:
1,只能重载为成员函数,不可为全局函数 。2,等价于对象.operator 类型名()的形式。3,单目运算符
通过Complex类的类型转换来说明强制转换运算符重载的过程
class Complex {double real, imag;public:Complex(double r = 0, double i = 0) :real(r), imag(i) {}operator double(){return real;}};int main() {Complex c(1.2, 3.4);cout << double(c) << endl;double n = 2 + c;cout << n;//a.operator-(b)//c.operator double()return 0;}
该程序实现的目的是将Complex的对象转化为double类型,因此double作为运算符进行重载。
在运行过程中double(c)<===>c.operator double(),与之前的运算符重载基本没有什么不同。

请注意,强制类型转换运算符重载的不是(),而是数据类型,比如double,int, Complex etc.
另外一种形式,比较简单粗暴:直接不用double,说明()已经被赋予了强制转换这个神圣的使命,即(c)<===>c.operator double()
class Complex {double real, imag;public:Complex(double r=0,double i=0):real(r),imag(i){}operator double() {return real;}};int main() {Complex c(1.2, 3.4);cout << (c) << endl;return 0;}

重载自增、自减运算符

对有C语言编程基础的亲们来说,++与--是再熟悉不过的运算符了,比如在循环中我们经常通过++i实现i的自增操作。
而在运算过程中这两种运算符又有前置与后置的区别,废话不多说,直接通过程序的实现来对++/--运算符的重载进行实现。

基础知识

前置/后置运算符的重载实现:

前置运算符:

1,作为一元运算符重载
2,可以重载为成员、全局函数
重载为成员函数时:T operator++()
重载为全局函数时:T operator++(T)

后置运算符

1,作为二元运算符(多一个无意义的参数,仅仅用来标记,编译器默认初始化为0值)
2,同样两种形式:成员与全局

需实现程序

int main() {CDemo d(5);cout << (d++) << ",";cout << d << ",";cout << (++d) << ",";cout << d << endl;cout << (d--) << ",";cout << d << ",";cout << (--d) << ",";cout << d << endl;return 0;}

输出结果

5,6,7,7
7,6,5,5

程序分析

主程序中首先定义了CDemo类d,通过构造函数进行内部成员变量的赋值->5。
赋值结束后,程序进入8行的输出行阶段。
在输出行这一段主要有的功能为:自增自减保持原有性质。

补充说明:

为了区分前置与后置区别,C++规定,在重载++或--时,允许多编写一个没用的int型形参。而这个多了一个int的情况是分给后置++、--的。

实现步骤

第一步:定义CDemo类与对应的构造函数
class CDemo {private:int num;public:CDemo(int i=0):num(i){}};

第二步:实现后置自增功能
如上述所述:后置自增功能需要多定义一个废材的形参与前置运算符进行区分,在这里加个int。
定义结束后,进行内部函数体实现:
由于是后置自增,所以需要在完成主程序第三行以后实现成员变量的自增;于是可定义一个tmp的临时变量实现数据的临时存储。
CDemo CDemo::operator++(int) {CDemo tmp(*this);num++;return tmp;}
第三步:强制类型转换运算符的使命
上面程序定义的后置自增运算符,仅仅是实现了变量的自增,而我们没有定义<<运算符的重载,当然也可以定义这个重载,但显得()有点多余。
回顾上面一段:强制运算符重载使得()有了相应类型的神圣使命。
可以编写int的强制类型转换重载运算符。
CDemo::operator int() {return num;}
第四步:编写前置重载运算符++
前置重载运算符事就不像后置那么多了,直接一个成员变量的自增,然后扔一个*this,就可以拍屁股走人了。
CDemo CDemo::operator++() {++num;return*this;}


第五步:编写后置重载运算符--
为了不那么无聊,这里就将--定义为全局形式的重载:比成员对象形式多一个参。
But,道路可能稍微艰辛一些:由于是全局形式的重载,无法直接访问私有变量,需要在类内声明为友军:friend才行。
程序可编写如下:
CDemo operator--(CDemo&de,int) {CDemo tmp(de);de.num--;return tmp;}

第六步:编写前置重载运算符--
依样画葫芦~

程序汇总

最后可以整理得到所有程序如下:
class CDemo {private:int num;public:CDemo(int i=0):num(i){}operator int();CDemo operator++(int);CDemo operator++();friend CDemo operator--(CDemo&de,int);friend CDemo operator--(CDemo&de);};CDemo CDemo::operator++(int) {CDemo tmp(*this);++num;return tmp;}CDemo::operator int() {return num;}CDemo CDemo::operator++() {++num;return*this;}CDemo operator--(CDemo&de,int) {CDemo tmp(de);de.num--;return tmp;}CDemo operator--(CDemo&de) {de.num--;return de;}int main() {CDemo d(5);cout << (d++) << ",";cout << d << ",";cout << (++d) << ",";cout << d << endl;cout << (d--) << ",";cout << d << ",";cout << (--d) << ",";cout << d << endl;return 0;}

运算符重载注意事项


本部分总结