运算符重载二

1.输入输出流对象

重载 << 和>>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}
ostream & operator<<(ostream &os){
os << _real << "+" << _image << "i" <<endl;
return os;
}


private:
int _real;
int _image;
};

void test(){
Complex cx(1,2);
cout << cx;//先不考虑endl先考虑前半部分,这样写发现报错了
// 本质是cx.operator<<(cout);
//报错是因为根据本质是 cx << cout 才是重载的形式,由于this指针导致参数的第一位必然是cx,所以成员函数不可行,只能用友元
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

friend
ostream & operator<<(ostream &os,const Complex &rhs);

private:
int _real;
int _image;
};
//使用友元函数重载
ostream & operator<<(ostream &os,const Complex &rhs){
os << rhs._real << "+" << rhs._image << "i";
return os;
}

void test(){
Complex cx(1,2);
cout << cx << endl;
}

重载输入>>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

friend
istream & operator>>(istream &is,Complex &rhs);

private:
int _real;
int _image;
};
//使用友元函数重载,但是不够完善,如果输入不合法的数据会出现一些问题
istream & operator>>(istream &os,Complex &rhs){
is >> rhs._real;
is >> rhs._image;
return is;
}

void test(){
Complex cx(1,2);
cout << cx << endl;
}

改善>>的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void input(istream &is, int &num){
while(is >> num,!is.eof()){
if(is.bad()){
cout << "istream status is bad" <<endl;
return;
}else if(is.fail()){
is.clear();
is.ignore(numeric_limits<streamsize>::max(),'\n');
}
}
}

class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

friend
istream & operator>>(istream &is,Complex &rhs);

private:
int _real;
int _image;
};
//使用友元函数重载,但是不够完善,如果输入不合法的数据会出现一些问题
istream & operator>>(istream &is,Complex &rhs){
input(is,rhs._real);
input(is,rhs._image;
return is;
}

void test(){
Complex cx(1,2);
cout << cx << endl;
}

成员访问运算符的重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Data
{
public:
Data(){}
~Data(){}

int getData() const{ return _data; }
private:
int _data = 10;
};

class MiddleLayer
{
public:
MiddleLayer(Data * p)
: _pdata(p)
{}

~MiddleLayer(){
if(_pdata){
delete _pdata;
_pdata = nullptr;
}
}
private:
Data * _pdata;
};


Data*原生指针的用法如下,需要关注堆空间资源的回收

1
2
3
4
5
Data * p = new Data();
p->getData();
(*p).getData();
delete p;
p = nullptr;

如果用这种方式创建MiddleLayer对象,我们发现不需要手动delet pdata,并没有发生内存泄露,反而手动delet pdata后会有double free的问题

需求:希望实现一个这样的效果,创建MiddleLayer对象ml,让ml对象可以使用箭头运算符去调用Data类的成员函数getData

1
2
MiddleLayer ml(new Data);
cout << ml->getData() << endl;

箭头运算符无法应对MiddleLayer对象,那么可以定义箭头运算符重载函数。

  • 首先不用考虑重载形式,箭头运算符必须以成员函数形式重载;
  • 然后考虑返回类型,返回值需要使用箭头运算符调用getData函数,而原生的用法只有Data* 才能这么用,所以返回值应该是一个Data* ,此时应该直接返回 _pdata;
1
2
3
Data* operator->(){
return _pdata;
}

重载解引用运算符*

三层结构下的成员访问运算符的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
class Data
{
public:
Data(){}
~Data(){}

int getData() const{ return _data; }
private:
int _data = 10;
};

class MiddleLayer
{
public:
MiddleLayer(Data * p)
: _pdata(p)
{}

~MiddleLayer(){
if(_pdata){
delete _pdata;
_pdata = nullptr;
}
}
friend ThirdLayer;
private:
Data * _pdata;
};

class ThirdLayer
{
public:
ThirdLayer(MiddleLayer *pml)
:_pml(pml)
{}

~ThirdLayer(){
if(_pml){
delete _pml;
_pml = nullptr;
}
}

MiddleLayer &operator->(){
return *_pml;
}

//目标一
/* MiddleLayer &operator*(){
return *_pml;
}*/
//目标二
Data & operator*(){
//利用MiddleLayer类中已经对*进行了重载作为铺垫
return *(*_pml);//法1
return *(*_pml)._pdata;//需要声明友元
}
private;
MiddleLayer * _pml;
};



void test(){
//错误的创建方式,让他对象接管了栈上的MiddleLayer对象
Data *p1 = new Data();
MiddlerLayer ml(p1);
ThirdLayer tl(&ml);//这里pml指向栈上的区间了
}

void test1(){
//这样就让middlelayer托管堆上的data对象 thirdLayer托管堆上的middlayer对象了 即thirdlayer(栈上)-> middlayer -> data
ThirdLayer tl(new Middlayer(new Data()));
//需求 直接实现 tl->getData();
//本质
((tl.operator->()).operator->)->getData();
(tl.operator->())->getData();
tl->getData();
//后面的箭头编译器会自己添加
//别忘记MiddlerLayer的->也重载过,其实就是套娃的过程


//解引用
//目标一
//一层一层的解引用
(*(*tl)).getData();
//本质
((tl.operator*()).operator*()).getData();
//目标二
//一部到位解引用
(*tl).getData();
}

可调用实体

普通函数和函数指针,成员函数他们都成为可调用实体

函数对象

重载了函数调用运算符的类的对象称为函数对象,该对象即可像普通对象一样调用也可以像普通函数一样调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class FunctionObject{
public:
//第一个()是函数名 第二个()是参数列表
void operator()(){
cout << "operator()()" << endl;
}

//函数重载
void operator()(int x){
cout <<"operator()(int)" << x << endl;
}
};


void test0(){
FunctionObject fo;
fo();//这个括号就是函数调用运算符,想让对象像函数一样被调用
//本质
fo.operator()();

//需求 如果要统计莫i一个函数被调用了几次,那么定义全局变量是不安全的,使用静态变量同样 所以希望类像函数一样被调用,让调用次数作为类的属性即可

//相比于普通函数,函数对象可以携带状态
}

函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void print(int x){
cout << "print(int):"<< x << endl;
}

void display(int x){
cout << "dispaly(inx):"<< x << endl;
}
void show(){
cout << "show()" << endl;
}

//给void(*)(int) 这种类型的指针变量总结出一种类型名称
typedef void (*Function)(int);


void test0(){
//定义函数指针时确定其指向的函数的返回类型与参数信息

//省略信息
void (*p1)(int) = print;
p1(3);
p1 = dispaly;
p1(4);
//p1 = show() error

//完整形式
void (*p2)(int) = &print;
(*p2)(5);
p2 = &dispaly;
(*p2)(6);
//p1 与 p2的类型时一直的


//本质上其实就是 void (*)(int)类型,但是只能作为逻辑类型,不能在代码中直接使用
//可以使用typedef来取别名
//这个时候就可以使用Function来指代void(*)(int)了
Function f1 = print;
f1(10);
f1 = &display;
(*f1)(20);

//f1 = show;//error
}

成员函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class FFF{
public:
void print(int x){
cout << "print(int):"<< x << endl;
}

void display(int x){
cout << "dispaly(inx):"<< x << endl;
}
static void show(int x){
cout <<"show(int x)" << x << endl;
}
};

typedef void (*Function)(int);

void print(int x){
cout << "print(int):"<< x << endl;
}

void display(int x){
cout << "dispaly(inx):"<< x << endl;
}
void show(){
cout << "show()" << endl;
}

void test(){
Function f;
f = FFF::print;//error 不能指向 说是不能指向非静态的成员函数,主要是因为函数指针没有包含类的信息
f = FFF::show;//发现可以了,但是静态成员函数很特殊,被所有对象共享
}
typedef void(FFF::*MemFunction)(int)
void test1(){
//定义一个成员函数指针时要确定属于哪个类,函数返回类型参数信息
void(FFF::*p1)(int) = &FFF:print;//需要包含上类的信息,在前面也加上作用域限定符,必须是完整的形式
//要创建对象才能使用
FFF fff;
fff.p(10);//error
//上面定义是完整形式,下面也必须用完整形式
//.*运算符,是成员指针运算符的第一种形式
(fff.*p)(10);


MemFunction p3;
p3 = &FFF::display;
(fff.*p3)(19);

FFF *fp = new FFF();
fp->print(10);//成员函数 的调用
(fp->*p3)(20);//成员指针运算符 的第二种形式
}

类型转化函数

由其他类型向自定义类型转化

由其他类型向自定义类型转换是由构造函数来实现的,只有当类中定义了合适的构造函数时,转换才能通过。这种转换,一般称为隐式转换

之前我们见识了隐式转换,当时的例子中能够进行隐式转换的前提是Point类中有相应的构造函数,编译器会看用一个int型数据能否创建出一个Point对象,如果可以,就创建出一个临时对象,并将它的值复制给pt

1
2
Point pt = 1;
//等价于Point pt = Point(1);

这种隐式转换是比较奇怪的,一般情况下,不希望这种转换成立,所以可以在相应的构造函数之前加上explicit关键字,禁止这种隐式转换。

而有些隐式转换使用起来很自然,比如:

1
2
string s1("hello,world");
string s1 = "hello,world";

这行语句其实也是隐式转换,利用C风格字符串构造一个临时的string对象,再调用string的拷贝构造函数创建s1

由自定义类型向其他类型转化

类型转换函数 operator 目标类型(){}

它有着如下的特征:

1.必须是成员函数

2.没有返回类型

3.没有参数

4.在函数执行体中必须要返回目标类型的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Point{
public:
Point(int x = 0,int y = 0)
:_ix(x)
,_iy(y)
{}

void print() const{
cout << "(" << _ix << "," << _iy <<endl;
}

//类型转化函数
operator int(){
return _ix +_iy;//自定义转化逻辑
}

private:
int _ix;
int _iy;

};

void test0(){
//隐式转化呢,相当于 Point pt = Point(1)
Point pt = 1;
pt.print();

//需要让Point对象转换为int类型数据
int num = pt;

//等价于
int num = pt.operator int();

}

运算符重载的优先级比隐式转换的优先级高一些

自定义类型转化为另一种自定义类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Point{
public:
Point(int x = 0,int y = 0)
:_ix(x)
,_iy(y)
{}

void print() const{
cout << "(" << _ix << "," << _iy <<endl;
}



private:
int _ix;
int _iy;

};
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

//类型转化函数
operator Point(){
return Point(_real,_image);//自定义转化逻辑,这里还发生了拷贝构造,返回的肯定不是临时对象本体
}

private:
int _real;
int _image;
};


void test(){
Point pt(1,2);
Complex cx(7,9);
//让complex类对象cx转化为另一个point对象
pt = cx;
pt.print();
}

提供特殊的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Complex;//前向声明
class Point{
public:
Point(int x = 0,int y = 0)
:_ix(x)
,_iy(y)
{}

Point(const Complex &rhs);

void print() const{
cout << "(" << _ix << "," << _iy <<endl;
}



private:
int _ix;
int _iy;

};
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

friend Point;


private:
int _real;
int _image;
};
Point::Point(const Complex &rhs)
:_ix(rhs._real)
,_iy(rhs._image)
{}

void test(){
Point pt(1,2);
Complex cx(7,9);
//让complex类对象cx转化为另一个point对象
pt = cx;
//上面两种方法都放开的话就走第一种
pt.print();
}

重载赋值运算符=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Complex;//前向声明
class Point{
public:
Point(int x = 0,int y = 0)
:_ix(x)
,_iy(y)
{}

Point(const Complex &rhs);

void print() const{
cout << "(" << _ix << "," << _iy <<endl;
}



private:
int _ix;
int _iy;

};
class Complex{
Complex(int real,int image)
:_real(real)
,_image(image)
{}

friend Point;
Point &operator=(const Complex &rhs);

private:
int _real;
int _image;
};
Point &Point::operator=(const Complex &rhs){
_ix = rhs._real;
_iy = rhs._image;
return *this;
}

void test(){
Point pt(1,2);
Complex cx(7,9);
//让complex类对象cx转化为另一个point对象
pt = cx;
//上面三种方法都开放的话,走赋值的这个方法
pt.print();
}

总结:使用类型转换函数(Complex的成员函数operator Point)和特殊的构造函数(Point的构造函数)都可以实现上述的需求。

当两种方式并存时,会优先调用类型转换函数。
还可以给Point类添加特殊的赋值运算符函数,当三种方法同时存在时,会优先调用赋值运算符函数(赋值操作本身就支持),其次类型转化的优先级高于隐式转化。