c++引用

嘰了咣啷biang發表於2012-07-04

引用就是別名

定義引用時一定要同時對該引用進行初始化,如:

int a;

int &ra=a;

而不能寫成:

int a;

int &ra;

ra=a;

 

按址傳遞

 

void swap(int *pa,int *pb);   //swap函式宣告
int a=3,b=4;                  //變數a和b
swap(&a,&b);                  //傳遞a和b

 

按別名傳遞

void swap(int &pa,int &pb);   //swap函式宣告
int a=3,b=4;                  //變數a和b
swap(a,b);                    //傳遞a和b


    指標是間接引用,別名時直接引用

 

傳遞物件

 

     按址傳遞可以修改原始變數的值,有雨按值傳遞的是原始變數的副本物件,因此它不會修改原始變數的值。

      假如僅僅是傳遞變數的話,採用指標或者引用這種按址傳遞的優勢不是很明顯,但是假如是傳遞較大的物件的話,這種優勢是比較明顯的。這是因為,按值傳遞在向函式傳遞一個物件時,會像傳遞變數那樣建立一個該物件的副本,而從函式返回一個物件時,也要建立這個被返回的物件的一個副本。假如物件的資料非常多,這種副本物件帶來的記憶體開銷是非常客觀的。

 

 

按值傳遞:

#include <iostream>
using namespace std;
class A
{
public:
	A();
	A(A&);
	~A();
};
A::A()
{
	cout<<"執行建構函式建立一個物件\n";
}
A::A(A&)
{
	cout<<"執行復制建構函式建立該物件的副本\n";
}
A::~A()
{
	cout<<"執行解構函式刪除物件\n";
}
A func(A one);
int main()
{
	A a;
	func(a);
	return 0;
}
A func(A one)
{
	return one;
}


按址傳遞

#include <iostream>
using namespace std;
class A
{
public:
	A();
	A(A&);
	~A();
};
A::A()
{
	cout<<"執行建構函式建立一個物件\n";
}
A::A(A&)
{
	cout<<"執行復制建構函式建立該物件的副本\n";
}
A::~A()
{
	cout<<"執行解構函式刪除物件\n";
}
A *func(A *one);
int main()
{
	A a;
	func(&a);
	return 0;
}
A *func(A *one)
{
	return one;
}

 

利用const指標來傳遞物件

 

       利用指標來傳遞物件,雖然可以避免呼叫複製建構函式和解構函式,但是由於它得到該物件的記憶體地址,可以隨時修改資料物件,因此它實際上破壞了按值傳遞的保護機制。  用const指標來傳遞物件,這樣就可以防止任何試圖對該物件所進行的操作行為,並且保證返回一個不被修改的物件。

 

const A *const func(const A *const one)
{
	return one;
}

指標常見的錯誤


     引用是它所引用的物件的別名,假如這個物件不存在了,使用該物件就會出現隨機錯誤

 

 

在哪裡建立就在哪裡釋放

     只要在堆中建立一塊記憶體空間,就會返回一個指向該空間的指標,我們一定不要把該指標弄丟,假如指標丟失,那麼它指向的空間就會成為一塊不可訪問的區域,也就是記憶體洩漏。假如我們將儲存在堆中的物件初始化給一個引用,那麼當該物件被刪除的時候,這個引用會成為空引用,假如無意中使用了空引用,會令程式出錯。

       我們在main函式中建立一個堆中物件,然後按引用的方式傳遞到func()函式中,在函式中對該物件操作完畢後返回該物件,然後在main函式中釋放該物件。這樣就實現了在哪裡建立就在哪裡釋放。

#include <iostream>
using namespace std;
class A
{
public:
	A(int i){x=i;cout<<"執行建構函式建立一個物件\n";}
	A(const A&a){a=a.x;cout<<"執行復制建構函式建立一個物件\n";}
	int get(){return x;}
	void set(int i){x=i;}
private:
	int x;
};
A &func(A&a);
int main()
{
	A *p=new A(32);
	cout<<p->get();
	func(*p);
	cout<<p->get();
	delete p;
	return 0;
}
A &func(A&a)
{
	a.set(66);
	return a;
}


 總結

 

        在函式中按地址返回一個棧中物件時,由於該物件是從函式中建立的,函式返回後,該函式的棧中物件也被銷燬了,因此不要對不存在的物件進行任何操作。

        但當函式按值返回在該函式中建立的棧中物件時,會把該物件複製到執行呼叫該函式的作用域中。在本例中,在main函式中執行呼叫的該函式,所以有愛哦吧棧中物件複製到main函式的作用域中,複製物件的工作結束後,接著會呼叫解構函式銷燬掉複製的新物件,但是由於引用會使臨時變數壽命延長到與自身壽命相同,因此複製到mian函式中的物件直到引用的壽命結束,也就是main函式,結束時,才釋放。但是指標沒有這個特例,函式返回後,物件和它的副本都先後被析構了。不過,有雨副本的記憶體地址已經返回給指標,因此指標可以根據這個地址訪問副本的資料,我們知道析構一個物件只是告訴表一切該物件所佔用的記憶體不再為該物件鎖獨享,可以分配給其他物件或者變數,因此儲存在該記憶體區域中的資料在沒有被其他資料覆蓋之前仍然存在,所以指標還可以訪問到該資料。

        假如函式按值返回一個堆中物件,由於指向堆中物件的指標超出函式的作用域而被自動釋放了,同時返回的是該物件的副本,因此函式中建立的堆中物件便成了一個不可訪問的物件,你無法找到它,因為指向它的指標被銷燬了,這就造成了記憶體洩漏,另外,返回的副本是在棧中建立的,引用可以延緩它的析構到引用釋放後,但是指標卻沒有這個特性,副本的記憶體地址返回給指標就會立即銷燬自身。假如其他變數或者物件沒有佔用該副本的記憶體空間,那麼該副本的資料將一直存在。

          假如按地址返回一個堆中物件而由引用接收,那麼就需要定義一個指標來儲存引用的地址,這樣刪除指標就可以刪除堆中物件。但是要注意,刪除該指標所指向的物件後,該物件的引用就成了空引用。他們也可以用指標來代替引用,但是繳入我們無法確定指向堆中物件的指標是哪一個,那麼就有可能該指標刪除了兩次,或者忘記刪除指標。解決的方法是在哪裡建立物件,就在哪裡釋放。比如說,在main函式中建立一個堆中物件,然後通過引用的方式傳遞到func()函式中去,在func()函式執行完對該物件的操作並返回該物件後,main函式再根據自己的需要對其進行釋放。這樣可避免混淆指向堆中物件的指標,同時按引用的方式傳遞堆中物件,不用生成該物件的副本。另外,由於引用可使臨時變數壽命延長,因此還可避免指標容易導致的臨時變數丟失的危險。


 

相關文章