C++学习笔记:(三)静态与名字空间
目錄
5.靜態與命名控制
5.1靜態數據成員
5.2靜態成員函數
5.3靜態對象
5.4類作用域及對象的生存期
5.5命名空間
5.靜態與命名控制
通常,在函數體內定義一個變量時,每次函數調用時編譯器會為這些內部變量分配內存。如果這個變量有一個初始化表達式,那么每當程序運行到此處,初始化就被執行。如果想在兩次函數調用之間保留一個變量的值,通常的做法是定義一個全局變量來解決,但是這個變量就不僅僅受這個函數的控制,從某種程度上說存在不安全因素。為了解決這個問題,C和C++語言允許在函數內部創建一個static對象,這個對象存儲在程序的靜態數據區中,而不是堆棧中。這個對象只在函數第一次調用時初始化一次,以后它將在兩次函數操作間保持它的值。
?
5.1靜態數據成員
當聲明一個類后,可以建立該類的多個對象,每個對象都有類中所定義的數據成員的拷貝,對應不同的存儲空間,各個對象相互獨立,實現了數據的封裝與隱藏;但在有些情況下,類中的某一數據成員是該類所有對象所共有的,如果采用前面所述的數據成員的定義,則不能實現數據成員的共享。
例如建立一個學生成績的線性表。設計學生類Student,抽象出的屬性:姓名、學號、成績;用構造函數實現學生的初始化,用成員函數Print實現輸出每個學生信息和總人數。
Class Student { private:char *name;int stu_no;int total;float score; public:Student(char *na, int no, float sco);void Print(); };想要統計學生總人數,一種方法是將需要共享的數據成員定義成全局變量,但這樣做將帶來安全隱患,如破壞了類的封裝特性、不利于信息隱藏等。另一種方法是在類中增加一個數據成員來存放總人數。但是這樣做,在用Student定義對象時,每定義一個對象,則該對象中就會有存放總人數的數據成員的副本。而總人數對于所有對象來說是一樣的。因此當總人數發生改變時,所有對象中存放總人數的數據成員都要同時改變,就需要在聲明一個專門用來記錄變化的總人數的成員函數。這樣做很不方便,也容易造成數據的不一致。由于這個數據是所有對象所共享的,C++提供了靜態數據成員來解決該類問題。類的靜態數據成員擁有一塊單獨的存儲區,不管用戶創建了多少個該類的對象。所有這些對象的靜態數據成員都共享這一塊靜態存儲空間,這就為這些對象提供了一種互相通信的方法。因此可將該數據成員定義為靜態數據成員。
靜態數據成員的定義: static 類型名 靜態成員名; 靜態數據成員的初始化: 類型 類名::靜態數據成員 = 初始化值; #include <iostream> #include <iomanip> #include <string.h> using namespace std; class Student {private:char *name;int stu_no;float score;static int total;public:Student(char *na, int no, float sco);void Print(); };int Student::total = 0;Student::Student(char *na, int no, float sco) {name = new char[strlen(na)+1];strcpy(name, na);stu_no = no;score = sco;total++; }void Student::Print() {cout << "NO." << total << "student:" << name << setw(4) << stu_no << setw(4) << score <<endl;cout << "Total:" << total <<endl; }int main() {Student s1("zhangming", 1, 90);s1.Print();Student s2("wanglan", 2, 95);s2.Print();Student s3("yumin", 3, 87);s3.Print();return 0; }注意:
(1)靜態數據成員聲明時,加關鍵字static說明。
(2)靜態數據成員的初始化應在類外聲明并在對象生成之前進行,默認時,靜態成員被初始化為零。
(3)靜態數據成員在編譯時創建并初始化。不能用構造函數進行初始化,靜態數據成員不能在任何函數內分配存儲空間和初始化。
(4)靜態數據成員屬于類,不屬于任何一個對象,只能在類外通過類名對它進行訪問。靜態數據成員的訪問形式是:
類名::靜態數據成員;
(5)靜態數據成員的主要用途是定義類的各個對象所公用的數據,如統計數據、平均數等。
(6)靜態數據成員和普通數據成員一樣,可以聲明為public、private或protected。
靜態數據成員的使用:
#include <iostream> using namespace std;class A {private:static int a;int b;public:A(int i, int j);void show(); };A::A(int i, int j) {a = i;b = j; }int A::a;void A::show() {cout << "This is static a:" << a <<endl;cout << "This is non-static b:" << b <<endl; }int main() {A x(1, 1);x.show();A y(2, 2);y.show();x.show();return 0; }?
5.2靜態成員函數
與靜態數據成員一樣,用戶可以創建靜態成員函數,方法是在成員函數名前用static修飾。靜態成員函數是為了類的全體服務而不是為了一個類的部分對象服務,因此就不需要定義全局函數。當產生一個靜態成員函數時,也就表達了與一個特定類的聯系。靜態成員函數不能訪問一般的數據成員和成員函數,它只能訪問靜態數據成員和其他的靜態成員函數。靜態成員函數是獨立于類對象而存在的,因此沒有this指針。
定義靜態成員函數: static 返回類型 靜態成員函數名(參數表); 調用方式: 類名::靜態成員函數名(實參表);靜態成員函數應用舉例:
#include <iostream> #include <iomanip> #include <string.h> using namespace std; class Employee {private:char *name;int number;static int total;public:Employee();static void Print();void Printinfo(); };int Employee::total = 0;Employee::Employee() {name = new char[10];cout << "Input name and number:" <<endl;cin >> name >> number;total++; }void Employee::Print() {cout << endl << "Total:" << total <<endl; }void Employee::Printinfo() {cout << "Name:" << name << setw(7) << " " << "Number:" << number <<endl; }int main() {Employee::Print();int i;Employee s[3];cout <<endl;for(i = 0; i < 3; i++){s[i].Printinfo();}Employee::Print();return 0; }(1)采用靜態成員函數,可以在創建對象之前處理靜態數據成員(在不生成類對象的情況下,直接存取靜態數據成員),這是普通成員函數不能實現的。
(2)靜態成員函數在同一個類中只有一個成員函數的地址映射,節約了計算機系統的開銷,提高了程序運行效率。
(3)靜態成員函數可以在類內定義,也可以在類外定義,在類外定義時,不要用static前綴。
(4)靜態數據成員可以被非靜態成員函數引用,也可以被靜態成員函數引用。但是靜態成員函數不能直接訪問類中的非靜態成員。
例如:將Print()寫成:
void Employee::Print() {cout << “name:”?<< name << setw(7) << “no:”?<< number <<endl;cout << endl << “total:”?<< total <<endl; }一般而言,靜態成員函數不能訪問類中的非靜態成員。若確實需要,靜態成員函數只能通過對象名(或指向對象的指針)訪問該對象的非靜態成員。
在靜態成員函數中訪問非靜態成員:
#include <iostream> #include <iomanip> #include <string.h> using namespace std; class Employee {private:char *name;int number;static int total;public:Employee();static void Printinfo(Employee); };int Employee::total = 0;Employee::Employee() {name = new char[10];cout << "Input name and number:" <<endl;cin >> name >> number;total++; }void Employee::Printinfo(Employee a) {cout <<endl << "Name:" << a.name << setw(7) << " " << "Number:" << a.number <<endl;cout << "Total:" << total <<endl; }int main() {int i;Employee s;cout <<endl;Employee::Printinfo(s);return 0; }(1)在靜態成員函數Print()中通過對象s訪問非靜態成員name和number。
(2)靜態成員函數不含this指針。
(3)如果程序中沒有實例化的對象,則只能通過“類名::”訪問靜態成員函數;如果有實例化的對象,則既可以通過類名方式訪問靜態成員函數,也可以通過對象訪問靜態成員函數。但一般不建議用對象名來引用。
例如: Class A { private:static int a; public:A(int i);static void show(); }; A::A(int i) {a = i; } void A::show() {cout << “This is static a:”?<< a <<endl; } int A::a; int main {A x(1);x.show(); ????//對象訪問靜態成員函數A::show(); ???//通過類名訪問靜態成員函數return 0; }?
5.3靜態對象
在定義對象時,可以定義類的靜態對象。與靜態變量一樣,在定義對象時,且只是第一次是,才需要執行構造函數進行初始化。靜態對象的析構函數是在main結束時才自動執行的。與普通對象相同,靜態對象的析構函數的執行與構造函數執行的順序相反。
靜態對象的定義:
static 類名 靜態對象名;
靜態對象的構造函數、析構函數的執行:
#include <iostream> using namespace std;class Obj {private:char ch;public:Obj(char c);~Obj(); };void f(); void g(); Obj A('A');Obj::Obj(char c):ch(c) {cout << "construct..." << ch <<endl; }Obj::~Obj() {cout << "destruct..." << ch <<endl; }void f() {static Obj B('B'); } void g() {static Obj C('C'); }int main() {cout << "inside main()" <<endl;f();f();g();cout << "outside main()" <<endl;return 0; }對象A是一個全局的obj類的對象,因此在main函數之前就調用A的構造函數。執行函數f()時,在函數f()內部定義一個靜態對象B,程序控制第一次轉到該對象的定義點時,系統自動執行B的構造函數,而且只執行一次。因此第二次調用函數f()時,就不再執行對象B的構造函數了。靜態對象的析構函數是在退出main函數時才被系統自動調用,而不是退出靜態對象所在的函數。因此,在退出main函數時才執行局部靜態對象B和C的析構函數。靜態對象的析構函數也是只執行一次,而且是按他們初始化時相反的順序進行的。因此本程序先執行對象C的析構函數,然后是對象B,最后是全局對象A。
?
5.4類作用域及對象的生存期
5.4.1類作用域
類作用域是指在類的定義中由一對花括號括起來的部分,包括數據成員和函數成員。在類所用域中聲明的標識符,其作用域與標識符聲明的順序沒有關系。類中的成員函數可以不受限制地訪問本類的數據成員和其他成員函數;但是在該類的作用域外,不能直接訪問類的數據成員和函數成員,即使是公有成員,也只能通過本類的對象才能訪問。
類作用域應用:
#include <iostream> using namespace std;class Test {private:int a;public:void f1();void f2(); };void Test::f1() {f2();cout << a <<endl; }void Test::f2() {a = 100; }int main() {Test t1;t1.f2();t1.f1();return 0; }?
5.4.2對象的生存期
類對象的生存期是指對象從被創建開始到生存期結束為止的時間,類對象在聲明時被創建,在釋放時被終止。
(1)局部對象:是指定義在一個程序塊或函數體內的對象。定義對象時,系統自動調用構造函數創建對象,程序運行結束時調用析構函數釋放對象。
(2)靜態對象:作用域從定義時開始到程序結束時止。程序第一次執行靜態對象定義時,自動調用構造函數創建對象,程序運行結束時調用析構函數釋放對象。
(3)全局對象:作用域是從定義時開始到程序結束時止。定義對象時,自動調用構造函數創建對象,程序運行結束時調用析構函數釋放對象。
(4)動態對象:執行運算符new時創建動態對象,執行運算符delete時釋放動態對象。
對象生存期應用:
#include <iostream> using namespace std;class Obj {private:char ch;public:Obj(char c);~Obj(); };void f(); void g(); Obj A('A');Obj::Obj(char c):ch(c) {cout << "construct..." << ch <<endl; }Obj::~Obj() {cout << "destruct..." << ch <<endl; }void f() {static Obj B('B');Obj C('C'); } void g() {static Obj C('C'); }int main() {cout << "inside main()" <<endl;f();f();cout << "outside main()" <<endl;return 0; }?
5.5命名空間
如果在一個C++程序中出現兩個變量、函數或類名完全相同,就會產生沖突。解決命名沖突的辦法有兩個,一個是使用不同的標識符名,但有時會降低程序的可讀性;另一個是使用命名空間,使用無.h擴展名的C++頭文件,并指定std命名空間。
例如: #include <iostream> using namespace std;using是一個在代碼編譯之前處理的指令。namespace為命名空間,它是ANSI/ISO C++的一個新特性,使用命名空間可以幫助開發人員在開發新的軟件組件時不會與已有的軟件組件產生標識符命名沖突,從而解決在程序中同名標識存在的潛在危機。
?
5.5.1命名空間的定義
所謂命名空間,是一種將程序庫名稱封裝起來的方法。命名空間是C++為解決變量、函數名和類名等標識符的命名沖突服務的,它將變量等標識符定義在一個不同名的命名空間中。
namespace 命名空間標識符名 {成員的聲明; }其中,命名空間標識符名在所定義的域中必須是唯一的,命名空間內的成員既包括變量,也包括函數。命名空間成員的訪問方式:
命名空間標識符名::成員名
如在C++標準程序庫中,使用了命名空間std。在使用C++標準程序庫的任何標識符時,可以直接指定標識符所屬的命名空間。
例如: #include <iostream> using namespace std; int main() {std::cout << “命名空間”?<<endl;return 0; }?
命名空間的應用:
#include <iostream> #include <string> #include <string.h> using namespace std;namespace A {char user_name[] = "namespace A";void showname(){cout << user_name <<endl;} }namespace B {string user_name = "namespace B";void showname(){cout << user_name <<endl;} }int main() {A::showname();B::showname();strcpy(A::user_name, "good");A::showname();return 0; }using namespace std;編譯指示在本程序中可以使用C++標準庫中定義的名字。std是標準命名空間名,在標準頭文件中聲明的函數、對象和類模板,都在明明空間std中聲明。程序中在命名空間A和B中定義了同名的字符串標識符user_name和函數showname(),但是因為是隸屬于不同的命名空間,所以同名的字符串標識符user_name和函數showname()互不干擾。在使用命名空間時,可以通過預編譯指示使用命名空間。
修改上例主程序:
Int main() {using namespace A;showname();B::showname();strcpy(user_name, “good”);cout << user_name <<endl;return 0; }訪問命名空間A的變量和函數時可以不顯示使用命名空間A的標識符,但是訪問命名空間B的變量和函數時,仍需要使用命名空間標識符訪問其變量和函數。關于賦值,定義兩個string類型的變量,并在定義的時候進行賦值。兩個string類型的變量可以用”+”實現連接,并賦值給同類型的變量。但是把string類型的對象直接賦值給C風格的字符串的話,編譯器就會報錯。
例如:string var = “Olympic”; char *p = var; ?????? ?//錯誤 char *p = (char*)var ??//正確?
總結
以上是生活随笔為你收集整理的C++学习笔记:(三)静态与名字空间的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++学习笔记:(二)函数重载 常量与
- 下一篇: C++学习笔记:(五)继承 多态