C++筆記之指針應用

一. 指針熱身

1. 與地址相關的運算 * 和 &

" * " :
聲明:表示聲明的是指針。int *p;
執行:表示訪問指針所指對象的內容。 *p;
" & ":
聲明:出現在聲明變量左邊,表示聲明的是引用。int &r=a;
執行:出現在等號右邊或作爲一元運算符出現,表示取對象的地址。int *pa=&b; pa=&a;

2.對象指針

聲明形式:

類名 *對象指針名

可以通過指針訪問對象成員:

對象指針名->成員名

例:

Point A(3,4);
Point *ptr; //聲明對象指針
ptr=&A; //將對象A的起始地址賦給對象指針ptr
(*ptr).x;  
ptr->x; //訪問所指對象中的x數據成員
(*ptr).getX();
ptr->getX()//訪問對象指針所指對象的函數成員

3.this指針

(1) 隱含於每一個類的成員函數中的特殊指針指向本類對象的指針,它的值是當前被調用的成員函數所在的領域的起始地址。
(2)明確地指出了成員函數當前所操作的數據所屬的對象。當通過一個對象調用成員函數時,系統先將該對象的地址賦給this指針,然後調用成員函數,成員函數對對象的數據成員進行操作時,就隱含使用了this指針。
(3)在需要時也可以顯式的使用this 指針。比如當函數形參和類內已說明變量名相同時,有變量作用域問題,要顯示使用this。

Point::Point(int x,int y)
{	this->x=x; //顯示使用了指向當前對象的this指針
	tihs->y=y;

4.指向普通函數的指針

定義方法:

數據類型(*函數指針名)(參數列表);
函數指針名=函數名;

舉例:

include<iostream>
using namespace std;
void getSum(int x,int y){  cout<<x+y<<endl;}
int main(){
     void (*p)(int x,int y);//p是指向void型函數的指針變量
     p=getSum;//將getSum函數的入口地址賦給指針變量p, p就指向了函數
     (*p)(2,3);//調用getSum函數
     return 0;
}

二. 指向類成員的指針

1.指向類的非靜態成員的指針

(1)通過指向成員的指針只能訪問公有成員
(2)聲明指向成員的指針:
包括指向公有數據成員的指針和指向公有成員函數的指針

類型說明符 類名::*指針名;    //指向類的數據成員的指針
類型說明符 (類名::*指針名)(參數表);   //指向類的成員函數的指針

(3)初始化:

指針名=&類名::數據成員名; 
指針名=&類名::函數成員名;

(4)通過對象 / 對象指針 與 成員指針結合訪問類的成員:

對象名.*類數據成員指針名;
對象指針名->*類數據成員指針名;

(對象名.*類成員函數指針名)(參數表)
(對象指針名->*類成員函數指針名)(參數表)

舉例:

#include<iostream>
using namespace std;
class Point
{
public:
 Point(int xx=1,int yy=2,int zz=3)   { x=xx;y=yy;z=zz; }
	  int z;
	  int GetX(){return x;}
	  int GetY(){return y;}
private:
       int x,y;
};
void main()
{
	 Point t;
	 Point *ptr=&t;
	 cout<<"x="<<t.GetX()<<endl;
	 cout<<"y="<<ptr->GetY()<<endl;
	 
	 int Point::*pz;
	 pz=&temp::z;
	 cout<<"z="<<t.z<<endl;
	 cout<<"z="<<t.*pz<<endl;
	 cout<<"z="<<ptr->z<<endl;
	 cout<<"z="<<ptr->*pz<<endl;
	 
	 int (Point::*pgetx)();
	 pgetx=&temp::GetX;
	 cout<<"x="<<(t.*pgetx)()<<endl;
	 cout<<"x="<<(ptr->*pgetx)()<<endl;
}  	

2.指向類的靜態成員的指針

對類的靜態成員的訪問不依賴對象。
可以用普通指針來指向和訪問靜態成員。

三. 指向對象的常指針 和 指向常對象的指針

1.指向對象的常指針

(1)將指針變量聲明爲 const 型,這樣指針始終保持爲其初值(初始指向),不能改變。
(2)定義指針對象的常指針的一般形式:

類名 * const 指針變量名;
//也可以在定義的時候初始化指針變量,即:
類名 * const 指針變量名= &對象名;

舉例:

Time t1(20,13,14),t2;
Time * const ptr1; //const在指針變量名之前,規定ptr1的值是常值,指向不變
ptr1 = &t1; //ptr1初始化指向t1,此後不能再改變指向
ptr1 = &t2; //報錯
// 定義時初始化
Time * const ptr1 = &t1;

(3)指向對象的常指針變量的值(指向的地址)不能改變,始終指向同一個對象,但可改變其所指對象的值
(4)往往用常指針作爲函數的形參,不允許在函數執行過程中修改指針變量的值,使其始終指向原來的對象。
常引用(引用就是起別名,引用名和變量名指向同一塊內存單元)和常對象做函數形參,可提高效率。

2.指向常對象的指針變量

先回憶一下怎麼定義一個指向常變量的指針變量:

const char *ptr;

const 的位置在最左側,與類型名char緊連,表示指針變量ptr指向的char型變量是常變量不能通過ptr來改變其值

(1)定義指向常變量的指針變量的一般形式
const 類型名 *指針變量名
(2)指向常變量的指針變量 -------- 幾句繞口令

<1> 如果一個變量已被聲明爲常變量,只能用指向常變量的指針變量指向它。

const char c[]="xiaobencong";
const char *p1;  //定義p1爲指向const型的char變量的指針變量
p1 = c;   //正確,p1指向常變量
char *p2 = c; //報錯,p2不是指向常變量的指針變量

<2> 指向常變量的指針變量還可以指向未被聲明爲const的變量。此時不能通過此指針變量改變該變量的值。

char c1 = 'a';
const char *p; //定義p1爲指向const型的char變量的指針變量
p=&c1;  //p指向字符變量c1
*p='b';  //錯誤,不能通過p改變c1的值
c1='b';   //正確,沒有通過p訪問c1,c1不是常變量

<3> 如果函數的形參是指向非const型變量的指針,實參只能用指向非const變量的指針,在執行函數的過程中可以改變形參指針變量所指向的變量的值。
<4> 如果形參是指向const型變量的指針,執行函數過程中不能改變指針變量所指向的變量的值。允許實參是指向const變量的指針,或指向非const變量指針。

const char s[]="xiao";
void fun(char *ptr); //函數形參是指向非const型變量的指針
fun(str);//調用fun函數,實參是const變量的地址,非法

上述幾句話的總結:,指向常變量的指針變量可以指向const變量和非const變量,而指向非const型變量的指針變量只能指向非const變量。

(3)指向常對象的指針變量

將上述 “變量”變爲“對象” 即可得到指向常對象的指針變量。
<1> 如果一個對象已經被聲明爲常對象,只能用指向常對象的指針變量指向它,而不能用一般的指針變量(非const)去指向。
<2> 如果定義了一個指向常對象的指針變量,並使它指向一個非const的對象,不能通過指針改變所指對象的值,但指針變量本身的值是可以改變的

Time t1(10,12,15),t2; //定義Time類對象t1,非const型
const Time *p = &t1; //定義p是指向常對象的指針變量,並指向非const型對象t1
t1.hour = 13;  //合法, t1不是常變量
(*p).hour=13;  //不合法,不能通過指針變量改變t1的值
p=&t2;   //合法

注意:

Time * const p;   //指向對象的常指針變量:指針始終指向那個對象,對象值可變,可由指針改變
const  Time *p;  //指向常對象的指針變量:對象值不變,指針可改變指向別的對象

<3> 指向常對象的指針最常用於函數的形參,目的是在保護形參指針指向的對象使它在函數執行過程中不被修改。
舉例:

int main(){
	void fun(const Time *p);  //形參是指向常對象的指針變量
	Time t1(12,24,56);  //t1不是常對象
	fun(&t1);  //實參是非const型對象t1的地址
	return 0;
	}
void fun(const Time *t)  
{  
	p->hour =19;  //錯誤   fun函數形參的性質不允許改變p所指向的對象的值
}

簡記:

  1. 當希望在調用函數時對象的值不被修改,就應當把形參定義爲指向常對象的指針變量,同時用對象的地址作爲實參(對象可以是const型或非const型)。
  2. 當要求該對象不僅在調用函數過程中不被改變,而且要求它在執行中不改變,則應把它定義爲const型。

四. 動態創建和釋放對象數組

在這裏插入代碼片
#include<iostream>
using namespace std;
class Point {
public:
 	Point() {}
 	Point(int xx, int yy);
 	~Point() {}
 	int getX() { return x; }
 	int getY() { return y; }
 	void moveTo(int xx, int yy) {
  		x = xx;y = yy;
  		cout << x << "," << y;
 	}
private:
 	int x, y;
};

class ArrayOfPoints{
public:
 	ArrayOfPoints(int n)
 	{	numberOfPoints = n;
  		points = new Point[n];
 	}
 	~ArrayOfPoints()
 	{    cout << "Deleting..." << endl;
  		numberOfPoints = 0;
  		delete[] points; //刪除動態創建的對象數組
 	}
 	Point &getElement(int n)  // 用&
 	{ return points[n]; } //返回對象數組某一對象
private:
 	Point *points;
 	int numberOfPoints;
};

int main(){ 
	 int number;
	 cout << "Please enter the number of points:";
	 cin >> number;
	 ArrayOfPoints pointsArray1(number); //創建對象數組
	 pointsArray1.getElement(0).moveTo(5, 10); //訪問數組元素的成員
	 return 0;
}
#include<iostream>
using namespace std;
class Student {
public:
	 Student(int num, int score) {
	 	 m_number = num;
  		 m_score = score;
	 }
	 ~Student() { };
	 int getNumber() { return m_number; }
	 int getScore() { return m_score; }
private:
	 int m_number;
	 int m_score;
};
Student *max(Student *p) {
 	Student *q = p;
	 int count = 1;
	 while (count < 5) {
 		 p++;
 		 if (p->getScore() > q->getScore())
   		q = p;//q指向成績最大的對象
  		count++;
 	}
	 return q;
}
int main() {
	 Student *pStu = new Student[5]{ Student(201701,88),
	 				Student(201702,88),
	 				Student(201703,89),
	 				Student(201704,98),
	 				Student(201705,99) };
	 Student* max(Student *); //聲明max函數
	 cout << "成績最高的學號:" << max(pStu)->getNumber() << ", 分數:" << max(pStu)->getScore() << endl;
	 delete[]pStu;
	 return 0;
	}

五. 深拷貝與淺拷貝

深拷貝:當被複制的對象數據成員是指針類型時,不是複製該指針成員本身,而是將指針所指的對象進行復制。
淺拷貝:實現對象間數據元素的一一對應複製。

#include<iostream>
using namespace std;
class Point {
public:
	 Point() {}
	 Point(int xx, int yy);
	 ~Point() {}
	 int getX() { return x; }
	 int getY() { return y; }
	 void moveTo(int xx, int yy) {
	  	x = xx;y = yy;
	  	cout << "(" << x << "," << y << ")" << endl;
	 }
private:
 	int x, y;
};
class ArrayOfPoints
{
public:
 	ArrayOfPoints(int n) {
  		numberOfPoints = n;
  		points = new Point[n];
 	}
 	ArrayOfPoints(ArrayOfPoints& pointsArray);
 	~ArrayOfPoints() {
  		numberOfPoints = 0;
  		delete[] points; //刪除動態創建的對象數組
        }		
        Point &getElement(int n) { // 用&
          return points[n]; //返回對象數組某一對象
        }
private:
 	Point *points;
 	int numberOfPoints;
};
ArrayOfPoints::ArrayOfPoints(ArrayOfPoints& pointsArray){    //因爲類中含有指針變量,所以複製構造函數重寫
 	numberOfPoints = pointsArray.numberOfPoints;
 	points = new Point[numberOfPoints];
 	for (int i = 0; i < numberOfPoints; i++) {
  		points[i].moveTo(pointsArray.getElement(i).getX(),pointsArray.getElement(i).getY());
 	}
}
int main(){
	 int number;
	 cout << "輸入點的個數:";
	 cin >> number;
	 ArrayOfPoints pointsArray1(number); //創建對象數組1
	 for (int i = 0;i < number;i++)
	  	pointsArray1.getElement(i).moveTo(5 * i + 5, 10 * i + 10);  //對象數組1每個點對象的座標移動
	 ArrayOfPoints pointsArray2(pointsArray1); //利用默認複製構造函數,1賦給2
	 cout << "複製後的pointArray2:" << endl;
	 for (int i = 0;i < number;i++) {  //讀取檢驗2的每個對象的座標賦值完成沒有
	 	 cout << "Point[" << i << "] of array2: ("
	   	      << pointsArray2.getElement(i).getX()
	   	      << ", " << pointsArray2.getElement(i).getY() << ")" << endl;
	 }
	 //改變pointArray1的每個對象的值之後
	 for (int i = 0;i < number;i++)
	  	pointsArray1.getElement(i).moveTo(5 * i + 10, 10 * i + 20);  //輸出改變後的pointArray1
		 cout << "輸出pointArray2:" << endl;      
	 for (int i = 0;i < number;i++){
	  	cout << "Point[" << i << "] of array2: ("
	 	     << pointsArray2.getElement(i).getX()                           //淺拷貝:實現對象間數據元素的一一對應複製。
	  	     << ", " << pointsArray2.getElement(i).getY() << ")" << endl;   //pointArray2按理說應該仍保持第一次賦值的結果,但現在卻和pointArray1的結果綁定相同  即出現了淺拷貝
	 }
	 return 0;
}

運行結果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章