zoukankan      html  css  js  c++  java
  • 【转】高效率的C++函数返回值

    1.有时候返回引用可以提高效率,有些情况不能返回引用

    类的赋值运算符函数中,返回对象的引用可以显著提高效率。

     view plaincopy to clipboardprint?
    class String  
    {  
    public:   
        String & operate=(const String &other);   
        // 相加函数,如果没有friend修饰则只许有一个右侧参数  
        friend  String   operate+(const String &s1, const String &s2);   
    private:  
        char *m_data;   
    }  
     
    String & String::operate=(const String &other)  
    {  
        if (this == &other)  
            return *this;  
        delete m_data;  
        m_data = new char[strlen(other.data)+1];  
        strcpy(m_data, other.data);  
        return *this;   // 返回的是 *this的引用,无需拷贝过程  

    class String
    {
    public:
     String & operate=(const String &other); 
     // 相加函数,如果没有friend修饰则只许有一个右侧参数
     friend String   operate+(const String &s1, const String &s2);
    private:
     char *m_data;
    }

    String & String::operate=(const String &other)
    {
     if (this == &other)
      return *this;
     delete m_data;
     m_data = new char[strlen(other.data)+1];
     strcpy(m_data, other.data);
     return *this; // 返回的是 *this的引用,无需拷贝过程
    }

    上面如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。

    String的相加函数operate + 的实现如下:

     view plaincopy to clipboardprint?
    String  operate+(const String &s1, const String &s2)    
    {  
        String temp;  
        delete temp.data;   // temp.data是仅含‘\0’的字符串  
        temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];  
        strcpy(temp.data, s1.data);  
        strcat(temp.data, s2.data);  
        return temp;  

    String  operate+(const String &s1, const String &s2) 
    {
     String temp;
     delete temp.data; // temp.data是仅含‘\0’的字符串
     temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];
     strcpy(temp.data, s1.data);
     strcat(temp.data, s2.data);
     return temp;
    }

    对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。

    设想一下,相加运算符重载函数应该是参数列表中的两个对象相加,必须产生一个新的对象并返回它。这区别于+=运算符,+=运算符是将右边的对象累加到左边的对象上,因此+=的实现应当返回运算符左边对象的引用。

    2. 返回静态局部变量的引用往往会带来问题
    上面已经说了operator+函数中不能返回临时对象的引用,那么返回静态局部变量的引用呢?静态局部变量的生命不会随着函数消失而结束,返回其引用似乎可行,但这样做往往会给程序带来问题。

     view plaincopy to clipboardprint?
    const Rational& operator*(const Rational& lhs, const Rational& rhs)  
    {  
        static Rational result;             // static object to which a reference will be returned  
        result = ... ;                      // multiply lhs by rhs and put the product inside result  
        return result;  

    const Rational& operator*(const Rational& lhs, const Rational& rhs)
    {
     static Rational result;             // static object to which a reference will be returned
     result = ... ;                      // multiply lhs by rhs and put the product inside result
     return result;
    }

     下面if恒为真,静态局部变量对全局可见,每次operator*返回的是它的引用,相当于是返回它的“新值”。

    view plaincopy to clipboardprint?
    bool operator==(const Rational& lhs, const Rational& rhs); // an operator== for Rationals  
    Rational a, b, c, d;  
    ...  
    if ((a * b) == (c * d))    
    {  
        do whatever's appropriate when the products are equal;  
    }  
    else   
    {  
        do whatever's appropriate when they're not;  

    bool operator==(const Rational& lhs, const Rational& rhs); // an operator== for Rationals
    Rational a, b, c, d;
    ...
    if ((a * b) == (c * d)) 
    {
     do whatever's appropriate when the products are equal;
    }
    else
    {
     do whatever's appropriate when they're not;
    }
     

    3. 返回临时对象,不要定义局部对象返回
    String temp(s1 + s2);
    return temp;
    上述代码将发生三件事。首先,temp对象被创建,同时完成初始化;然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束时被销毁(调用析构函数)。然而,创建一个临时对象并返回它:return temp(s1 + s2);,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。
    由于内部数据类型如int,float,double的变量不存在构造函数与析构函数,虽然该“临时变量的语法”不会提高多少效率,但是程序更加简洁易读。

    4. 注意不要返回栈指针和常量字符串
    不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡。

    view plaincopy to clipboardprint?
    char *GetString(void)  
    {  
        char p[] = "hello world";  
        return p;   // 编译器将提出警告  
    }  
     
    void Test4(void)  
    {  
        char *str = NULL;  
        str = GetString();  // str 的内容是垃圾  
        cout<< str << endl;  

    char *GetString(void)
    {
     char p[] = "hello world";
     return p; // 编译器将提出警告
    }

    void Test4(void)
    {
     char *str = NULL;
     str = GetString(); // str 的内容是垃圾
     cout<< str << endl;
    }

    函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

    view plaincopy to clipboardprint?
    char *GetString2(void)  
    {  
        char *p = "hello world";  
        return p;  
    }  
     
    void Test5(void)  
    {  
        char *str = NULL;  
        str = GetString2();  
        cout<< str << endl;  

    char *GetString2(void)
    {
     char *p = "hello world";
     return p;
    }

    void Test5(void)
    {
     char *str = NULL;
     str = GetString2();
     cout<< str << endl;
    }
     

    5. 谨慎返回对象内部成员对象的引用
    有时候希望使得类对象占用空间较小,通常可以使用一个指向对象数据的指针作为该类的数据成员,如Rectangle类的成员指针pData。类的成员函数upperLeft和lowerRight仅仅是想获取类的数据内容而不想修改其内容,因此函数结尾使用了const表示类的数据成员不可更改。然而却不能达到设计的目的,虽然成员是私有,然而却通过公有函数返回了可以修改它的引用。如可以这样修改它:Rectangle r(&rd); r.upperLeft().setX(5);

    view plaincopy to clipboardprint?
    class Point  
    {  
    public:  
        Point(float p1, float p2){ x = p1; y = p2; }  
        void setX(float f){ x = f; }  
        void setY(float f){ y = f; }  
    private:  
        float x, y;  
    };  
     
    struct RectData  
    {  
        RectData(Point p1, Point p2) : ulhc(p1), lrhc(p2){}  
        Point ulhc, lrhc;  
    };  
     
    class Rectangle   
    {  
    public:  
        Rectangle(RectData *p) { pData = p; }  
        Point& upperLeft() const { return pData->ulhc; }  
        Point& lowerRight() const { return pData->lrhc; }  
    private:  
        RectData *pData;  
    }; 
    class Point
    {
    public:
     Point(float p1, float p2){ x = p1; y = p2; }
     void setX(float f){ x = f; }
     void setY(float f){ y = f; }
    private:
     float x, y;
    };

    struct RectData
    {
     RectData(Point p1, Point p2) : ulhc(p1), lrhc(p2){}
     Point ulhc, lrhc;
    };

    class Rectangle
    {
    public:
     Rectangle(RectData *p) { pData = p; }
     Point& upperLeft() const { return pData->ulhc; }
     Point& lowerRight() const { return pData->lrhc; }
    private:
     RectData *pData;
    };

    因此需要将两函数改为,这样想试图修改点的数据内容时,编译器会报错。

    view plaincopy to clipboardprint?
    const Point& upperLeft() const { return pData->ulhc; }  
    const Point& lowerRight() const { return pData->lrhc; } 
    const Point& upperLeft() const { return pData->ulhc; }
    const Point& lowerRight() const { return pData->lrhc; }

    参考文献:

    Effective C++

    高质量 C/C++ 编程指南

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Breathomn/archive/2009/10/11/4654830.aspx

  • 相关阅读:
    LVS-NAT模式的配置详解
    流处理与消息队列------《Designing Data-Intensive Applications》读书笔记16
    流式计算与计算抽象化------《Designing Data-Intensive Applications》读书笔记15
    MapReduce与批处理------《Designing Data-Intensive Applications》读书笔记14
    分布式系统的一致性算法------《Designing Data-Intensive Applications》读书笔记13
    线性一致性与全序广播------《Designing Data-Intensive Applications》读书笔记12
    分布式系统的烦恼------《Designing Data-Intensive Applications》读书笔记11
    猫眼电影和电影天堂数据csv和mysql存储
    爬虫——requests.get爬虫模块参数
    机器学习——数据预处理
  • 原文地址:https://www.cnblogs.com/freebird92/p/1844694.html
Copyright © 2011-2022 走看看