原文地址:https://www.cnblogs.com/jycboy/p/5184638.html
一劳秋、變量的引用:
引用僅是變量的別名陡厘,而不是實實在在地定義了一個變量抽米,因此引用本身并不占用內(nèi)存,而是和目標變量共同指向目標變量的內(nèi)存地址.
表達式中的取地址符&不再是取變量的地址糙置,而是用來表示該變量是引用類型的變量云茸。
定義一個引用時,必須對其初始化罢低。
#include <stdio.h>
#include <iostream>
using namespace std;
//c++中的引用實例
void fun(int &x){
printf("形式參數(shù)的值為:%d\n",x);
printf("形式參數(shù)的地址為:%d\n",&x);
x+=10;
}
void fun2(int *num){
printf("形式參數(shù)的值為:%d\n",*num);
printf("形式參數(shù)的地址值為:%d\n",num); //2686724
printf("形式參數(shù)的地址為:%d\n",&num); //2686704 這個和
*num =(*num)+10;
}
int main(void){
int num = 4,a=3;
printf("實參num的值為:%d\n",num);
printf("實參num的地址為:%d\n",&num);//都是一樣的
fun(num);
cout<<"num的值為:"<<num<<endl; //14 num的值改啦
fun2(&a);
printf("實參a的值為:%d\n",a);
printf("實參a的地址為:%d\n",&a);//
return 0;
}
1.和是一樣的查辩,
而后邊用指針作為函數(shù)參數(shù),把地址傳過去网持,宜岛,而(在指針的地址上存的值),2686696這個地址指向的值是3功舀,也就是num的值萍倡。
,和不一樣辟汰,而引用的地址是和變量的值一樣的列敲。
二、引用的應(yīng)用
引用的一個重要作用就是作為函數(shù)的參數(shù)戴而。以前的C語言中函數(shù)參數(shù)傳遞是值傳遞,如果有大塊數(shù)據(jù)作為參數(shù)傳遞的時候翩蘸,采用的方案往往是指針所意,因為這樣可以避免將整塊數(shù)據(jù)全部壓棧,可以提高程序的效率。但是現(xiàn)在(C++中)又增加了一種同樣有效率的選擇(在某些特殊情況下又是必須的選擇)扶踊,就是引用泄鹏。
#include<iostream.h>
////此處函數(shù)的形參p1, p2都是引用
void swap(int &p1,int &p2){
int p=p1;
p1=p2;
p2=p;
}
為在程序中調(diào)用該函數(shù),則相應(yīng)的主調(diào)函數(shù)的調(diào)用點處秧耗,直接以變量作為實參進行調(diào)用即可备籽,而不需要實參
變量有任何的特殊要求。如:對應(yīng)上面定義的swap函數(shù)分井,相應(yīng)的主調(diào)函數(shù)可寫為:
void main(){
int a,b;
cin>>a>>b;//輸入a,b兩個變量的值
swap(a,b);//直接以a和b作為實參調(diào)用swap函數(shù)
cout<<"a="<<a<<",b="<<b<<endl;
}
上述程序運行時车猬,如果輸入數(shù)據(jù)10 20并回車后,則輸出結(jié)果為a=20,b=10杂抽。
而使用一般變量傳遞函數(shù)的參數(shù)铸磅,當(dāng)發(fā)生函數(shù)調(diào)用時,需要給形參分配存儲單元杭朱,形參變量是實參變量的副本阅仔;如果傳遞的是對象,還將調(diào)用拷貝構(gòu)造函數(shù)弧械。因此八酒,當(dāng)參數(shù)傳遞的數(shù)據(jù)較大時,用引用比用一般變量傳遞參數(shù)的效率和所占空間都好刃唐。
(2)使用指針作為函數(shù)的參數(shù)雖然也能達到與使用引用的效果羞迷,但是,在被調(diào)函數(shù)中同樣要給形參分配存儲單元画饥,且需要重復(fù)使用"*指針變量名"的形式進行運算衔瓮,這很容易產(chǎn)生錯誤且程序的閱讀性較差;另一方面抖甘,在主調(diào)函數(shù)的調(diào)用點處热鞍,必須用變量的地址作為實參。而引用更容易使用衔彻,更清晰薇宠。
就如同上邊的例子一樣。
常引用聲明方式:const 類型標識符 &引用名 = 目標變量名慢睡;
用這種方式聲明的引用逐工,不能通過引用對目標變量的值進行修改,從而使引用的目標成為const,達到了引用的安全性漂辐。
#include <stdio.h>
#include <iostream>
using namespace std;
void test_const();
int main(void){
test_const();
return 0;
}
void test_const(){
int a=1;
int &b=a;
b=2;
cout<<"a="<<a<<endl;//2
int c=1;
const int &d=c;
// d=2;//編譯錯誤 error: assignment of read_only reference 'd'
c=2;//正確
cout<<"c="<<c<<endl;//
}
這不光是讓代碼更健壯,也有其它方面的需求棕硫。
string foo();
void bar(string &s);
那么下面的表達式將是非法的:
bar(foo());
bar("hello world");
原因在于foo( )和"hello world"串都會產(chǎn)生一個臨時對象髓涯,因此上面的表達式就是試圖將一個const類型的對象轉(zhuǎn)換為非const類型纬纪,這是非法的。
要以引用返回函數(shù)值靶庙,則函數(shù)定義時要按以下格式:
類型標識符 &函數(shù)名 (形參列表及類型說明)
∥食{ 函數(shù)體 }
說明:
(1)以引用返回函數(shù)值,定義函數(shù)時需要在函數(shù)名前加&
以下程序中定義了一個普通的函數(shù)fn1(它用返回值的方法返回函數(shù)值),另外一個函數(shù)fn2掏击,它以引用的方法返回函數(shù)值卵皂。
#include <iostream>
using namespace std;
float temp;//定義全局變量temp
float fn1(float r);//聲明函數(shù)fn1
float &fn2(float r);//聲明函數(shù)fn2 r
float fn1(float r){//定義函數(shù)fn1,它以返回值的方法返回函數(shù)值
temp=(float)(r*r*3.14);
return temp;
}
float &fn2(float r){//定義函數(shù)fn2砚亭,它以引用方式返回函數(shù)值
temp=(float)(r*r*3.14);
return temp;
}
int main(){
float e=10.0;
float a=fn1(10.0);//第1種情況灯变,系統(tǒng)生成要返回值的副本(即臨時變量)
/****************************************
error: invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
****************************************/
// float &b=fn1(10.0); //第2種情況,可能會出錯(不同 C++系統(tǒng)有不同規(guī)定)
//不能從被調(diào)函數(shù)中返回一個臨時變量或局部變量的引用
float c=fn2(10.0);//第3種情況捅膘,系統(tǒng)不生成返回值的副本
//可以從被調(diào)函數(shù)中返回一個全局變量的引用
float &d=fn2(10.0); //第4種情況添祸,系統(tǒng)不生成返回值的副本
e=d;
cout<<"a="<<a<<",c="<<c<<",d="<<d<<",e="<<e<<endl;
//a=314,c=314,d=314
return 0;
}
引用作為返回值,必須遵守以下規(guī)則:
(1)這條可以參照Effective C++[1]的Item 31膝捞。,因此被返回的引用就成為了"無所指"的引用愧沟,程序會進入未知狀態(tài)蔬咬。如上面中的第2種情況出現(xiàn)編譯錯誤。
(2)這條可以參照Effective C++[1]的Item 31林艘。雖然不存在局部變量的被動銷毀問題,可對于這種情況(返回函數(shù)內(nèi)部new分配內(nèi)存的引用)混坞,又面臨其它尷尬局面狐援。例如钢坦,被函數(shù)返回的引用只是作為一個臨時變量出現(xiàn),而沒有被賦予一個實際的變量啥酱,那么這個引用所指向的空間(由new分配)就無法釋放爹凹,造成memory leak。
(3)這條原則可以參照Effective C++[1]的Item 30。主要原因是當(dāng)對象的屬性是與某種業(yè)務(wù)規(guī)則(business rule)相關(guān)聯(lián)的時候绘趋,其賦值常常與某些其它屬性或者對象的狀態(tài)有關(guān)颤陶,因此有必要將賦值操作封裝在一個業(yè)務(wù)規(guī)則當(dāng)中。如果其它對象可以獲得該屬性的非常量引用(或指針)陷遮,那么對該屬性的單純賦值就會破壞業(yè)務(wù)規(guī)則的完整性滓走。
(4)引用與一些操作符的重載:流操作符<<和>>,這兩個操作符常常希望被連續(xù)使用帽馋,例如:cout << "hello" << endl; 因此這兩個操作符的返回值應(yīng)該是一個仍然支持這兩個操作符的流引用搅方。可選的其它方案包括:返回一個流對象和返回一個流對象指針茬斧。但是對于返回一個流對象腰懂,程序必須重新(拷貝)構(gòu)造一個新的流對象,也就是說项秉,連續(xù)的兩個<<操作符實際上是針對不同對象的绣溜!這無法讓人接受娄蔼。對于返回一個流指針則不能連續(xù)使用<<操作符锚沸。因此哗蜈,返回一個流對象引用是惟一選擇距潘。這個唯一選擇很關(guān)鍵音比,它說明了引用的重要性以及無可替代性稽犁,也許這就是C++語言中引入引用這個概念的原因吧已亥。 賦值操作符=陷猫。這個操作符象流操作符一樣的妖,是可以連續(xù)使用的嫂粟,例如:x = j = 10;或者(x=10)=100;賦值操作符的返回值必須是一個左值墨缘,以便可以被繼續(xù)賦值镊讼。因此引用成了這個操作符的惟一返回值選擇蝶棋。
#include<iostream.h>
int &put(int n);
int vals[10];
int error=-1;
void main(){
put(0)=10;//以put(0)函數(shù)值作為左值兼贸,等價于vals[0]=10;
put(9)=20;//以put(9)函數(shù)值作為左值,等價于vals[9]=20;
cout<<vals[0]<<endl;//10
cout<<vals[9]<<endl;//20
}
int &put(int n){
if(n>=0 && n<=9)
return vals[n];
else{
cout<<"subscript error";
return error;
}
}
(5)在另外的一些操作符中螺垢,卻千萬不能返回引用:+-*/ 四則運算符。它們不能返回引用讯蒲,Effective C++[1]的Item23詳細的討論了這個問題墨林。主要原因是這四個操作符沒有side effect赁酝,因此旭等,它們必須構(gòu)造一個對象作為返回值,可選的方案包括:返回一個對象搔耕、返回一個局部變量的引用,返回一個new分配的對象的引用弃榨、返回一個靜態(tài)對象引用菩收。根據(jù)前面提到的引用作為返回值的三個規(guī)則,第2娜饵、3兩個方案都被否決了。靜態(tài)對象的引用又因為((a+b) == (c+d))會永遠為true而導(dǎo)致錯誤。所以可選的只剩下返回一個對象了拳亿。
【例7】:
class A;
class B:public A{ ... ... }
B b;
A &Ref = b;//用派生類對象初始化基類對象的引用
Ref 只能用來訪問派生類對象中從基類繼承下來的成員酗捌,是基類引用指向派生類呢诬。如果A類中定義有虛函數(shù),并且在B類中重寫了這個虛函數(shù)胖缤,就可以通過Ref產(chǎn)生多態(tài)效果尚镰。
(1)在引用的使用中,單純給某個變量取個別名是毫無意義的哪廓,
(2)分俯,
(3),;
(4)蹋半、