• 条款24:若所有参数皆需类型转换,请为此采用non-member函数


    1、问题引出

    通常,令class支持隐式转换是不好的设计。但是也有例外,最常见的例外是在建立数值类型时。例如设计一个类表示有理数时,允许整数隐式转换为有理数是合理的。

    class Rational{
    public:
        Rational(int numerator = 0, int denominator = 1); //刻意不为explicit;允许int-to-Rational隐式转换
        int numerator()const;
        int denominator()const;
    };
    

    我需要为有理数类实现加法、乘法等,是该写成member函数、non-member、non-friend函数还是non-member、friend函数呢?

    答案是:non-member、non-friend函数。

    2、写成member函数引发的问题

    写成member函数形式如下:

    class Rational
    {
    public:
        Rational(int numerator = 0,int denominator = 1) : x(numerator), y(denominator){}
        //构造函数不为explicit,允许int-to-Rational隐式转换 
        int numerator() const; //分子
        int denominator() const; //分母
        const Rational operator*(const Rational& rhs) const //有理数的乘法
        {
        	return Rational(x*rhs.x, y*rhs.y);
        }
    private:
    	int x;
    	int y;
    };
    
    (1)此时执行普通的运算是没问题的:
    	Rational oneEight(1, 8);
    	Rational oneHalf(1, 2);
    	Rational result = onHalf*oneEight;
    	result = result*oneEight;
    
    (2)但是执行混合运算时却会发生错误:
    	result = oneHalf * 2; //很好
    	result = 2 * oneHalf; //错误
    
    (3)第二句为什么会引发错误呢?
    	result = oneHalf.operator*(2);
    	result = 2.operator*(oneHalf);
    

    首先,oneHalf确实可以作为operator* 的参数,但是问题是编译器找不到2 所属的类的operator* 的成员方法,所以就会报错。

    (4)第一句为什么能够通过编译呢?

    答案就是发生了隐式转换,首先oneHalf有成员方法operator* ,它需要一个参数Rational 类对象,但是传递的参数确是一个int 型的2,于是编译器开始查找Rational 的类型转换构造函数,看能否把2转换成Rational 类对象,它找到了,于是产生了一个临时的对象,然后将这个对象传递给了operator*,因此编译通过。这便是所谓的隐式转换。

    (5)为什么第二句的参数2不能发生隐式转换呢?

    只有当参数被列于参数列内,而不是隐喻参数this时,这个参数才是隐式转换的合格参与者。隐喻参数this不是隐式转换的合格参与者。

    如果将类型转换构造函数写成non-explicit 的话,就达成了二者的一致性,即:都不能通过编译。

    显然我们的有理数类是应该支持混合运算的,那么我们的设计就需要改进。

    将operator* 写成non-member、non-friend函数

    class Rational
    {
    public:
    	Rational(int numerator = 0, int denominator = 1) : x(numerator), y(denominator) {}
    	//构造函数不为explicit,允许int-to-Rational隐式转换 
    	int numerator() const //分子
    	{
    		return x;
    	}
    	int denominator() const //分母
    	{
    		return y;
    	}
    private:
    	int x;
    	int y;
    };
    const Rational operator*(const Rational& lhs, const Rational& rhs)
    {
    	return Rational(lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator());
    }
    

    3、为什么写成non-friend的,而不写成friend的?

    (1)误解

    首先,澄清一个误解。member函数的反面不一定是non-member、friend的。而是non-member、non-friend的。

    (2)原则

    无论何时,我们都应该避免non-member函数写成friend的。当然,也有必须写成friend的场景,friend是有用的。

    4、本条款注意点

    当进入泛型编程时,本条款可能就不太适用了。

  • 相关阅读:
    在SharePoint 2010中使用jQuery
    SharePoint 2010整合Silverlight 4应用 任务管理
    在LINQ to SharePoint中使用创建时间,创建者,修改时间,修改者
    修改SharePoint页面上的控件数量的限制
    SharePoint 2010开发工具图解系列:PowerShell脚本
    SharePoint 2010:部署.resx(资源)文件到App_GlobalResources的简单方法
    使用[本人]创建视图筛选时的一个问题和解答
    在SharePoint 2010环境下区分w3wp进程
    通过PowerShell实现SharePoint列表增删改
    SharePoint 2010开发工具图解系列:Visual Studio 2010创建事件接收器
  • 原文地址:https://www.cnblogs.com/lasnitch/p/12764171.html
Copyright © 2020-2023  润新知