C++ primer Plus第十一章:使用类

11.1运算符重载

运算符重载就是将参与运算符设置到用户自己定义的数据类型里,使我们自己定义的数据类型可以通过运算符来进行运算。

如 重载加号 我们可以直接使我们的两个对象相加,而不是通过成员函数,将对象内部的成员变量相加。

语法

运算符重载函数有两种,一种是作为成员函数出现在类的声明里,一种是通过友元函数出现在类外。

第一种的格式是这样的

返回类型 operator 运算符(类的引用)

第二种的格式是这样的

返回类型 operator 运算符(x,y)x和y是参与运算的两个算子

举个例子,C++中没有 复数的数据类型,下面我们将自己定义一个复数类型的类,并实现两个复数对象直接相加

#include <iostream>

using namespace std;
class complex
{
private:
    int a,b;
public:
    complex (int aa=0,int bb=0):a(aa),b(bb){}
    void display();
    complex operator +(complex &p);
};
void complex::display()
{
    cout<<a;
    if(b>0)
        cout<<'+';
    cout<<b<<'i'<<endl;
}
complex complex::operator +(complex &p)重载+
{
    complex c;
    c.a=a+p.a;
    c.b=b+p.b;
    return c;
}
int main()
{
    complex x(1,1),y(2,2);
    (x+y).display();
    return 0;
}

实际上编译器发现了x+y 它会将其转换为x.operator+(y).

这样也就存在一个问题 如果x是一个int类型的变量,他不是complex类的对象,那么就不能使用这个成员函数,这种情况下将重载函数作为成员函数就行不通了。

这时候应该使用友元函数

什么是友元函数呢,友元函数就是可以使用类里的私有成员的函数。

例如

#include <iostream>

using namespace std;
class complex
{
private:
    int a,b;
public:
    complex (int aa=0,int bb=0):a(aa),b(bb){}
    void display();
    friend complex operator +(int i,complex &p);//友元函数
};
void complex::display()
{
    cout<<a;
    if(b>0)
        cout<<'+';
    cout<<b<<'i'<<endl;
}
complex operator +(int i,complex &p)//不需要加作用域解析符,因为友元函数不是成员函数
{
    complex c;
    c.a=i+p.a;
    c.b=p.b;
    return c;
}
int main()
{
    complex x(1,1),y(2,2);
    complex z=1+y;
    z.display();
    return 0;
}

通过使用友元函数,就可以实现 一个整型变量加一个对象了。

编译器在发现1+y的时候会将其自动换为 operator+(1,y)

注意事项

友元函数只有在类声明中的原型中才能使用friend关键字。

友元函数不需要使用限定符(::)

11.2.2重载限制

重载不能修改运算符的优先级

有些符号不允许重载如 sizeof   .   ::   ?:等等

不能创建新的运算符 如 不能重载 **作为幂运算 

某些运算符只能通过成员函数来进行重载 如:  =   ()  []  ->

友元函数重载和成员函数重载只能使用一种,不然会发生二义性问题,也就是编译器不知道该用哪一个函数,编译器会报错。

11.3.2常见的友元:重载<<

如何直接使用cout 输出一个复数呢,答案是重载 <<,但是应该重载哪个类呢?

要做到 cout<<a(a是一个类的对象)

肯定用成员函数不行,因为 cout在前,cout属于ostream类

所以重载的是ostream类的<<,但是我们去修改系统的类库很麻烦,而且总不能你写一个类就去改一下ostream,所以还是用友元函数吧。

#include <iostream>

using namespace std;
class complex
{
private:
    int a,b;
public:
    complex (int aa=0,int bb=0):a(aa),b(bb){}
    void display();
    complex operator +(complex &p);
    //由于我们包含了头文件iostream,所以就可以用到ostream类
    friend ostream &operator <<(ostream &os,complex &p);
};
void complex::display()
{
    cout<<a;
    if(b>0)
        cout<<'+';
    cout<<b<<'i'<<endl;
}
complex complex::operator +(complex &p)
{
    complex c;
    c.a=a+p.a;
    c.b=b+p.b;
    return c;
}
ostream& operator<<(ostream &os,complex &p)
{
    os<<p.a;
    if(p.b>0)
        os<<'+';
    os<<p.b<<'j'<<endl;
    return os;
}
int main()
{
    complex x(1,1),y(2,2);
    complex z=x+y;
    cout<<z;
    return 0;
}

为什么返回类型是ostream&呢,因为 你输出的时候可能是cout<<a<<b;

cout<<a 返回类型是一个ostream的对象,cout<<a会被解释为 operator<<(cout,a),它的返回值是一个ostream类的引用,于是可以继续进行运算,相当于(operator<<(cout,a))<<b。

为什么输出用os而不是cout呢

是因为我们不一定使用cout<<a 也有可能是fout<<a 将a输出到文件里。

11.6类的自动转换和强制类型转换

C++可以对内置类型进行自动转换,对类也可以进行转换。

如我们可以让一个类的对象等于一个整型值如:

#include <iostream>
#include <fstream>
using namespace std;
class fun
{
    int a,b;
public:
    fun(int aa=0,int bb=0):a(aa),b(bb){}
    void display();
};
void fun::display()
{
    cout<<a<<" "<<b<<endl;
}
int main()
{
    fun A;
    A=1;//把一个整型变量赋给对象A
    A.display();
    return 0;
}

输出结果为

从这个例子我们可以看出,如果一个类的构造函数有一个参数,或者有多个参数但都提供了默认的参数,就可以实现这种类型转换。

具体的原理就是程序自动地执行了构造函数,创建了一个临时无名对象,并且把这个对象赋值给等式左边的对象。

考虑到C++还可以对内置数据类型进行自动转换,还可以把构造函数改成这样

fun(double aa,int bb=0):a(aa),b(bb){}

原理就是,编译器首先会查找参数为int类型的构造函数,找不到,编译器发现了参数为double类型的参数,然后编译器发现int转为double类型是可行的,所以编译器就将int转为了double类型然后调用了这个构造函数。

但这种将构造函数作为自动转换函数作用并不都是好的,C++提供了explicit来关闭构造函数的这个自动转换的功能。

例如 explicit fun(double aa,int bb=0):a(aa),b(bb){}

这样 如果使用 A=1 就会报错。

11.6.1转换函数

上面我们讲了将数字转换为类的对象,那么反过来将类的对象转换为数字行吗?

可以这么做,但是必须要使用转换函数

转换函数格式

operator typeName()typeName是类型的名字

转换函数必须是类方法,不能指定返回类型,不能有参数。

举个例子

#include <iostream>
#include <fstream>
using namespace std;
class fun
{
    int a;
public:
    fun(double aa):a(aa){}
    operator int();//将fun类转换为int类型的转换函数
    void display();
};
void fun::display()
{
    cout<<a<<endl;
}
fun::operator int()
{
    return a;
}
int main()
{
    fun A(0);
    int x=A;//编译器查找是否定义了相应的转换函数,若有则执行,无则报错
    cout<<x<<endl;
    return 0;
}

同样的也存在一个基本类型自动转换的问题,如果你定义了一个double类型转换函数,但是你却将一个对象赋值给了int类型,编译器会使用转换函数,将其转换为double类型,然后在赋给int类型。

发布了37 篇原创文章 · 获赞 3 · 访问量 2379

猜你喜欢

转载自blog.csdn.net/Stillboring/article/details/105265567