1. 程式人生 > >C++程式設計學習筆記 複習/拾遺 5

C++程式設計學習筆記 複習/拾遺 5

靜態與友元

封裝性:物件的狀態資訊隱藏在物件內部,不允許外部程式直接訪問物件內部資訊,而是通過該類所提供的方法來實現對內部資訊的操作與訪問。

類外如何訪問被封裝的成員(private 和 protected成員)?
–通過物件名.公有成員函式

良好的封裝性,考慮角度: 將物件的成員變數與實現細節隱藏起來,不允許外部訪問。
把方法暴露出來,讓方法來控制對這些成員變數進行安全的訪問與操作。

例6.1 使用普通函式計算圓環面積

#include <iostream>
using namespace std;
class Circle{	
public:	
    Circle(double a=0.0)  //帶預設引數值的建構函式
    {  r=a;  } 
	double Area()
	{return 3.14*r*r;}		
private:
	double r;
};
double AreaRing(Circle &c1,Circle &c2)//普通函式 
{ return c1.Area()-c2.Area();//通過物件.成員函式
}
//由於封裝,類外無法訪問私有資料成員,只能通過公有函式呼叫

int main()
{
	Circle c1(10),c2(5);//定義圓類物件
	cout<<AreaRing(c1,c2)<<endl;//呼叫普通函式
	return 0;
}

例6.2 全域性變數使用示例

統計Circle類被建立物件的次數

#include <iostream>
using namespace std;
int total=0;//使用全域性變數
class Circle{	
public:	
    Circle(double a=0)  //帶預設引數值的建構函式
    {  r=a;total++;  } 
    Circle(Circle &x)//拷貝建構函式
    {	r=x.r;
	    total++;
    }
	double Area()
	{return 3.14*r*r;}		
private:
	double r;
};
int main()
{
	Circle c1(10),c2(5),c3,c4(c1);//定義圓類物件
	cout<<"定義物件的個數="<<total<<endl;//通過全域性變數輸出定義物件的個數
	return 0;
}

Static的使用

Static:(靜態)類資料和(靜態)類函式
類中有一種特殊的資料成員或成員函式,它不屬於某個物件,不能通過某個物件來引用,在宣告前加上static關鍵字。

不論定義多少個物件,在記憶體中,靜態的資料備份只有1份。

靜態資料可以被所有的物件共享,生命週期從建立開始到程式執行結束。

C++一個類中的四種成員

靜態資料成員和非靜態資料成員、靜態函式和非靜態函式。

①非靜態資料成員被放在每一個物件體內作為物件專有的資料成員。
②靜態資料成員被提取出來放在程式的靜態資料區內,為該類所有物件共享,因此只存在一份。
③靜態和非靜態成員函式最終都被提取出來放在程式的程式碼段中併為該類所有物件共享,因此每一個成員函式也只能存在一份程式碼實體。在c++中類的成員函式都是儲存在靜態儲存區中的,那靜態函式也是儲存在靜態儲存區中的,他們都是在類中儲存同一個備份。
因此,構成物件本身的只有資料,任何成員函式都不隸屬於任何一個物件,非靜態成員函式與物件的關係就是繫結,繫結的中介就是this指標。成員函式為該類所有物件共享,不僅是出於簡化語言實現、節省儲存的目的,而且是為了使同類物件有一致的行為。同類物件的行為雖然一致,但是操作不同的資料成員。

靜態機制-同類的所有物件共享

靜態成員:static

static所修飾的資料成員為靜態資料成員(即類資料成員),所修飾的成員函式為靜態成員函式(即類成員函式)。

靜態資料成員是同類的所有物件共享的成員,而不是某一物件的獨有的成員,它的值對每個物件都是一樣的。對靜態資料成員的值的更新,即是對所有物件的該靜態資料成員值的更新。
靜態資料成員初始化須在類的外部進行,與一般資料成員初始化不同,它的格式如下:

<資料型別><類名>::<靜態資料成員名>=<值> 

靜態資料成員可被該類的靜態成員函式使用,也可被該類的普通成員函式使用。

使用示例1

#include <iostream>
using namespace std;
class Circle{	
public:	
    Circle(double a=0)  //帶預設引數值的建構函式
    {  r=a;total++;  } 
     Circle(Circle &x)//拷貝建構函式
    {	r=x.r;
	    total++;
    }
	double Area()
	{return 3.14*r*r;}	
	static int total;//定義靜態資料成員	
private:
	double r;
};
int Circle::total=0;
int main()
{
	Circle c1(10),c2(5),c3,c4(c1);//定義圓類物件
	cout<<"定義物件的個數="<<Circle::total<<endl;//通過靜態成員輸出定義物件的個數
	return 0;
}

使用例項2

#include <iostream>
using namespace std;
class Circle{	
public:	
    Circle(double a=0)  //帶預設引數值的建構函式
    {  r=a;total++;  } 
    Circle(Circle &x)
    {	r=x.r;
	    total++;
    }
	double Area()
	{return 3.14*r*r;}	
	int GetTotal()//靜態成員可以被該類的普通成員函式使用。
	{
		return total;
	}
private:
	double r;
	static int total;//定義靜態資料成員	
};
int Circle::total=0;
int main()
{
	Circle c1(10),c2(5),c3,c4(c1);//定義圓類物件
	cout<<"定義物件的個數="<<c1.GetTotal()<<endl;//如果沒有定義任何物件,就無法訪問類資料了

	return 0;
}

靜態成員用下劃線表示

在這裡插入圖片描述

靜態成員函式

  1. 靜態成員函式和靜態資料成員一樣,它們都屬於類的靜態成員,但它們都不是某一物件的成員。
  2. 類外程式碼可以使用類名和作用域操作符來呼叫靜態成員函式。
  3. 靜態成員函式只能引用屬於該類的靜態資料成員或靜態成員函式。

例6.5 靜態成員函式使用示例

#include <iostream>
using namespace std;
class Circle{	
public:	
    Circle(double a=0)  //帶預設引數值的建構函式
    {  r=a;total++;  } 
    Circle(Circle &x)//拷貝建構函式定義 
    {	r=x.r;
	    total++;
    }
	double Area()
	{return 3.14*r*r;}	
	static int GetTotal()	
	{
		return total;
	}
private:
	double r;
	static int total;//定義靜態資料成員	
};
int Circle::total=0;
int main()
{
	Circle c1(10),c2(5),c3,c4(c1);//定義圓類物件
	cout<<"定義物件的個數="<<Circle::GetTotal()<<endl;//如果沒有定義任何物件,也可以通過Circle::GetTotal()訪問類資料
	return 0;
}

類外不同物件之間資料共享:友元機制

友元—friend

友元是C++提供的一種破壞資料封裝和資料隱藏的機制。 通過將一個模組宣告為另一個模組的友元,一個模組能夠引用到另一個模組中本是被隱藏的資訊。
可以使用友元函式、友元成員函式和友元類。 為了確保資料的完整性,及資料封裝與隱藏的原則,建議儘量不使用或少使用友元。

友元函式

友元函式是在類宣告中由關鍵字friend修飾說明的非成員函式,在它的函式體中能夠通過物件名訪問 private 和 protected成員
作用:增加靈活性,使程式設計師可以在封裝和快速性方面做合理選擇。
訪問物件中的成員必須通過物件名。

注意:一個函式可以是多個類的友元函式,需要在各個類中分別宣告,並且友元關係不具有傳遞性。

例6.6 使用友元函式計算圓環面積

#include <iostream>
using namespace std;
class Circle{	
public:	
    Circle(double a=0.0)  //帶預設引數值的建構函式
    {  r=a;  } 
	double Area()
	{return 3.14*r*r;}	
	friend double AreaRing(Circle &c1,Circle &c2);//友元函式宣告 
	//注意:友元關係是單向的
private:
	double r;
};
double AreaRing(Circle &c1,Circle &c2)//普通友元函式 
{ return 3.14*c1.r*c1.r-3.14*c2.r*c2.r;//通過友元函式訪問私有資料成員 
}//由於友元函式打破了封裝,類外可以訪問私有資料成員
int main()
{
	Circle c1(10),c2(5);//定義圓類物件
	cout<<AreaRing(c1,c2)<<endl;//呼叫友元函式
	return 0;
}

友元成員函式

若一個類的成員函式是另一個類的友元,則稱該成員函式為另一個類的友元成員函式。
宣告語法:將某個類的成員函式在另一個類中使用friend修飾說明,並且還加上成員函式所在的類名。

例6.7:使用友元成員函式輸出圓資訊

#include <iostream>
using namespace std;
class Point;//前向引用宣告 
class Circle{	
public:	
    Circle(double a=0.0)  //帶預設引數值的建構函式
    {  r=a;  } 
	double Area()
	{return 3.14*r*r;}	
	void Show(Point &p);//該函式是Circle的成員函式,Point類的友元函式 
private:
	double r;
};
class Point
{private:
	double x,y;	
public:
	Point(double a=0.0,double b=0.0)
	{		x=a;y=b;	}
	void Show()
	{		cout<<x<<","<<y<<endl;	}
	double GetX()
	{return x;}
	double GetY()
	{return y;}
	friend void Circle::Show(Point &p);//友元函式宣告
};
void Circle::Show(Point &p)
{
      cout<<"半徑="<<r<<endl<<"圓心=";
        cout<<p.x<<","<<p.y<<endl;//等價於p.Show();		
}int main()
{
Circle c(10);//定義圓類物件
Point p(100,100);//定義點類物件 
c.Show(p);//呼叫友元成員函式
return 0;
}

前向引用宣告

類應該先宣告,後使用。
如果需要在某個類的宣告之前,引用該類,則應進行前向引用宣告。
前向引用宣告只為程式引入一個識別符號,但具體宣告在其它地方。

友元類

若一個類為另一個類的友元,則此類的所有成員都能訪問對方類的私有成員。
//從這個角度來看,友元類的成員函式都是另一個類的友元成員函式
宣告語法:將友元類名在另一個類中使用friend修飾說明。

#include <iostream>
using namespace std;
class Point;//前向引用宣告 
class Circle{	
public:	
    Circle(double a=0.0)  //帶預設引數值的建構函式
    {  r=a;  } 
	double Area()
	{return 3.14*r*r;}	
	void Show(Point &p); 
private:
	double r;
};
class Point
{private:
	double x,y;	
public:
	Point(double a=0.0,double b=0.0)
	{	x=a;y=b;	}
	void Show()
	{	cout<<x<<","<<y<<endl;	}
	double GetX()
	{return x;}
	double GetY()
	{return y;}
	friend class Circle;//友元類宣告,意味著Circle類的所有成員函式都是Point類的友元成員函式 
};
void Circle::Show(Point &p)
{
       cout<<"半徑="<<r<<endl<<"圓心=";
        cout<<p.x<<","<<p.y<<endl;//等價於p.Show();		
}
int main()
{
Circle c(10);//定義圓類物件
Point p(100,100);//定義點類物件 
c.Show(p);//呼叫友元類的成員函式
return 0;
}

常型別-共享資料保護

  • 常引用:被引用的物件不能被更新。
    const 型別說明符 &引用名
  • 常物件:必須進行初始化,不能被更新。
    類名 const 物件名 或const 類名 物件名
  • 常成員:用const修飾的類成員

例6.9 常引用做形參

#include <iostream>
using namespace std;
void display(const double& r);
int main( )
{   double d(9.5);
     display(d);
     return 0;
}
void display(const double& r)
//常引用做形參,在函式中不能更新 r所引用的變數。
{   cout<<r<<endl;   }
//注意:常物件引用用法與常物件一樣,只能呼叫常成員函式。

例6.10:常物件舉例
凡希望保證資料成員不被改變的物件,可以宣告為常物件。

#include <iostream>
using namespace std;
class A
{
     public:
        A(int i,int j) {x=i; y=j;}
     void print( );
     void print( ) const;
     private:
         int x,y;
};
void A::print ( )
{     cout<<"普通成員函式:"<<x<<","<<y<<endl;
}
void A::print ( ) const
{     cout<<"常成員函式:"<<x<<","<<y<<endl;
}//通過常物件只能呼叫它的常成員函式
int main()
{
 A const a(3,4); //a是常物件,不能被更新
 a.print();
 return 0;
}

用const修飾的物件成員

常成員函式:使用const關鍵字說明的函式。
常成員函式不更新物件的資料成員。
常成員函式說明格式: 型別說明符 函式名(引數表)const; 這裡,const是函式型別的一個組成部分,因此在實現部分也要帶const關鍵字。
const關鍵字可以被用於參與對過載函式的區分
常資料成員
使用const說明的資料成員。
普通成員函式可以呼叫常資料成員

例6.11 常資料成員舉例

#include <iostream>
using namespace std;
class A
{public:
	A(int i,int j);
	void print( );	
private:
	const int a;//常資料成員a 	
	int b;//普通資料成員b 
};
A::A(int i,int j):a(i) //建構函式
//常成員只能通過初始化列表給物件的常資料成員a賦初值
{ b=j;}
void A::print()
{    cout<<a<<","<<b<<endl; }//普通成員函式可以呼叫常資料成員
int main( )
{//建立物件a1和a2,並以100和0作初值
    A a1(100,10),a2(0,2);  
    a1.print();
    a2.print();
   return 0;
}