c++ primer plus chapter11 使用类

一个简单的将两个对象相加赋值给第三个对象的例子,第一个版本使用成员函数,第二种版本使用运算符重载

class Test
{
private:
    int i;
public:
    Test(){}
    ~Test(){}
    Test sum(const Test &t) const;
    Test operator+(const Test &t) const;
};

Test Test::sum(const Test &t) const
{
    Test new = i + t.i;
    return new;
}

Test Test::operator+(const Test & t)
{
    Test new = i + t.i;
    return new;
}

/* 调用者 */
Test t1(1);
Test t2(2);
Test t3 = t1 + t2;

使用重载运算符,隐式调用t1的operator函数,传入显式的t2对象,注意返回值不能是引用。

对于如下这种代码

Test t4 = t1 + t2 + t3;

由于加法是从左向右结合的,因此先执行t1.operator+(t2),返回一个对象,然后此对象调用operator+(t3),得到一个最终对象返回给t4,t1 + t2的时候会返回给一个临时对象,在赋值语句结束之后,这个对象就不需要了,会被析构。

重载限制:

1.必须有一个操作数是用户定义类型,这样可以防止给基本类型重载运算符,比如将“-”重载为两个int相加。

2.重载不能违反原来运算符的句法规则,即操作数和运算符的顺序,不能修改运算符的优先级

3.不能创造新的运算符,比如@

4.有些运算符不能被重载,比如sizeof等

5.多数运算符可以通过成员函数或者非成员函数进行重载,但是有4个只能用成员函数重载,=,(),[],->。

通常,能访问类私有对象的只有通过公有方法,友元可以突破这个限制。友元有3种:

1.友元函数

2.友元类

3.友元成员函数

让函数成为类的友元,就是友元函数,可以访问类的私有对象。友元函数可以解决一个问题,就是二元运算符有一个是基本类型,比如重载减号运算符之后,如下代码:

Test t1(1)
Test t2 = t1 * 10;    //valid
Test t2 = 10 * t1;    //invalid

因为重载运算符是成员函数,只能从对象调用,所以不能把10放在前面。使用非成员函数可以把10放在前面,但是函数内部无法访问类的私有成员。友元函数不能通过成员运算符调用。

需要反转操作数顺序的时候,可以用友元内容调用成员函数,这是一个小技巧,不用重复编码,如下:

Test operator*(double m, const Test & t)
{
    return t * m;
}

友元函数声明时要加friend,定义时不加

类的类型转换:只有一个参数的构造函数允许类和基本类型自动转换,例如

class Test
{
private:
    double i;
public:
    Test(double);
    Test(){}
    ~Test(){}
};

Test::Test(double d)
{
    i = d;
}

int main()
{
    Test tmp;
    tmp = 19.6;
    return 1;
}

若此构造函数后面的参数提供了默认值,那么也可以自动转换。编译器的具体操作是,调用此构造函数创建一个临时对象,将19.6作为初始值,然后逐成员赋值给tmp。这个过程是隐式的,称为隐式转换。给构造函数前面加explicit可以禁止自动转换。这个例子中隐式转换的几种情况:

1.将Test对象初始化为double

2.将double赋值给Test对象

3.将double传递给接收Test对象参数的函数

4.返回值被声明为Test的函数返回double

注意一个问题,将int赋值给Test对象的时候,会提升为double,然后赋值,但是不能有二义性,比如另一个构造函数使用一个参数是long,而int可以提升为long或者double,就会二义性。

上面这种转换是从double转换成对象,但是如下代码会报错:

Test tmp(19.6);
double i = tmp;

只有定义转换函数之后,这样的赋值才能成立,转换函数的要求:

1.转换函数必须是类方法

2.转换函数不能指定返回类型

3.转换函数不能有参数

转换函数的书写格式如下:

class Test
{
private:
    double i;
public:
    Test(double d){i = d;};
    ~Test(){}
    operator double() const;
};

Test::operator double() const
{
    return i;
}

/* 用户调用 */
Test t(19.6);
double i = t;    //valid, i is 19.6

c++11允许使用explicit禁止隐式转换函数,但是c++98不支持。应该尽可能少使用隐式转换。

隐式转换和重载加法运算符:如下代码

class Test
{
private:
    double i;
public:
    Test(){}
    Test(double);
    ~Test(){}
    Test operator+(const Test & t) const;
};

Test::Test(double d)
{
    i = d;
}

Test Test::operator+(const Test &t) const
{
    Test sum;
    sum.i = i + t.i;
    return sum;
}

/* 调用者 */
Test t1(19.6);
double tmp = 1.2;
Test t2 = t1 + tmp;    //valid
Test t2 = tmp + t1;    //invalid

第一种赋值可行,因为tmp被隐式转换成Test对象作为函数参数,但是第二种不可行,对一个double调用成员函数是无法被转换为对象的,解决方案有两种,一种是重载加法运算符为友元函数而不是成员函数,另一种方法是定义一个成员函数和一个重载加法运算符的友元,如下:

Test operator+(double n);                         //成员函数
friend Test operator+(double n, const Test &t);   //友元函数

这样每种加法都有严格对应的函数原型,好处是避免了隐式转换,缺点是增加了代码量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值