【C++學習教程07】引用
阿新 • • 發佈:2021-01-02
目錄
參考
- 範磊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;
}
輸出結果: