溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++ 命名返回值優化(NRVO)

發布時間:2020-06-14 18:17:17 來源:網絡 閱讀:1804 作者:XDATAPLUS 欄目:編程語言

命名的返回值優化(NRVO),這優化了冗余拷貝構造函數和析構函數調用,從而提高了總體性能。值得注意的是,這可能導致優化和非優化程序之間的不同行為。

下面是代碼段1中的一個簡單示例,以說明優化及其實現方式:

A MyMethod (B &var)
{
    A retVal;
    retVal.member = var.value + bar(var);
    return retVal;
}

使用上述函數的程序可能具有如下構造:

valA = MyMethod(valB);

從MyMethod返回的值是在valA通過使用隱藏的參數所指向的內存空間中創建的。下面是當我們公開隱藏的參數并顯示地顯示構造函數和析構函數時的功能:

A MyMethod(A &_hiddenArg, B &var)
{
    A retVal;
    retVal.A::A(); //constructor for retVal
    retVal.member = var.value + var(var);
    _hiddenArg.A::A(retVal); // the copy constructor for A
    return;
return.A::~A(); // destructor for retVal
}

上段代碼為不使用NRVO的隱藏參數代碼(偽代碼)
從上面的代碼可以看出,有一些優化的機會。其基本思想是消除基于堆棧的臨時值(retVal)并使用隱藏的參數。因此,這將消除基于堆棧的值的拷貝構造函數和析構函數。下面是基于NRVO的優化代碼:

A MyMethod(A &_hiddenArg, B &var)
{
    _hiddenArg.A::A();
    _hiddenArg.member = var.value + bar(var);
    Resurn
}

帶有NRVO的隱藏參數代碼(偽代碼)

代碼示例
示例1:簡單示例

#include <stdio.h>

class RVO
{
public:
        RVO() { printf("I am in constructor\n"); }
        RVO( const RVO& c_RVO ) { printf("I am in copy constructor\n"); }
        ~RVO() { printf("I am in destructor\n"); }
        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        rvo.mem_var = i;
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代碼:Sample1.cpp
如果沒有NRVO,預期的輸出將是:

I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor

使用NRVO,預期的輸出將是:

I am in constructor
I am in constructor
I am in destructor
I am in destructor

示例2:更復雜的示例

#include <stdio.h>
class A
{
public:
        A()
        {
                printf("A: I am in constructor\n");
                i = 1;
        }

        ~A()
        {
                printf("A: I am in destructor\n");
                i = 0;
        }

        A(const A& a)
        {
                printf("A: I am in copy constructor\n");
                i = a.i;
        }

        int i, x, w;
};

class B
{
public:
        A a;
        B()
        {
                printf("B: I am in constructor\n");
        }

        ~B()
        {
                printf("B: I am in destructor\n");
        }

        B(const B& b)
        {
                printf("B: I am in copy constructor\n");
        }
};

A MyMethod()
{
        B* b = new B();
        A a = b->a;
        delete b;
        return a;
}

int main()
{
        A a;
        a = MyMethod();
}

代碼Sample2.cpp
無NRVO的輸出將如下所:

A: I am in constructor
A: I am in constructor
B: I am in constructor
A: I am in copy constructor
B: I am in destructor
A: I am in destructor
A: I am in copy constructor
A: I am in destructor
A: I am in destructor
A: I am in destructor

當NRVO優化啟動時,輸出將是:

A: I am in constructor
A: I am in constructor
B: I am in constructor
A: I am in copy constructor
B: I am in destructor
A: I am in destructor
A: I am in destructor
A: I am in destructor

優化限制
有些情況下,優化不會真正啟動。以下是這些限制的樣本
示例3:異常示例
在遇到異常時,隱藏的參數必須在它正在替換的臨時范圍內被破壞。

// RVO class is defined above in figure 4
#include <stdio.h>
RVO MyMethod(int i)
{
    RVO rvo;
    cvo.mem_var = i;
    throw "I am throwing an exception!";
    return rvo;
}

int main()
{
    RVO rvo;
    try
    {
        rvo = MyMethod(5);
    }
    catch(char* str)
    {
        printf("I caught the exception\n");
    }

代碼Sample3.cpp
如果沒有NRVO,預期的輸出將是:

I am in constructor
I am in constructor
I am in destructor
I caught the exception
I am in destructor

如果“拋出”被注釋掉,輸出將是:

I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor

現在,如果“拋出”被注釋掉,并且NRVO被觸發,輸出將如下所示:

I am in constructor
I am in constructor
I am in destructor
I am in destructor

也就是說sample3.cpp在沒有NRVO的情況下,會表現出相同的行為。

示例4:不同的命名對象示例
若要使用優化,所有退出路徑必須返回同一命名對象。

#include <stdio.h>
class RVO
{
public:
        RVO()
        {
                printf("I am in construct\n");
        }

        RVO(const RVO& c_RVO)
        {
                printf("I am in copy construct\n");
        }

        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        rvo.mem_var = i;
        if( rvo.mem_var == 10 )
                return RVO();
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代碼Sample4.cpp
啟用優化時輸出與不啟用任何優化相同。NRVO實際上并不發生,因為并非所有返回都返回相同的對象。

I am in constructor
I am in constructor
I am in copy constructor

如果將上面的示例更改為返回rvo。在返回對象時,優化將消除復制構造函數:

#include <stdio.h>
class RVO
{
public:
        RVO()
        {
                printf("I am in constructor\n");
        }

        RVO(const RVO& c_RVO)
        {
                printf("I am in copy constructor\n");
        }

        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        if( i == 10 )
                return rvo;
        rvo.mem_var = i;
                return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代碼Sample4_Modified.cpp修改并使用NRVO,輸出結果將如下所示:

I am in constructor
I am in constructor

優化副作用
程序員應該意識到這種優化可能會影響應用程序的流程。下面的示例說明了這種副作用:

#include <stdio.h>

int NumConsCalls = 0;
int NumCpyConsCalls = 0;

class RVO
{
public:
        RVO()
        {
                NumConsCalls ++;
        }

        RVO(const RVO& c_RVO)
        {
                NumCpyConsCalls++;
        }
};

RVO MyMethod()
{
        RVO rvo;
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod();
        int Division = NumConsCalls / NumCpyConsCalls;
        printf("Construct calls / Copy constructor calls = %d\n", Division);
}

代碼段Sample5.cpp
編譯未啟用優化將產生大多數用戶所期望的?!皹嬙旌瘮怠北徽{用兩次?!翱截悩嬙旌瘮怠北徽{用一次。因此除法生成2。

Constructor calls / Copy constructor calls = 2

另一方面,如果上面的代碼通過啟用優化進行編譯,NRVO將會啟用。因此“拷貝構造函數”調用將被刪除。因此,NumCpyConsCalls將為零,導致異常。如果沒有適當處理,可以導致應用程序崩潰。


引入自:https://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女