|
| |
| 尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。函数对象(也称“算符”)是重载了“()”操作符的普通类对象。因此从语法上讲,函数对象与普通的函数行为类似。 用函数对象代替函数指针有几个优点,首先,因为对象可以在内部修改而不用改动外部接口,因此设计更灵活,更富有弹性。函数对象也具备有存储先前调用结果的数据成员。在使用普通函数时需要将先前调用的结果存储在全程或者本地静态变量中,但是全程或者本地静态变量有某些我们不愿意看到的缺陷。 其次,在函数对象中编译器能实现内联调用,从而更进一步增强了性能。这在函数指针中几乎是不可能实现的。 下面举例说明如何定义和使用函数对象。首先,声明一个普通的类并重载“()”操作符: class Negate { public: int operator() (int n) { return -n;} }; 重载操作语句中,记住第一个圆括弧总是空的,因为它代表重载的操作符名;第二个圆括弧是参数列表。一般在重载操作符时,参数数量是固定的,而重载“()”操作符时有所不同,它可以有任意多个参数。 因为在Negate中内建的操作是一元的(只有一个操作数),重载的“()”操作符也只有一个参数。返回类型与参数类型相同-本例中为int。函数返回与参数符号相反的整数。 使用函数对象 我们现在定义一个叫Callback()的函数来测试函数对象。Callback()有两个参数:一个为int一个是对类Negate的引用。Callback()将函数对象neg作为一个普通的函数名: #include <iostream> using std::cout; void Callback(int n, Negate & neg) { int val = neg(n); //调用重载的操作符“()” cout << val; } 不要的代码中,注意neg是对象,而不是函数。编译器将语句 int val = neg(n); 转化为 int val = neg.operator()(n); 通常,函数对象不定义构造函数和析构函数。因此,在创建和销毁过程中就不会发生任何问题。前面曾提到过,编译器能内联重载的操作符代码,所以就避免了与函数调用相关的运行时问题。 为了完成上面个例子,我们用主函数main()实现Callback()的参数传递: int main() { Callback(5, Negate() ); //输出 -5 } 本例传递整数5和一个临时Negate对象到Callback(),然后程序输出-5。 模板函数对象 从上面的例子中可以看出,其数据类型被限制在int,而通用性是函数对象的优势之一,如何创建具有通用性的函数对象呢?方法是使用模板,也就是将重载的操作符“()”定义为类成员模板,以便函数对象适用于任何数据类型:如double,_int64或char: class GenericNegate { public: template <class T> T operator() (T t) const {return -t;} }; int main() { GenericNegate negate; cout<< negate(5.3333); // double cout<< negate(10000000000i64); // __int64 } 如果用普通的回调函数实现上述的灵活性是相当困难的。 标准库中函数对象 C++标准库定义了几个有用的函数对象,它们可以被放到STL算法中。例如,sort()算法以 判断对象(predicate object)作为其第三个参数。判断对象是一个返回Boolean型结果的 模板化的函数对象。可以向sort()传递greater<>或者less<>来强行实现排序的升序或降序: #include <functional> // for greater<> and less<> #include <algorithm> //for sort() #include <vector> using namespace std; int main() { vector <int> vi; //..填充向量 sort(vi.begin(), vi.end(), greater<int>() );//降序( descending ) sort(vi.begin(), vi.end(), less<int>() ); //升序 ( ascending ) } |
C++中的模板分为类模板和函数模板,.模板的特化
(1) 类模板特化
有时为了需要,针对特定的类型,需要对模板进行特化,也就是特殊处理.例如,stack类模板针对bool类型,因为实际上bool类型只需要一个二进制位,就可以对其进行存储,使用一个字或者一个字节都是浪费存储空间的.
template <class T>
class stack {};
template < >
class stack<bool> { //…// };
上述定义中template < >告诉编译器这是一个特化的模板。并且在声明特化模板之前一定要有非特化的声明!并且两个类的名字是一样的!
(2) 函数模板的特化
看下面的例子
main()
{
int highest = mymax(5,10);
char c = mymax(‘a’, ’z’);
const char* p1 = “hello”;
const char* p2 = “world”;
const char* p = mymax(p1,p2);
}
前面两个mymax都能返回正确的结果.而第三个却不能,因为,此时mymax直接比较两个指针p1 和 p2 而不是其指向的内容.
针对这种情况,当mymax函数的参数类型为const char* 时,需要特化。
template <class T>
T mymax(const T t1, const T t2)
{
return t1 < t2 ? t2 : t1;
}
template <>
const char* mymax(const char* t1,const char* t2)
{
return (strcmp(t1,t2) < 0) ? t2 : t1;
}
现在mymax(p1,p2)能够返回正确的结果了。
4.模板的偏特化
模板的偏特化是指需要根据模板的某些但不是全部的参数进行特化
(1) 类模板的偏特化
例如c++标准库中的类vector的定义
template <class T, class Allocator>
class vector { // … // };
template <class Allocator>
class vector<bool, Allocator> { //…//};
这个偏特化的例子中,一个参数被绑定到bool类型,而另一个参数仍未绑定需要由用户指定。
(2) 函数模板的偏特化
严格的来说,函数模板并不支持偏特化,但由于可以对函数进行重载,所以可以达到类似于类模板偏特化的效果。
template <class T> void f(T); (a)
根据重载规则,对(a)进行重载
template < class T> void f(T*); (b)
如果将(a)称为基模板,那么(b)称为对基模板(a)的重载,而非对(a)的偏特化。C++的标准委员会仍在对下一个版本中是否允许函数模板的偏特化进行讨论。
5.模板特化时的匹配规则
(1) 类模板的匹配规则
最优化的优于次特化的,即模板参数最精确匹配的具有最高的优先权
例子:
template <class T> class vector{//…//}; // (a) 普通型
template <class T> class vector<T*>{//…//}; // (b) 对指针类型特化
template <> class vector <void*>{//…//}; // (c) 对void*进行特化
每个类型都可以用作普通型(a)的参数,但只有指针类型才能用作(b)的参数,而只有void*才能作为(c)的参数
(2) 函数模板的匹配规则
非模板函数具有最高的优先权。如果不存在匹配的非模板函数的话,那么最匹配的和最特化的函数具有高优先权
例子:
template <class T> void f(T); // (d)
template <class T> void f(int, T, double); // (e)
template <class T> void f(T*); // (f)
template <> void f<int> (int) ; // (g)
void f(double); // (h)
bool b;
int i;
double d;
f(b); // 以 T = bool 调用 (d)
f(i,42,d) // 以 T = int 调用(e)
f(&i) ; // 以 T = int* 调用(f)
f(d); // 调用(h)
template <typename T1,typename T2>
class MyClass{
...
}
就可以有下面几种局部特化:
//局部特化:两个模板参数具有相同的类型
template <typename T>
class MyClass<T,T>{
……
}
//局部特化:第二个模板参数的类型是int
template <typename T>
class MyClass<T,int>{
...
}
//局部特化:两个模板参数都是指针类型
template<typename T1,typename T2>
class MyClass<T1*,T2*>{
...
}
下面的例子展示各种声明会使用哪个模板:
MyClass<int,float> mif;//使用Myclass<T1,T2>
MyClass<float,float> mff;//使用MyClass<T,T>
MyClass<folat,int> mfi;//使用MyClass<T,int>
MyClass<int*,float*> mp;//使用MyClass<T1*,T2*>
如果有多个局部特化同等程度的匹配某个声明,那么就称该声明具有二义性:
MyClass<int,int> m;//错误:同等陈德匹配MyClass<T,T>和MyClass<T,int>
MyClass<int*,int*> m;//错误:同等程度的匹配MyClass<T,T>和MyClass<T1*,T2*>
A.重点锻炼部位:胸大肌、三角肌和肱三头肌。B.开始位置:仰卧在平的卧推凳上,两脚平踏在地上。两手掌向上伸直握住哑铃。 C.动作过程:使两直臂向两侧张开,两臂慢慢弯屈,哑铃垂直落下,下降至最低处时,即做上推动作,上推时呼气。然后向上推起至开设位置,重复做。 D.训练要点:不要把背和臀部拱起或憋气,这样会使肌肉失去控制,是危险的。
上斜哑铃卧推
A.重点锻炼部位:胸大肌上部,其次是三角肌前束和肱三头肌。 B.开始位置:仰卧在上斜角度为35-45度的卧推凳上。 C.动作过程:两臂伸直持哑铃位于肩的上部。放下至胸部上方(接近锁骨处)时吸气。下降至最低处时,即做上推动作,上推时呼气。 D.训练要点:练习过程将主要力量集中在胸大肌上,使胸肌始终处于紧张状态。肱三头肌作为次要的补充力量。
平卧哑铃飞鸟
A.重点锻炼部位:胸大肌和三角肌。 B.开始位置:仰卧在平的卧推凳上,两手各持哑铃,掌心相对,推起至两臂伸直,支撑在胸部上方。 C.动作过程:两手持哑铃平行地向两侧落下,手肘稍微弯屈,哑铃落下至感到胸部两侧肌肉有充分的拉伸感,并使上臂落下至低于肩部水平线。当哑铃落下时,要深深吸气。持铃循原路举起回原位时呼气。 D.训练要点:如果哑铃向两侧落下时,两臂如呈伸直状态,胸部肌肉便很难得到拉伸和肌肉收缩的感觉。
上斜哑铃飞鸟
A.重点锻炼部位:上胸和三角肌。 B.开始位置:仰卧在斜的卧推凳上,两手各持哑铃,掌心相对,推起至两臂伸直。 C.动作过程:两手持哑铃平行地向两侧落下,手肘稍微弯屈,哑铃落下至感到胸部两侧肌肉有充分的拉伸感。当哑铃落下时,要深深吸气。持铃循原路举起回原位时呼气。 D.训练要点:如果哑铃向两侧落下时,两臂如呈伸直状态,胸部肌肉便很难得到拉伸和肌肉收缩的感觉。

站姿颈后臂屈伸
A.重点锻炼部位:主要健美肱三头肌。。B.开始位置:全身直立,两手正握或反握杠铃,上臂屈曲固定在头的两侧。 C.动作过程:吸气,以肘关节为轴,用力将前臂伸直上举,稍停2-3秒。然后吸气,屈臂慢慢落下还原至颈后,重复练习。 D.训练要点:上臂必须紧贴耳侧,两肘夹紧,上臂保持与地面垂直状,两肘尖垂直向上,不要向前后移动借力。
俯立臂屈伸
A.重点锻炼部位:肱三头肌。 B.开始位置:自然站立在凳的一端,上体前屈至背部与地面平行,左手以手掌支撑在凳上,右手持哑铃,屈肘,使右上臂紧贴体侧与背部平行,前臂下垂。 C.动作过程:手持铃,上臂贴身,固定肘部位置,持铃向后上方举起至臂伸直,再慢慢放下还原。只有前臂上下活动。 D:训练要点:采用“孤立训练原则”,持铃至全臂伸直时,使肱三头肌彻底收缩,保持静止并默数1、2、3,然后再放下还原
俯坐弯举
A.重点锻炼部位:肱二头肌 B.开始位置:坐或俯立,上体稍向前倾,一手握哑铃下垂于一腿内侧,另一手臂自然地屈肘,以手掌或肘部搁在一侧大腿上。 C.动作过程:持铃慢慢屈肘向上弯起至胸前,上臂不准移动,紧贴大腿内侧。 D.训练要点:当持铃弯起时,腰背部不要放松。当持铃弯起至胸前时,使肱二头肌尽量收紧,并保持静止3秒钟。然后,再慢慢放下。也可以立姿进行。
站姿哑铃锤式弯举
A.重点锻炼部位:主要健美肱肌和肱二头肌肌群。B.开始位置:直立或坐姿,两手臂伸直自然下垂,手握哑铃,虎口朝前。C.动作过程:两上臂同时以肘为轴经体侧弯起带哑铃,上、前臂用力收紧,稍停2-3秒,然后呼气,持铃缓慢放下还原至体侧,重复练习。D.训练要点:对握弯举时,两上臂固定不动,直腕握铃,不得借助上体摆动的惯性力。

站姿拉力器单臂反握弯举
A.重点锻炼部位:主要健美肱二头肌和肱肌 B.开始位置:自然站立,两脚间距和肩同宽,挺胸收腹紧腰。右臂向下伸直置于体侧,掌心向前握住把柄一端。 C.动作过程:吸气,屈肘慢慢向上拉开拉力器置右手接近右肩部,稍停2-3秒钟,然后呼气,缓慢还原,重复做。 D.训练要点:上拉时,上体要保持平直,肘部不要前后摇动。
坐姿哑铃交替弯举
A.重点锻炼部位:肱二头肌 B.开始位置:正坐在凳的一端,两手各持哑铃,下垂体侧。 C.动作过程:把一手持铃弯起至肩前.然后慢慢放下,同时另一手持铃 弯起.两手交替做弯举。 D:训练要点:有些健美冠军喜欢在开始时掌心向下,弯起时,使手腕 向外转至肩前。放下时再转回还原,他们认为这样练更有效。
哑铃推举
A.重点锻炼部位:这个动作是锻炼躯干上部的大肌肉群。例如:三角肌、斜方肌、上胸肌、肱三头肌、和上背肌群。 B.开始位置:双手持铃握于头部两侧 C.动作过程:两手垂直方向把哑铃推起至两臂伸直。然后再慢慢放下至起始位置。D.训练要点:哑铃握法比杠铃有很大的自由度。
俯立侧平举
A.重点锻炼部位:三角肌后束和上背肌群。B.开始位置:两脚分开站立同肩宽,两手掌心相对持哑铃,上体向前屈体至与地面平行,两腿稍屈,使下背部没有拉紧感。C.动作过程:两手持铃向两侧举起,直至上臂与背部平行(或略为超过),稍停,然后放下哑铃还原。重复做。D.训练要点:如果在持铃向两侧举起时,使肘和腕部稍微弯屈,你会感到能使三角肌群获到更好的收缩。在整个动作过程中,思想要集中在收缩的肌肉群上。
侧平举
A.重点锻炼部位:三角肌外侧中束部位。B.开始位置:自然站立,两手各持哑铃下垂体前,两肘部稍弯屈,拳眼向前。C.动作过程:两手持铃同时向两侧举起,直到举起至与头部齐高位置。然后,慢慢地循原路落下回原位,再重复做。D.训练要点:在持铃提起和放下过程中,使肘和腕部始终稍微弯屈,对三角肌的收缩更为有效。当哑铃向两侧提起时,同时使手腕向上转起至比大姆指稍高些,直到提起至 最高位置。哑铃落下时,手腕再转回。
“前平举”用哑铃
A.重点锻炼部位:上胸部和三角肌前束。B.开始位置,自然站立,两手各持亚铃或持杠铃下垂于腿前。C.动作过程:把哑铃或杠铃向前上方举起(肘部稍屈),直至与视线平行高度。然后,慢慢放下还原,重复做。D.训练要点:如果采用哑铃时,以拳眼向前,持铃于体前上举。这种方法是单独集中锻炼三角肌前束。
剪跨
A.重点锻炼部位:臀大肌、腿筋和股四头肌。B.开始位置:两脚并立,把杠铃置于颈后肩上(或双手耻哑铃)。先使右脚向前跨出一大步。然后,慢慢蹲下,右膝前屈,左腿稍稍挺直下沉。C.动作过程:当下蹲至最低位置时,再使两腿同时向上伸直,左脚向前收回,并向右脚$&*拢并立。然后,再使左脚向前跨出一大步下蹲。重复做。D.训练要点;如果你在下蹲起立至四分之三或还有一段短距离到即将伸直时,主要是以股四头肌用力收缩的。这个动作也可以作原地剪蹲,左、右脚交替练。 

7月的头一个节气,虽然立秋,但炎热的天气必须过了处暑到白露时,才会转凉。换言之,从立秋开始,还会热一阵子,这段期间大约是三十天。民间形容一种热得令人十分难受,甚至有刺痛之感的气候为“秋老虎”,那正是立秋之后的天气。在立秋之后,午后的雨水逐渐减少,因此被太阳一晒,往往比大暑时觉得还要难过。
立秋后,天气渐渐转凉,农谚有“早晨立了秋,晚上凉飕飕”,“立秋一日,水冷三分”之说。但江南的“秋老虎”却也有它的威力。
根据气候平均温度划分季节的标准,必须是5天的平均温度在22℃以下才算是秋天,按照这样的标准,江淮地区一般是在9月中下旬才进入秋天。立秋后虽然一时暑气难消,虽有“秋老虎”的余威,但总的趋势是天气逐渐凉爽。气温的日较差逐渐明显,往往是白天很热,而夜晚却比较凉爽。此外,秋的含义,还有庄稼快成熟的意思。
古代立秋之日,皇帝会率领文武百官到西郊祭祀迎秋,并下令武将开始操练士兵,以保家卫国。另外,不论朝廷还是民间,在立秋收成之后,会挑选一个黄道吉日,一方面祭拜感谢上苍与祖先的庇佑,另一方面则尝试新收成的米谷,以庆祝五谷丰收。
立秋以后气温由热转凉,人体的消耗也逐渐减少,食欲开始增加。因此,可根据秋季的特点来科学地摄取营养和调整饮食,以补充夏季的消耗,并为越冬做准备。秋季气候干燥,夜晚虽然凉爽,但白天气候仍较高,所以根据“燥则润之”的原则,应以养阴清热、润燥止渴、清新安神的食品为主,可选用芝麻、 蜂蜜、银耳、乳品等具有滋润作用的食物。秋季空气中湿度小,皮肤容易干燥。因此,在整个秋季都应重视机体水分和维生素的摄入。
秋季为人体最适宜进补的季节,以在冬季到来时,减少病毒感染和防止旧病复发。秋季进补应选用“防燥不腻”的平补之品。具有这类作用的食物有茭白、南瓜、莲子、桂圆、黑芝麻、红枣、核桃等。患有脾胃虚弱、消化不良的人,可以服食具有健脾补胃的莲子、山药、扁豆等。秋季出现口感唇焦等“秋燥症”的气候,应选用滋养润燥、益中补气的食品,这类食品有银耳、百合等,可起到滋阴、润肺、养胃、生津的补益作用。
还有,我国东南沿海地区于“立秋”时,一般可种植下列蔬果:
1.北部:高丽菜、花椰菜、茄子、芹菜、乌豆、白豆、葱。
2.中部:番茄、茄子、芹菜、芥蓝菜。
3.南部:高丽菜、长年菜、番薯。
可捕获下列鱼类:
目吼、卓鲲、鲟鱼、龙尖、沙鱼等。
也许,是季节的轮回交替;也许,是长河的孜孜不倦,似乎明白了时间的意义。时间是生命的一种负重,因此生命中有着负重的心情——时间不留人。时间是一把剪刀,当它慢慢地剪去生命这根长节鞭时,生命便呼呼作响了。不要懈怠生命,也不停留。当一江春水哗哗东流时,不要问君能有几多愁,因为愁丝是剪不断理还乱,还是仰天长笑出门去,释放生命的累赘吧。生命已经承担了时间的负重,用自己的爱去呵护这种生命,用心来慰问自己吧。
没有清茗在手时,理亦载负时光之车,生命之舟。因为生命有一种负重叫时间。
老年人常常会叹惋生命的速度之快,不经意间一切都安详走过。此时的年轻人在草长莺飞的季节是肆意的,花儿香鸟儿鸣难道不该享受吗?不是这样的,时光在穿梭着,生命不仅有如此之重更有日薄西山回望当年的历史之重——责任。责任是生命的一种负重,它或许平平淡淡或许轰轰烈烈,但很昂扬。这种昂扬,决定了生命的责任之重。刘邦有一种生命负重,那是安天下威加海内兮归故乡;诸葛孔明有一种生命负重,那是鞠躬尽瘁死而后已;孔繁森也有一种生命负重,那是走进西藏奉献人民。生命之重,就是这样轻飘又沉重的降下。是天将降大任于斯人也,是爱着自己的生命?其实用自己的爱诠释着自己的生命负重,何尝不是在排解生命的负重呢?
罗兰讲:“生命的过程注定由激越到安详,由绚烂到平淡。”那么,我们所做的只是选择生命充实生命,不超载只承担的重任。六月的江南是杂花生树,沉醉不知归路,可要紧把生命之重用爱来诠释,稳之又稳啊!
当春风不度玉门关时,当天下谁人不识君时,当滚滚长江东逝水时,把持负重的生命!
#include <iostream>
#include <string>
using namespace std;
void main(int argc,char* argv[])
{
int a=10;
int b=20;
int &rn=a;
cout<<rn<<"|"<<a<<endl;
cout<<&rn<<"|"<<&a<<endl;//c++中是无法取得引用的内存地址的,取引用的地址就是取目标的地址!
rn=b;//把引用指向另一个目标----变量b
cout<<&rn<<"|"<<&a<<"|"<<&b<<endl;
rn=100;//试图改变b的值
cout<<a<<"|"<<b<<endl;//输出修改后的结果
cin.get();
}
由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。
引用一旦初始化,就不在能够被指向其它的目标,虽然编译不会出错,但操作是不起作用的,实际上还是指向最先指向的目标。
上面代码中的rn=b实际在计算机看来就是a=b,所以修改的还是a的值。
#include <iostream>
#include <string>
using namespace std;
void main(int argc,char* argv[])
{
int a=10;
void &rn=a;// 错误的,void即无类型的类型
int a[100];
int &ra[100]=a;//错误,不能声明引用数组
cin.get();
}
上面的两错误要记住引用的特性,void修饰是不能够声明引用的,引用是不能够声明数组的,即不能够声明引用数组。
下面我们要说一下,也是补充中最重要最需要掌握的内容,也是对传统函数操作的内存状态的一个补充学习。
下面我们来看一个例子:
#include <iostream>
#include <string>
using namespace std;
float c;
float test(float,float);
void main(int argc,char* argv[])
{
float pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float test(float a,float b)
{
c=a*b;
return c;
}
在上面的代码中我们可能我们可能以为函数返回的就是c变量,呵呵。这么想可能就错了,普通情况下我们在函数内进行普通值返回的时候在内存栈空间内其实是自动产生了一个临时变量temp,它是返回值的一个副本一个copy,函数在return的时候其实是return的这个临时产生的副本。
数据在内存中的情况如下图:

上图明确表示了副本领事变量的情况。
下面我们再来看一种情况,就是把返回值赋给引用:
#include <iostream>
#include <string>
using namespace std;
float c;
float test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);//警告:返回的将是临时变量,pn引用将成为临时变量的别名!
cout<<pn;
cin.get();
}
float test(float a,float b)
{
c=a*b;
return c;
}
float &pn=test(3.0f,1.2f);这句在bc中能够编译通过,因为bc扩展设置为临时变量设置引用,那么临时变量的生命周期将和引用的生命周期一致,但在vc中却不能通过编译,因为一但test()执行过后临时变量消失在栈空间内,这时候pn将成为一个没有明确目标的引用,严重的时候会导致内存出错。
它在内存中的情况见下图:

我们在图中看到,由于函数仍然是普通方法返回,所以仍然会有一个副本临时变量产生,只不过,这一次只是返回一个目标地址,在main中目标地址被赋予了引用pn。
下面我们再看一种情况,这是返回引用给变量的情况:
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
这种返回引用给变量的情况下,在内存中,test()所在的栈空间内并没有产生临时变量,而是直接将全局变量c的值给了变量pn,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,程序的可读性也是比较好的。
它在内存中的情况见下图:

最后的一种情况是函数返回引用,并且发值赋给一个引用的情况:
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
这种情况同样也不产生临时变量,可读和性能都很好,但有一点容易弄错,就是当c是非main的局部变量或者是在堆内存中临时开辟后来又被fee掉了以后的区域,这种情况和返回的指针是局部指针的后果一样严重,会导致引用指向了一个不明确的地址,这种情况在内存中情况见下图:

由于这种情况存在作用域的问题,故我们推荐采用第三种方式处理。
接下来我们说几个利用引用作为左值参与计算的例子,这一点一非常重要,对于理解返回引用的函数是非常有帮助的。
#include <iostream>
#include <string>
using namespace std;
float c;
float& test(float,float);
void main(int argc,char* argv[])
{
float &pn=test(3.0f,1.2f);
cout<<pn<<endl;
test(3.0f,1.2f)=12.1;//把函数作左值进行计算!
cout<<pn;
cin.get();
}
float &test(float a,float b)
{
c=a*b;
return c;
}
通常来说函数是不能作为左值,因为引用可以做为左值,所以返回引用的函数自然也就可以作为左值来计算了。
在上面的代码中:
float &pn=test(3.0f,1.2f);
进行到这里的时候pn已经指向到了目标c的地址了。
接下来运行了
test(3.0f,1.2f)=12.1;
把函数作左值进行计算,这里由于test是返回引用的函数,其实返回值返回的地址就是c的地址,自然c的值就被修改成了12.1。

