Sales_item operator+(const Sales_item& lhs,const Sales_item& rhs)
{
Sales_item ret(lhs) ;
ret+= rhs ;
return ret ;
}
怎么可以返回一個局部變量呢??????
例如
Sales_item item1,item2,item3 ;
......(item2,item3初始化成員變量)
item1 = item2 + item3 ;
沒有問題,它返回的是一個值,而不是引用,所以是正確的。在item1 = item2 + item3 中發(fā)生了一次值拷貝(賦值),也就是將item2+item3返回的局部變量拷貝給了item1之后,局部變量的作用域結(jié)束
可以返回局部變量,但是不能返回局部變量的引用。理解區(qū)分值和引用這兩個概念是學(xué)習(xí)C++的一大關(guān)鍵,明白這兩個概念之后,你就會理解為什么C++的類里面需要有拷貝構(gòu)造函數(shù),賦值操作符,析構(gòu)函數(shù)三個元素了以及其它的一些稀里古怪的用法和忠告了
--
Sales_item operator+(const Sales_item& lhs,const Sales_item& rhs)
{
Sales_item ret(lhs) ;
ret+= rhs ;
return ret ;
}
在return ret 時并不是簡單的返回這個局部變量,而是返回的是ret的一個副本temp,
temp是通過拷貝構(gòu)造函數(shù)實現(xiàn)的 即 Sales_item temp(ret);也就是說在主函數(shù)中用的其實是個temp,而ret早在operator+調(diào)用完畢后就釋放了,而temp這個 對象一直在主函數(shù)中貯存(雖然顯示是看不到的 ) ????
嗯嗯,臨時對象一直是個有爭議的話題。temp只是臨時構(gòu)造的,在賦值完畢之后它就析構(gòu)了,不是一直在主函數(shù)中貯存的。這個臨時對象的作用域是什么???
在你提的這種情況下,其實編譯器是可以優(yōu)化掉這個副本的,但是可惜C++標準只允許優(yōu)化 “Sales_item item1=item2+item3” 這種情況的,也就是在拷貝構(gòu)造這種情況下,編譯器都不再產(chǎn)生副本,而賦值還是不行的。也許在未來的C++標準由可能通過拓展語義來消除臨時對象,畢竟臨時對象成為影響C++效率的一個主要因素。
--
???
目前的C++標準不允許在賦值的時候優(yōu)化掉函數(shù)返回的副本,也就是下面這兩種情況是不一樣的
//這是賦值給item1,目前的標準下是會產(chǎn)生副本 temp
Sales_item item1,item2,item3;
item1=item2+item3;
//這是拷貝構(gòu)造item1,編譯器通常會優(yōu)化(這個優(yōu)化區(qū)別下面所說的NRV優(yōu)化)掉副本,也就是不產(chǎn)生副本 temp
Sales_item item2,item3;
Sales_item item1=item2+item3;
看一下代碼中的TheFunctionTwo();
1 #include<iostream>
2 #include<string>
3
4 using namespace std;
5
6 class SimpleCat
7 {
8 public:
9 SimpleCat(void);
10 SimpleCat(int age,int weight);
11 SimpleCat(SimpleCat &rsc);
12 SimpleCat & operator=(const SimpleCat &rhs);
13 int GetAge(){return itsAge;}
14 int GetWeight(){return itsWeight;}
15 void SetAge(int age){itsAge = age;}
16
17 private:
18 int itsAge;
19 int itsWeight;
20 //int itsAge=5;//1>.\SimpleCat.cpp(14) : error C2864: “SimpleCat::itsAge”: 只有靜態(tài)常量整型數(shù)據(jù)成員才可以在類中初始化
21
22 public:
23 virtual ~SimpleCat(void);
24 };
25
26 SimpleCat::SimpleCat(void)
27 {
28 cout<<"SimpleCat constructor "<<this<<endl;
29 }
30
31 SimpleCat::SimpleCat(int age,int weight)
32 {
33 cout<<"SimpleCat constructor "<<this<<endl;
34 itsAge = age;
35 itsWeight = weight;
36 }
37
38 SimpleCat::SimpleCat(SimpleCat &rhs)
39 {
40 cout<<"SimpleCat copy constructor"<<this<<" 從 "<<&rhs<<"拷貝"<<endl;
41 itsAge = rhs.itsAge;
42 itsWeight = rhs.itsWeight;
43 }
44
45 SimpleCat &SimpleCat::operator=(const SimpleCat &rhs)
46 {
47 cout<<"SimpleCat重載賦值運算符"<<this<<" 從 "<<&rhs<<"賦值"<<endl;
48 itsAge = rhs.itsAge;
49 itsWeight = rhs.itsWeight;
50 return *this;
51 }
52
53 SimpleCat::~SimpleCat(void)
54 {
55 cout<<"SimpleCat destructor"<<this<<endl;
56 }
57
58 SimpleCat &TheFunction();
59
60 SimpleCat TheFunctionTwo();
61
62 SimpleCat &TheFunctionThree();
63
64 void TheFunctionFour(SimpleCat simpleCat);
65
66 int main()
67 {
68 SimpleCat myCat;
69 cout<<myCat.GetAge()<<endl;//這個值區(qū)別于java不是賦初值為0的,而是一個隨機的值
70 // cout<<myCat.itsAge<<endl;
71
72 cout<<"------------------------"<<endl;
73 SimpleCat &rCat = TheFunction();
74 int age = rCat.GetAge();
75 cout<<"rCat is "<<age<<"yeas old!"<<endl;
76 cout<<"&rCat: "<<&rCat<<endl;
77 SimpleCat *pCat = &rCat;
78 //delete rCat;//不能對引用使用delete
79 delete pCat;
80 //delete好像沒有釋放內(nèi)存,怎么獲取的還是原來的值
81 //可能在這個內(nèi)存區(qū)域存放的還是原來的?先new string后再調(diào)用也沒變,與編譯器有關(guān)還是什么??
82 for(int i =0;i<10;i++)
83 {
84 //想通過創(chuàng)建string對象來填充之前的內(nèi)存區(qū)間,好像沒用
85 string *s = new string("abcdefghijklmn");
86 }
87
88 //這時問題來了,rCat.getAge()為123了
89 SimpleCat *pSecond = new SimpleCat(123,444);
90
91 cout<<"delete pCat后再使用rCat引用會發(fā)生什么問題???"<<endl;
92 cout<<"delete pCat后 &rCat"<<&rCat<<endl;
93 cout<<"delete pCat后 rCat.age"<<rCat.GetAge()<<endl;
94
95 cout<<"--------------------------"<<endl;
96 SimpleCat myCat2 = TheFunction();//這個會發(fā)生內(nèi)存泄漏,在函數(shù)中申請的內(nèi)存會得不到釋放
97 //myCat2是通過默認的拷貝函數(shù)來進行初始化的
98 cout<<"myCat2的地址是 "<<&myCat2<<endl;
99 cout<<endl<<"---------------------------"<<endl;
100
101 //直接的調(diào)用默認拷貝構(gòu)造函數(shù)的
102 //cout<<"直接的調(diào)用默認拷貝構(gòu)造函數(shù)的,這個語句一共創(chuàng)建了多少個對象"<<endl;
103 //SimpleCat myCat3 = TheFunctionTwo();//這個盡然沒調(diào)用拷貝構(gòu)造函數(shù)?????
104 //cout<<"myCat3的地址是 "<<&myCat3<<endl;
105 //cout<<"myCat3.GetAge() "<<myCat3.GetAge()<<endl;
106
107 //調(diào)用默認的賦值運算符的
108 //cout<<"調(diào)用默認的賦值運算符的,這個語句一共創(chuàng)建了多少個對象"<<endl;
109 //SimpleCat myCat4;
110 //myCat4 = TheFunctionTwo();
111 //cout<<"myCat4.GetAge() "<<myCat4.GetAge()<<endl;
112
113
114 //這種調(diào)用的方式
115 //cout<<"TheFunctionTwo()返回的臨時對象賦給一個引用"<<endl;
116 //SimpleCat &rmyCat = TheFunctionTwo();
117 //cout<<"打印一下返回的臨時對象的地址,臨時對象賦給引用之后就不會立即析構(gòu)了---"<<&rmyCat<<endl;
118 //cout<<"rmCat.GetAge() "<<rmyCat.GetAge()<<endl;
119
120 SimpleCat myCat5;
121 TheFunctionFour(myCat5);
122
123 return 0;
124 }
125
126
127 //這個函數(shù)在返回時,是否會創(chuàng)建一個臨時引用變量???
128 //TheFunctionTwo(SimpleCat &simpleCat)這個函數(shù)在傳遞參數(shù)時是否會創(chuàng)建一個臨時的引用變量
129 //臨時的引用變量的作用域范圍是什么
130 SimpleCat &TheFunction()
131 {
132 SimpleCat *pFrisky = new SimpleCat(5,9);
133 cout<<"pFrisky: "<<pFrisky<<endl;
134 return *pFrisky;
135 }
136
137
138
139 //我要看對象被創(chuàng)建了幾次,是否創(chuàng)建臨時對象
140 SimpleCat TheFunctionTwo()
141 {
142 SimpleCat tempCat;
143 cout<<"in TheFunctionTwo tempCat指針 "<<&tempCat<<endl;
144 tempCat.SetAge(9999);
145 //返回時是否還創(chuàng)建一個臨時對象
146 return tempCat;
147
148 }
149
150
151 //這種方式肯定是錯的,返回了局部變量的引用
152 SimpleCat &TheFunctionThree()
153 {
154 SimpleCat tempCat;//局部變量
155 cout<<"in TheFunctionThree tempCat指針 "<<&tempCat<<endl;
156 tempCat.SetAge(9999);
157 //返回時是否還創(chuàng)建一個臨時對象,已引用的形式
158 return tempCat;
159 }
160
161
162
163 //我要看再實參對形參進行拷貝構(gòu)造時,是否會打印出調(diào)用的拷貝函數(shù)信息
164 void TheFunctionFour(SimpleCat simpleCat)
165 {
166 cout<<"形式參數(shù)simpleCat的地址 "<<&simpleCat<<endl;
167
168 }
169
170
171
------------------------------------------------------------------------------------------------------------------------------
先說說第一塊吧,
這個貌似應(yīng)該調(diào)用拷貝構(gòu)造的地方?jīng)]有調(diào)用拷貝構(gòu)造,應(yīng)該是編譯器做的優(yōu)化,可以參考《深入探索C++對象模型》P66頁。
按照書中的說法,很可能只創(chuàng)建了1個對象
X bar()
{
X xx;
return xx;
}
可能會被編譯器優(yōu)化成
void bar(X & _result)
{
_result.X::X();
return;
}
所以調(diào)用X a = bar(),其實被轉(zhuǎn)換成 bar(X& a);
所以一個本該調(diào)用拷貝構(gòu)造的地方卻很可能調(diào)僅用了構(gòu)造函數(shù)來完成工作。
這個問題我也沒有搞明白,我沒有試過lz的代碼是不是會這樣,如果真這樣的話,我覺得編譯器就管的太多了,如果拷貝構(gòu)造中有一些特殊的功能呢(就像樓主有個輸出語句),豈不是無聲無息中被抹殺了。這個功能被稱為NRV,好像一直沒有人對這個問題給出非常明確的回答
------------------------------------------------------------------------------------------------------------------------------
thx,在vs2005中代碼優(yōu)化開啟時,第一塊確實只會調(diào)一個構(gòu)造函數(shù)
把代碼優(yōu)化禁用后,就會調(diào)用拷貝構(gòu)造函數(shù)了
確實是個NRV的問題
http://blog.vckbase.com/bruceteen/archive/2005/12/30/16652.html
------------------------------------------------------------------------------------------------------------------------------
正如4樓的所說~~~
到底創(chuàng)建多少臨時對象,要視編譯器而定,看編譯器是否采用NVR...
(其實一般情況下,NVR都是未采用的~),因此對后三個注釋代碼做
如下分析:寫出編譯后的偽代碼(僅供參考)
編譯器更改后的函數(shù)原型void TheFunctionTwo(SimpleCat&)
注釋1:
SimpleCat myCat3;
TheFunctionTwo(myCat3)
{
SimpleCat tempCat;
templCat.SimpleCat::SimpleCat();
cout<<"in TheFunctionTwo tempCat指針 "<<&tempCat<<endl;
tempCat.SetAge(9999);
myCat3.SimpleCat::SimpleCat(tempCat);
tempCat.SimpleCat::~SimpleCat();
}
注釋2:
SimpleCat myCat4;
myCat4.SimpleCat::Simple();
SimpleCat temp;
TheFunctionTwo(temp)
{
SimpleCat tempCat;
templCat.SimpleCat::SimpleCat();
cout<<"in TheFunctionTwo tempCat指針 "<<&tempCat<<endl;
tempCat.SetAge(9999);
temp.SimpleCat::SimpleCat(tempCat);
tempCat.SimpleCat::~SimpleCat();
}
myCat4.operator=(temp);
temp.SimpleCat::~SimpleCat();
注釋3:
//SimpleCat &rmyCat = TheFunctionTwo();
SimpleCat temp;
SimpleCat &rmyCat=temp;
TheFunctionTwo(temp)
{
SimpleCat tempCat;
templCat.SimpleCat::SimpleCat();
cout<<"in TheFunctionTwo tempCat指針 "<<&tempCat<<endl;
tempCat.SetAge(9999);
temp.SimpleCat::SimpleCat(tempCat);
tempCat.SimpleCat::~SimpleCat();
}
這樣就知道到底需要多少臨時對象了~~~~
posted on 2009-05-11 15:22
Frank_Fang 閱讀(1375)
評論(4) 編輯 收藏 所屬分類:
C++編程