*數據的共享與保護: * 1.作用域: * 作用域是一個標識符在程序正文中有效的區域。C++中標識符的作用域有函數原型作用域、局部作用域(塊作用域)、類作用域和命名空間作用域。 * (1).函數原型作用域: * 函數原型作用域是C++中最小的作用域,在函數原型中一定要包含形參的類型說明。在函數原型聲明時形式參數的作用范圍就是函數原型的作用域。如:double area(double radius);標識符radius的作用范圍就在函數area形參列表的括號之間。 * 由於在函數原型的形參列表中起作用的只是形參類型,標識符並不起作用,因此在程序中是允許省去的,但是為了程序的可讀性,通常還是要在函數原型聲明時給出形參標識符。 * (2).局部作用域: * 函數形參列表中形參的作用域,從形參列表中的聲明處開始,到整個函數體結束為止;函數體內聲明的變量,其作用域從聲明處開始,一直到聲明所在塊結束的大括號為止;具有局部作用域的變量也稱為局部變量。 * (3).類作用域: * 類可以看作是一組有名成員的集合,類x的成員m具有類作用域,對m的訪問方式有3中:如果在x的成員函數中沒有聲明同名的局部作用域的標識符,那麼在該函數內可以直接訪問成員m;通過表達式x.m或者x::m。即程序中訪問對象成員的最基本方法;通過ptr->m這樣的表達式,其中ptr為指向x類的一個對象的指針。 * (4).命名空間作用域: * 一個大型的程序通常有不同模塊構成,不同的模塊有可能有不同人員開發的。不同模塊中的類和函數之間可能發生重名。命名空間就會消除這些錯誤。語法結構: * namespace 命名空間名{ * //命名空間內的各種聲明(函數聲明、類聲明、。。。); * } * 一個命名空間確定了一個命名空間作用域,凡是在該命名空間之內聲明的、不屬於前面所說各個作用域的標識符,都屬於該命名空間作用域。在命名空間內部可以直接引用當前命名空間中聲明的標識符,如果需要引用其他命名空間的標識符,需要使用下面語法: * 命名空間::標識符 * * namespace someNs{ * class SomeClass{...}; * }; * 如果要引用類名SomeClass或函數名SomeFunc,需要使用下面的方式: * someNs::SomeClass obj; * 有時,在標識符前面總使用這樣的命名空間限定會顯得過於冗長,為了解決這個問題,C++又提供了using 語句,using語句有兩種: * using 命名空間::標識符; * using namespace 命名空間名; * 命名空間也允許嵌套: * namespace OuterNs{ * namespace InnerNs{ * class SomeClass{...}; * } * } * 引用其中的SomeClass類,需要使用OuterNs::InnerNs::SomeClass的語法; * 此外,還有兩種比較特殊的命名空間: * 全局命名空間和匿名命名空間。全局命名空間是默認的命名空間,在顯示聲明的命名空間之外聲明的標識符都在一個全局命名空間中,匿名命名空間是在一個需要顯示聲明的沒有名字的命名空間。聲明如下: * namespace{ * 匿名命名空間內的各種聲明(函數聲明、類聲明、...); * } * 具有命名空間作用域的變量又稱為全局變量; * * 2.對象的生存期: * (1).靜態生存期: * 如果對象的生存期與程序的運行期相同,則稱它具有靜態生存期;在命名空間作用域中聲明的對象都是具有靜態生存期的。如果在函數內部的局部作用域中聲明具有靜態生存期的對象,則要使用關鍵字static。如:static int i; * 局部作用域中的靜態變量的額特點是:它並不隨著每次函數調用而產生副本,也不會隨著函數返回而失效。也就是說當一個函數返回後,下一個再調用時,該變量還是上一次的值。即使發生遞歸調用也不會為該變量建立新的副本,該變量會在每次調用間共享。 * (2).動態生存期: * 局部生存期對象誕生於聲明點,結束聲明所在的執行完畢之時。類成員對象也有自己的生存期。不用static修飾的成員對象其生存期都與它們所屬對象的生存期保持一致。 * * 3.類的靜態成員: * 在結構化程序設計中程序模塊的基本單位是函數,因此模塊間對內存中數據的共享是通過函數與函數之間的數據共享來實現的,其中包括兩個途徑:參數傳遞和全局變量。 * (1).靜態數據成員: * 如果某個屬性為整個類所共有,不屬於任何一個具體對象,則采用static關鍵字來聲明一個靜態成員。靜態成員在每個類只有一個副本,由該類所有對象共同維護和使用,從而實現了同一類的不同對象之間的數據共享。類屬性是描述類的所有對象共同特征的一個數據項,對於任何對象實例,它的屬性值是相同的靜態數據成員具有靜態生存期。由於靜態數據成員不屬於任何一個對象,因此可以通過類名對它進行訪問,一般用的語法是: * 類名::標識符; * * (2).靜態函數成員: * 靜態成員函數可以直接訪問該類的靜態數據和函數成員。而訪問非靜態成員,必須通過對象名。 *class A{ public: static void f(A a); private : int x; }; void A::f(A a){ cout<<x;// This is WRONG! cout<<a.x;//之所以在靜態成員函數中訪問類的非靜態成員需要指明對象是因為對靜態成員函數的調用是沒有目的對象的,因此不能像非靜態成員函數那樣隱含地通過目的對象訪問類的非靜態成員。 } * * 4.類的友元: * 友元關系提供了不同類或對象的成員函數之間、類的成元函數與一般函數之間的關系進行數據共享的機制。通俗的說,就是一個類主動聲明哪些其他類或函數是它的友員,今兒給它們提供對本類的訪問特許。通過友元關系一個普通函數或者類的成員函數可以訪問封裝與另一個類中的數據。從一定程度上講,與友元關系是對數據隱蔽和封裝的破壞。 * 在一個類中可以利用關鍵字friend將其他函數或類生命為友元。如果友元是一般函數或類的成員函數,稱為友元函數;如果友元是一個類,則稱為友元類,友元類的所有函數都自動成為友元函數。 * (1).友元函數在類中用關鍵詞friend修飾的非成員函數。友元函數可以使一個普通函數也可以是其他類的成員函數。 * #include "iostream" #include "cmath" using namespace std; class Point{ public: Point(int x=0,int y=0):x(x),y(y){}; int getX(){return x;} int getY(){return y;} friend float dist(Point &p1,Point &p2);//友元函數聲明; private: int x,y; }; //友元函數dist的定義 float dist(Point &p1,Point &p2){ double x=p1.x-p2.x; double y=p1.y-p2.y; return static_cast<float>(sqrt(x*x+y*y)); } int main(){ Point myP1(1,1),myP2(4,3); cout<<"The distance is:"; cout<<dist(myP1,myP2)<<endl; return 0; } * 在Point類中只聲明友元函數的原型,友元函數dist的定義在類外,可以看出友元函數通過對象名直接訪問了Point類的x和y屬性。 * * (2).友元類: * 若類A為B類的友元類,則A類的所有成員函數都是B類的友元函數,都可以訪問B類的私有和保護成員。 * class B{ * friend class A;//聲明A為B的友元類。 * }; * 聲明友元類是建立在類與類之間的練習,實現類與類之間數據的共享的一種途徑。 * #include <iostream> using namespace std; class A{ public: void display(){cout<<x<<endl;} int getX(){return x;} friend class B; private: int x; }; class B{ public : void set(int i); void display(); private: A a; }; void B::set(int i){ a.x=i;//因為B是A的友元,所以在B的成員函數中可以訪問A類的所有私有成員; } * * 注:友元關系是不能傳遞的,B是A的友元,C是B的友元,如果沒有聲明C是A的友元就沒有友元關系。友元關系是單向的,如果B是A的友元,B可以訪問A的私有數據和保護數據,但A的成員函數不能訪問B的私有和保護數據。友元關系是不能被繼承的,如果B是A的友元,B的派生類不能自動的成為A的友元。 * * 5.共享數據的保護: * (1).常對象: * 常對象的數據成員值在對象的整個生存期內不能被改變。也就是說常對象必須進行初始化,而且不能被更新。 * const 類型說明符 對象名; * class A{ public: A(int i,int j):x(i),y(j){}; private : int x,y; }; const A a(3,4);//a是常對象,不能被更新。 * * (2).const 修飾的類成員: * 常用成員函數:使用const關鍵字修飾的函數為常成員函數,聲明格式: * 類型說明符 函數名(參數表) const; * 如果將一個對象設置為常對象,則通過該常對象只能調用它的常成員函數,而不能調用其他成員函數; * const關鍵字可以用於對重載函數的區分,void print(); void print() const; * 常數據成員:使用const說明的數據成員為常數據成員,如果在一個類中說明了一個常數據成員,那麼在任何函數中都不能對該常數據成員賦值。 * #include <iostream> using namespace std; class A{ public: A(int i); void print(); private: const int a; static const int b; }; const int A::b=10;//靜態常數據成員在類外說明和初始化; A::A(int i):a(i){}//常數據成員只能通過初始化列表來獲取初值; void A::print(){cout<<a<<":"<<b<<endl;} int main(){ A a(100),a2(3); a.print(); a2.print(); return 0; } * * 常引用: * 如果在申明引用時用const修飾,被申明的引用就是常引用,常引用所引用的對象不能被更新。如果常引用作為形參,便不會發生對實參的更改。 * const 類型說明符 &引用名; * 非const的引用只能綁定到普通的對象,而不能綁定到常對象,但常引用可以綁定到常對象。一個常引用,無論是綁定到一個普通對象還是常對象,通過該引用訪問該對象時,只能把該對象當作常對象。這意味著對於基本數據類型的引用,則不能為數據賦值,對於類類型的引用,則不能修改它的數據成員,也不能調用它的非const的成員函數。 * * 6.C++多文件結構和編譯預處理命令: * (1).C++的一般結構: * 在多個文件結構中,#include指令的作用是將指定的文件嵌入到當前源文件中,這個被嵌入的文件可以使cpp文件,也可以是h文件。指令include有兩種寫法:#include<文件名>表示按照標准方式搜索要嵌入的文件,該文件位於編譯器環境的include子目錄下,一般嵌入系統提供的標准文件時采用的方式。另一種就是#include"文件名"表示首先在當前目錄下搜索要嵌入 的文件,如果沒有再按照標准方式搜索。 * (2).外部變量: * 外部變量可以在源文件中可以使用,還可以被其他文件使用。 命名空間作用域中定義的變量,默認情況下都是外部變量,但在其他文件中如果需要使用這一變量,需要用extern關鍵字加以聲明。 * (3).外部函數: * 在所有類之外聲明的函數(非成員函數),都是具有命名空間作用域的,如果沒有特殊說明,這樣的函數都可以在不同的編譯單元中被調用,只要在調用之前進行引用性聲明。也可以在聲明函數原型或定義函數時用extern修飾。 * * 7.標准C++庫: * C++的庫中保留了大部分C語言系統函數和另外預定義的模板和類。使用標准C++庫時,還需要加入下面一條語句來將指定命名空間中的名稱引入到當前作用域中: * using namespace std; * 如果不使用using namespace std,就需要在使用std命名空間中的標識符時冠以命名空間名std::; * (1).編譯預處理: * #include指令: * 文件包含指令,起作用是將另一個源文件嵌入到當前源文件中該點處,通常用#include指令來嵌入頭文件。 * #define和#undef指令: * #define曾經在C程序中被廣泛使用,但#define能完成的一些功能,能夠被C++引入的一些語言特性很好的代替。在C語言中用#define來定義符號常量,如:#define PI 3.14;在C++中也同樣定義符號常量,但是更好地方法是在類型說明語句中用const修飾。#undef的作用是刪除由#define定義的宏,使之不再起作用。 * 條件編譯指令: * #if 常量表達式 或 #ifdef 標識符 或 #ifndef 標識符 * 程序段; * #elif * 程序段; * #else * 程序段; * #endif * */