1. 程式人生 > 實用技巧 >【C++學習教程07】引用

【C++學習教程07】引用

目錄

參考

  • 範磊C++(第9課時)
  • Xcode VS2015

內容

什麼是引用

引用的地址

#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
    int a;
    int &ra=a;//引用需要初始化
    cout<<"a:\t"<<&a<<endl;
    cout<<"ra:\t"<<&ra<<endl;
    return 0;
}

輸出結果:

引用就是別名變數

#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
    int a;
    int &ra=a;//引用需要初始化
    a=999;
    cout<<"&a:\t"<<&a<<endl;
    cout<<"&ra:\t"<<&ra<<endl;
    int b=888;
    ra=b;
    cout<<"&a:\t"<<&a<<endl;
    cout<<"&ra:\t"<<&ra<<endl;
    cout<<"&b:\t"<<&b<<endl;
    cout<<"a:\t"<<a<<endl;
    cout<<"ra:\t"<<ra<<endl;
    cout<<"b:\t"<<b<<endl;
    ra=1;
    cout<<"a:\t"<<a<<endl;
    cout<<"ra:\t"<<ra<<endl;
    cout<<"b:\t"<<b<<endl;
    //&ra=b;//非法操作
    return 0;
}

輸出結果:

引用物件

#include <iostream>
using namespace std;
class Human
{
public:
    int get()const{return i;}
    void set(int x){i=x;}
private:
    int i;
};

int main(int argc, const char * argv[]) {
    Human Mike;
    Human &rMike=Mike;
    rMike.set(123);
    cout<<rMike.get()<<endl;
}

輸出結果:

空引用

按值傳遞

按址傳遞

按別名傳遞

指標出錯率高,且不易閱讀。

利用指標和引用返回多值

例子略。

按值傳遞物件

在這裡插入程式碼片

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

class A
{
public:
	A() { cout << "執行建構函式建立一個物件" << endl; }
	A(A&) { cout << "執行復制建構函式建立該物件的副本" << endl; }//帶引數的建構函式
	~A() { cout << "執行解構函式刪除該物件"<< endl; }
};
A func(A one) //返回值是類A的物件,輸入值也是類A的一個物件。
{
	return one;
}
int main(int argc, const char * argv[]) {
	A a;
	func(a);//按值傳遞
	system("pause");
	return 0;
}

輸出結果:


注意賦值建構函式的含義!!

按址傳遞物件

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

class A
{
public:
	A() { cout << "執行建構函式建立一個物件" << endl; }
	A(A&) { cout << "執行賦值建構函式建立該物件的副本" << endl; }//帶引數的建構函式
	~A() { cout << "執行解構函式刪除該物件"<< endl; }
};
A* func(A *one) //返回值是類A的物件,輸入值也是類A的一個物件。
{
	//return *one;//按照值返回,也會呼叫賦值建構函式
	return one;//返回一個地址
}
int main(int argc, const char * argv[]) {
	A a;
	func(&a);//按址傳遞
	system("pause");
	return 0;
}

輸出結果:

使用const指標來傳遞對物件




但是使用const會很麻煩

用別名的方式來傳遞物件

省略了使用const的複雜。

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
	A() { cout << "執行建構函式建立一個物件" << endl; }
	A(A&) { cout << "執行復制建構函式建立該物件的副本" << endl; }//帶引數的建構函式
	~A() { cout << "執行解構函式刪除該物件"<< endl; }
	void set(int i) { x = i; }
	int get() const { return x; }
private:
	int x;
};
const A& func(A &one) 
{
	return one;
}
int main(int argc, const char * argv[]) {
	A a;
	a.set(11);
	const A &b = func(a);//這樣通過別名就不能改變a
	//A b = func(a);//不能將引用賦給物件
	cout << b.get() << endl;
	system("pause");
	return 0;
}

輸出結果:


引用實現了與指標相同的效果且不用const的,操作起來更加方便。

指標還是引用

  • 引用只可以被初始化,但是指標能夠賦值;
  • 指標可以為空但是引用必須初始化;
  • 堆中的記憶體必須用指標來訪問,申請堆的時候返回的是指標。
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
	int *p = new int;
	if (p != NULL)
	{
		int &r = *p;//將r初始化為p指向的記憶體空間中資料的別名。
		r = 3;
		cout << r << endl;
	}
	//r = 4;//並且r的壽命只在大括號範圍內有效。
	system("pause");
	return 0;
}

引用容易犯的錯誤

最後幾課時需要好好學習!

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
	A(int i) { cout << "執行建構函式建立一個物件"<< endl; x = i; }
	A(A&a) { x = a.x; cout << "執行復制建構函式建立一個物件"<< endl; }//複製建構函式
	~A(){ cout << "執行解構函式" << endl; }
	int get()const { return x; }
private:
	int x;
};
A func()//返回建立物件a的別名 //按值傳遞時返回的也是副本的值,其生存期大於t它的引用的生存期。//指標就沒問題
{
	cout << "跳轉到func函式中" << endl;
	A a(23);// a在大括號後壽命就結束,a消失了,返回的就是一個並不存在的物件的別名。
	cout << "物件a的地址:"<<&a << endl;
	return a;
}

int main(int argc, const char * argv[]) {
	A *r = &func();//並不存在的物件的別名
	cout << "物件a的副本的地址:" << r << endl;
	cout << r->get() << endl;
	system("pause");
	return 0;
}

具體分析見課件

引用一個按值返回的堆中物件

  • 錯誤例子
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
	A(int i) { cout << "執行建構函式建立一個物件"<< endl; x = i; }
	A(A&a) { x = a.x; cout << "執行復制建構函式建立一個物件"<< endl; }//a是堆中物件的別名->執行後p指標無效被銷燬(造成記憶體洩漏),這個程式只能訪問其副本的地址。
	~A(){ cout << "執行解構函式" << endl; }
	int get()const { return x; }
private:
	int x;
};
A func() //按值返回的函式
{
	cout << "跳轉到func函式中" << endl; 
	A *p=new A(99);
	cout << "堆中物件的地址:"<<p<< endl;
	return *p;//讀到堆中物件,建立其副本->複製建構函式
}

int main(int argc, const char * argv[]) {
	A &r = func();//讀到的是副本的地址
	cout << "堆中物件的副本的地址:" << &r << endl;//地址證明他只是副本的地址
	cout << r.get() << endl;//引用延長了臨時變數的宣告(也就是副本的)
	system("pause");
	return 0;
}

輸出結果:

報錯原因:

引用一個按別名返回的堆中物件

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
	A(int i) { cout << "執行建構函式建立一個物件"<< endl; x = i; }
	A(A&a) { x = a.x; cout << "執行復制建構函式建立一個物件"<< endl; }//a是堆中物件的別名->執行後p指標無效被銷燬(造成記憶體洩漏),這個程式只能訪問其副本的地址。
	~A(){ cout << "執行解構函式" << endl; }
	int get()const { return x; }
private:
	int x;
};
A& func() //按引用返回的函式
{
	cout << "跳轉到func函式中" << endl; 
	A *p=new A(99);
	cout << "堆中物件的地址:"<<p<< endl;
	return *p;//返回引用注意!!
}

int main(int argc, const char * argv[]) {
	A &r = func();//引用
	cout << "堆中物件的副本的地址:" << &r << endl;//地址證明他只是副本的地址
	cout << r.get() << endl;//引用延長了臨時變數的宣告(也就是副本的)
	A *p = &r;
	delete p;//刪除p所指向的記憶體空間//所以呼叫了解構函式//r變成了空別名
	cout << r.get() << endl;//致命的錯誤
	system("pause");
	return 0;
}

輸出結果:

在哪裡建立就在哪裡釋放

唯一正確無誤的程式,推薦使用

#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
class A
{
public:
	A(int i) { cout << "執行建構函式建立一個物件"<< endl; x = i; }
	A(A&a) { x = a.x; cout << "執行復制建構函式建立一個物件"<< endl; }
	~A(){ cout << "執行解構函式" << endl; }
	void set(int i) { x = i; }
	int get()const { return x; }
private:
	int x;
};
A& func(A &a) 
{
	cout << "跳轉到func函式中" << endl; 
	a.set(66);
	return a;
}

int main(int argc, const char * argv[]) {
	A *p = new A(99);//在main中建立
	func(*p);
	cout<< p->get()<<endl;
	delete p;//在main中釋放
	system("pause");
	return 0;
}

輸出結果: