中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

c++臨時對象是什么

發布時間:2022-03-25 09:27:54 來源:億速云 閱讀:172 作者:iii 欄目:互聯網科技

本篇內容介紹了“c++臨時對象是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

什么是臨時對象?

        C++真正的臨時對象是不可見的匿名對象,不會出現在你的源碼中,但是程序在運行時確實生成了這樣的對象.

通常出現在以下兩種情況:

(1)為了使函數調用成功而進行隱式類型轉換的時候

        傳遞某對象給一個函數,而其類型與函數的形參類型不同時,如果可以通過隱式轉化的話可以使函數調用成功,那么此時會通過構造函數生成一個臨時對象,當函數返回時臨時對象即自動銷毀。如下例:

//計算字符ch在字符串str中出現的次數 int countChar (const string& str, char ch); char buffer[]; 
char c; //調用上面的函數 countChar (buffer, c);

      我們看的第一個參數為char[],而函數的參數類型為const string&,參數不一致,看看能否進行隱式轉化,string類有個構造函數是可以作為隱式轉化函數(參見5)的。那么編譯器會產生一個 string的臨時變量,以buffer為參數進行構造,那么countChar中的str參數會綁定到此臨時變量上,直到函數返回時銷毀。

      注意這樣的轉化只會出現在兩種情況下:函數參數以傳值(by value)的方式傳遞 或者 對象被傳遞到一個 reference-to-const 參數上。

傳值方式:

int countChar (string str, char ch); 
string buffer;
 char c;
 //參數通過傳值方式傳遞 countChar (buffer, c);

       這種方法會調用string的拷貝構造函數生成一個臨時變量,再將這個臨時變量綁定到str上,函數返回時進行銷毀。

傳常量引用:

       開始的實例即時屬于這種情況,但一定強調的是傳遞的是const型引用,如將開始函數的原型改為

int countChar (string& str, char ch);

       下面調用相同,編譯器會報錯!為什么C++設計時要求當對象傳遞給一個reference-to-non-const 參數不會發生隱式類型轉化呢?

       下面的實例可能向你說明這樣設計的目的:

//聲明一個將str中字符全部轉化為大寫 void toUpper (string& str); char buffer[] = "hazirguo"; 
toUpper(buffer);                 //error!!非const引用傳遞參數不能完成隱式轉化

        如果編譯器允許上面的傳遞完成,那么,會生成一個臨時對象,toUpper函數將臨時變量的字符轉化為大寫,返回是銷毀對象,但是對buffer內容毫無影響!程序設計的目地是期望對“非臨時對象”進行修改,而如果對reference-to-non-cosnt對象進行轉化,函數只會對臨時變量進行修 改。這就是為什么C++中要禁止non-const-reference參數產生臨時變量的原因了。

(2)當函數返回對象的時候

        當函數返回一個對象時,編譯器會生成一個臨時對象返回,如聲明一個函數用來合并兩個字符串:

const string strMerge (const string s1, const string s2);
大多時候是無法避免這樣的臨時變量產生的,但是現代編譯器可以將這樣的臨時變量進行優化掉,這樣的優化策略中,有個所謂的“返回值優化”,下一篇具體講解。
 總結:
臨時對象有構造和析構的成本,影響程序的效率,因此盡可能地消除它們。而更為重要的是很快地發現什么地方會生成臨時對象:
  • 當我們看到一個reference-to-const參數時,極可能一個臨時對象綁定到該參數上;

  • 當我們看到函數返回一個對象時,就會產生臨時對象。

參考:http://www.cnblogs.com/hazir/archive/2012/04/18/2456144.html

C++中的返回值優化(return value optimization)

返回值優化(Return Value Optimization,簡稱RVO),是這么一種優化機制:當函數需要返回一個對象的時候,如果自己創建一個臨時對象用戶返回,那么這個臨時對象會消 耗一個構造函數(Constructor)的調用、一個復制構造函數的調用(Copy Constructor)以及一個析構函數(Destructor)的調用的代價。而如果稍微做一點優化,就可以將成本降低到一個構造函數的代價,下面是 在Visual Studio 2008的Debug模式下做的一個測試:(在GCC下測試的時候可能編譯器自己進行了RVO優化,看不到兩種代碼的區別) 

c++臨時對象是什么  

//  C++ Return Value Optimization
//  作者:代碼瘋子
//  博客: http://www.programlife.net/
#include <iostream>
using  namespace std;
class Rational
{
public:
    Rational( int numerator =  0,  int denominator =  1) : n(numerator), d(denominator) {
          cout <<  " Constructor Called... " << endl;
      }
      ~Rational() {
          cout <<  " Destructor Called... " << endl;
      }
      Rational( const Rational& rhs) {
           this->d = rhs.d;
           this->n = rhs.n;
          cout <<  " Copy Constructor Called... " << endl;
      }
       int numerator()  const {  return n; }
       int denominator()  const {  return d; }
private:
     int n, d;
};
const Rational  operator*( const Rational& lhs,  const Rational& rhs) {
    cout <<  " ----------- Enter operator* ----------- " << endl;
    Rational tmp(lhs.numerator() * rhs.numerator(),
        lhs.denominator() * rhs.denominator());
    cout <<  " ----------- Leave operator* ----------- " << endl;
     return tmp;
}
int main( int argc,  char **argv) {
    Rational x( 1,  5), y( 2,  9);
    Rational z = x * y;
    cout <<  " calc result:  " << z.numerator() 
        <<  " / " << z.denominator() << endl;
 
     return  0;
}

復制代碼  

函數輸出截圖如下:
c++臨時對象是什么
可以看到消耗一個構造函數(Constructor)的調用、一個復制構造函數的調用(Copy Constructor)以及一個析構函數(Destructor)的調用的代價。

而如果把operator*換成另一種形式:

const Rational  operator*( const Rational& lhs, const Rational& rhs)
{
     return Rational(lhs.numerator() * rhs.numerator(),
                lhs.denominator() * rhs.denominator());
}

就只會消耗一個構造函數的成本了:
c++臨時對象是什么

參考: http://www.programlife.net/cpp-return-value-optimization.html

返回值優化(RVO)與具命返回值優化(NRVO)

這是一項編譯器做的優化,已經是一種很常見的優化手段了,搜一下可以找到很多的資料,在MSDN 里也有相關的說明。

返回值優化,顧名思義,就是與返回值有關的優化,是當函數是按值返回(而不是引用、指針)時,為了避免產生不必要的臨時對象以及值拷貝而進行的優化。

先看看下面的代碼:

c++臨時對象是什么  

typedef unsigned    int UINT32;  
  class MyCla  
{  
  public:  
    MyCla(UINT32 a_size =    10):size(a_size) {  
        p =    new UINT32[size];          
    }  
    MyCla(MyCla    const & a_right):size(a_right.size) {  
        p =    new UINT32[size];  
        memcpy(p, a_right.p, size*   sizeof(UINT32));  
    }  
    MyCla    const&    operator = (MyCla    const & a_right) {  
        size = a_right.size;  
        p =    new UINT32[size];  
        memcpy(p, a_right.p, size*   sizeof(UINT32));  
           return *   this;  
    }  
    ~MyCla() {  
        delete [] p;  
    }  
  private:  
    UINT32 *p;  
    UINT32 size;  
};  
MyCla TestFun() {  
       return MyCla();  
}  
  int _tmain(   int argc, _TCHAR* argv[])  
{  
    MyCla a = TestFun();     
       return    0;  
}

復制代碼  

TestFun() 函數返回了一個 MyCla 對象,而且是按值傳遞的。

在沒有任何“優化”之前,這段代碼的行為也許是這樣的:return MyCla() 這行代碼中,構造了一個 MyCla 類的臨時的無名對象(姑且叫它t1),接著把 t1 拷貝到另一塊臨時對象 t2(不在棧上),然后函數保存好 t2 的地址(放在 eax 寄存器中)后返回,TestFun 的棧區間被“撤消”(這時 t1 也就“沒有”了,t1 的生存域在 TestFun 中,所以被析構了),在 MyCla a = TestFun(); 這一句中,a 利用 t2 的地址,可以找到 t2 進行,接著進行構造。這樣 a 的構造過程就完成了。然后再把 t2 也“干掉”。

可以看到, 在這個過程中,t1 和 t2 這兩個臨時的對象的存在實在是很浪費的,占用空間不說,關鍵是他們都只是為a的構造而存在,a構造完了之后生命也就終結了。既然這兩個臨時的對象對于程序 員來說根本就“看不到、摸不著”(匿名對象),于是編譯器干脆在里面做點手腳,不生成它們!怎么做呢?很簡單,編譯器“偷偷地”在我們寫的TestFun 函數中增加一個參數 MyCla&,然后把 a 的地址傳進去(注意,這個時候 a 的內存空間已經存在了,但對象還沒有被“構造”,也就是構造函數還沒有被調用),然后在函數體內部,直接用a來代替原來的“匿名對象”,在函數體內部就完 成a的構造。這樣,就省下了兩個臨時變量的開銷。這就是所謂的“返回值優化”!在 VC7 里,按值返回匿名對象時,默認都是這么做。

上 面說的是“返回值優化(RVO)”,還有一種“具名返回值優化(NRVO)”,是對于按值返回“具名對象”(就是有名字的變量!)時的優化手段,其實道理 是一樣的,但由于返回的值是具名變量,情況會復雜很多,所以,能執行優化的條件更苛刻,在下面三種情況下(來自 MSDN),NRVO 將一定不起作用:

  1. 不同的返回路徑上返回不同名的對象(比如if XXX 的時候返回x,else的時候返回y)

  2. 引入 EH 狀態的多個返回路徑(就算所有的路徑上返回的都是同一個具名對象)

  3. 在內聯asm語句中引用了返回的對象名。

不過就算 NRVO 不能進行,在上面的描述中的 t2 這個臨時變量也不會產生,對于 VC 的 C++ 編譯器來說,只要你寫的程序是把對象按值返回的,它會有兩種做法,來避免 t2 的產生。拿下面這個程序來說明:

MyCla TestFun2() {  
    MyCla x(   3);  
       return x;  
}

一種做法是像 RVO一樣,把作為表達式中獲取返回值來進行構造的變量 a 當成一個引用參數傳入函數中,然后在返回語句之前,用要返回的那個變量來拷貝構造 a,然后再把這個變量析構,函數返回原調用點,a 就構造好了。

還有一種方式, 是在函數返回的時候,不析構x,而直接把x的地址放到 exa 寄存器中,返回調到 TestFun2 的調用點上,這時,a 可以用 exa 中存著的地址來進行構造,a 構造完成之后,再析構原來的變量 x !是的,注意到其實這時,x 的生存域已經超出了TestFun2,但由于這里x所在TestFun2的棧雖然已經無效,但是并沒有誰去擦寫這塊存,所以x其實還是有效的,當然,一切 都在匯編的層面,對于C++語言層面來講是透明的。

“c++臨時對象是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

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

c++
AI

武夷山市| 井研县| 沂南县| 永康市| 全州县| 拜城县| 周至县| 太保市| 车险| 乌鲁木齐县| 溧水县| 富裕县| 旬阳县| 扶绥县| 澜沧| 开平市| 阿勒泰市| 全南县| 册亨县| 无为县| 资兴市| 息烽县| 子长县| 抚州市| 荆州市| 沙雅县| 青海省| 健康| 车险| 阿克苏市| 都兰县| 武邑县| 陵川县| 泰安市| 清远市| 慈利县| 井冈山市| 新余市| 竹北市| 望谟县| 抚远县|