C++的數據共享與保護你了解嗎
1.作用域
作用域是一個標識符在程序正文中有效的區(qū)域
作用域關系從大到小為:
命名空間作用域 > 類作用域 > 局部作用域
標識符的有效范圍就是標識符的可見性,可見性的一般規(guī)則為:
- 標識符聲明在前,使用在后
- 在同一作用域中,不能聲明同名的標識符
- 在沒有包含關系的不同的作用域中聲明的同名標識符,互不影響
- 如果在多個作用域中定義了同名標識符,則外層標識符在內層不可見
2.對象生存期
如果對象生存期與程序的運行期相同,則它具有靜態(tài)生存期
局部生存期對象誕生于聲明點,結束于聲明所在塊執(zhí)行完畢之時
- 命名空間作用域中聲明的對象都具有靜態(tài)生存期
- 定義時未指定初值的基本類型靜態(tài)生存期變量,會被賦予0值初始化
- 如果要在函數內部的局部作用域中聲明具有靜態(tài)生存期的對象,則要使用關鍵字static
void f() { static int m=0; //m僅在f中能訪問 m++; cout<<m<<endl; }
靜態(tài)數據成員
如果某個屬性為整個類所共有,可以采用static關鍵字來聲明為靜態(tài)成員。
- 靜態(tài)成員在每個類中只有一個副本,由該類的所有對象共同維護和使用
- 在類中不能對static靜態(tài)數據成員進行初始化,要初始化必須在類外進行定義
class A{ public: static int m; }; int A::m=20; //在類外進行初始化 int main(){ A a,b; a.m=10; //可以賦值 cout<<a.m<<endl; cout<<b.m<<endl; //兩者的值相同 }
靜態(tài)成員函數
- 靜態(tài)成員函數可以通過類名或對象名來進行調用,非靜態(tài)成員函數只能通過對象名來調用
- 靜態(tài)成員函數可以直接訪問該類的靜態(tài)數據和函數成員,而訪問非靜態(tài)成員,必須通過對象名
class A{ public: static void f(A a); static void g(){ cout<<"hello"<<endl; } void fun(){ cout<<"world"<<endl; } private: int x; static int y; }; int A::y=10; void A::f(A a){ cout<<a.x<<endl; //必須通過對象名訪問 cout<<y<<endl; //直接訪問該類的靜態(tài)數據成員 g(); //直接訪問該類的靜態(tài)成員函數 } int main(){ A a; A::f(a); //直接通過類名調用 return 0; }
3.類的友元
友元關系是一種數據共享機制,通過友元關系,一個普通函數或者類中的成員函數可以訪問封裝于另外一個類中的數據
為了確保數據的完整性和封閉性,建議盡量少地使用友元
友元函數
友元函數是類中用關鍵字friend修飾的非成員函數,在它的函數體中可以通過對象名訪問類的private和protected成員
class A{ public: friend int main(); private: int x; }; int main(){ A a; a.x=5; //訪問 private對象 }
友元類
若B類為A類的友元類,則B類的所有成員函數都是A類的友元函數,都可以訪問A類的private和protected成員
class A{ public: friend class B; private: int x,y; void f(){ cout<<x<<endl; } }; class B{ public: void print(){ a.x=5; a.y=10; cout<<a.y<<endl;//可以訪問A類對象的私有成員 a.f(); //可以訪問A類對象的私有函數 } private: A a; };
注意(?。。。?/p>
1.友元關系是單向的。
B類是A類的友元,B能訪問A的私有數據,但A不能訪問B的私有數據
2.友元關系不能傳遞。B是A的友元,C是B的友元,C和A之間如果沒有聲明就不存在友元關系
3.友元關系不能被繼承。
4.共享數據的保護
常對象
數據成員值在對象的整個生存期間不能被修改的對象叫做常對象,一般用const進行修飾
常對象必須進行初始化,而且不能被更新
聲明常對象的語法形式為:
const 類型說明符 對象名;
class A{ public: A(int i,int j):x(i),y(j){···} private: int x,y; }; int main(){ const A a(3,4); //a為常對象,不能被更新 }
常成員函數
使用const關鍵字修飾的函數為常成員函數
語法:
類型說明符 函數名(參數表)const;
如果將一個對象說明為常對象,通過該常對象只能調用它的常成員函數,不能調用其他函數(常對象唯一的對外接口方式)
class A{ public: A(int i,int j):x(i),y(j){···} void print(){ cout<<x<<" "<<y<<endl; } void print() const{ //常成員函數 cout<<x<<" const "<<y<<endl; } private: int x,y; }; int main(){ A a(1,2); a.print(); //調用 void print() const A b(3,4); b.print(); //調用 void print() const }
使用const說明的數據成員為常數據成員
類中說明了常數據成員,任何函數不能對它賦值,構造函數對它進行初始化只能通過初始化列表
class A{ public: //常數據成員只能通過構造函數的初始化列表來獲得初值 A(int i):a(i){···} private: const int a; //常數據成員 static const int b;//靜態(tài)常數據成員 }; const int A::b=10; //靜態(tài)常數據成員在類外初始化
常引用
如果在聲明引用時用const修飾,被聲明的引用就是常引用,常引用所引用的對象不能被更新
常引用的聲明形式:
const 類型說明符 & 引用名;
一個常引用,無論是綁定到一個普通對象,還是一個常對象,通過該引用訪問該對象時,都只能把該對象當做常對象
class Point{ public: Point(int x,int y):x(x),y(y){} friend float dist(const Point &p1,const Point &p2); private: int x,y; }; float dist(const Point &p1,const Point &p2){ //常引用作形參 double x=p1.x-p2.x; double y=p1.y-p2.y; return sqrt(x*x+y*y); } int main(){ const Point m1(1,1),m2(4,5); cout<<dist(m1,m2)<<endl; //兩點間距離 }
Tips
- include書寫方式
- include <文件名>,按照標準方式搜索嵌入文件,文件位于編譯環(huán)境include子目錄下。示例:
include <iostream>
- include “文件名”,在當前目錄下搜索嵌入文件,如果搜不到則轉為標準搜索。示例:
include "point.h"
- include <文件名>,按照標準方式搜索嵌入文件,文件位于編譯環(huán)境include子目錄下。示例:
- 外部變量
- 如果一個變量可以在本文件和其他文件中使用,稱為外部變量,用 extern 關鍵字說明
extern int n;//聲明一個在其他文件定義的外部變量n
對于外部函數,加不加 extern 效果都是一樣的
如果在定義變量和函數時使用static關鍵字,可以讓該變量和函數無法被其他編譯單元引用
- 動態(tài)內存分配
運算符new的功能是動態(tài)分配內存,語法形式為
new 數據類型(初始化參數列表)
對于基本數據類型,如果不希望在分配內存后設立初值,可以把括號省去
int* point = new int;
如果保留括號,但括號中不寫任何數值,則表示用0來進行初始化
int* point = new int( );
運算符delete用來刪除由new建立的對象,釋放指針所指向的內存空間,格式為:
delete 指針名;
用new分配的內存,必須用delete加以釋放,否則會造成“內存泄漏”,而且只用delete進行一次刪除,對同一內存空間多次使用delete進行刪除會導致運行錯誤
用new創(chuàng)建一維數組時,在方括號后加或者不加小括號的區(qū)別和“new T( )”一樣,用delete刪除時在指針名前面要加“[ ]”
int* p = new int[100]; //不設置初值
int *p = new int[100] ( ); //用0進行初始化
delete[ ] p;
- 深復制與淺復制
- 淺復制只是對指針的復制,復制后兩個指針指向同一個內存空間;
- 深復制不但對指針進行復制,而且對指針指向的內容進行復制,經深復制后的指針是指向兩個不同地址的指針
默認復制構造函數,進行的是淺復制,對指針復制后會出現兩個指針指向同一個內存空間的情況,內存空間會被析構函數釋放兩次,導致運行錯誤
解決這一問題必須要自己定義復制構造函數,使復制后的對象指針成員有自己的內存空間,即進行深復制,這樣就避免了內存泄漏發(fā)生。
- this指針
- this指針是一個隱含于每一個類的非靜態(tài)成員函數中的特殊指針,它用于指向正在被成員函數操作的對象
- 當局部作用域中聲明了與類成員同名的標識符時,對該標識符的直接引用代表的是局部作用域中所聲明的標識符,這時為了訪問該類成員,可以通過this指針
class A{ public: void display(int x){ this->x=x; //前一個x為數據成員,后一個x為形參 } private: int x; };
總結
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!