在類的定義中,用下列訪問範圍關鍵字來講明類成員ios
可被訪問的範圍: – private: 私有成員,只能在成員函數內訪問 – public : 公有成員,能夠在任何地方訪問 – protected: 保護成員,之後再說 以上三種關鍵字出現的次數和前後次序都沒有限制。
定義一個類數組
class className { private: // 說明類成員的可訪問範圍 私有屬性和函數 public: 公有屬性和函數 protected: 保護屬性和函數 };
如過某個成員前面沒有上述關鍵字,則缺省地被認爲是私有成員。函數
class Man { int nAge; // 私有成員 char szName[20]; // 私有成員 public: void SetName(char * szName){ strcpy( Man::szName,szName); } };
在類的成員函數內部,可以訪問:優化
– 當前對象的所有屬性、函數;spa
– 同類其它對象的所有屬性、函數。對象
在類的成員函數之外的地方,只可以訪問該類對象的公有成員。blog
class CEmployee { private: char szName[30]; // 名字 public : int salary; // 工資 void setName(char * name); void getName(char * name); void averageSalary(CEmployee e1,CEmployee e2); }; void CEmployee::setName( char * name) { strcpy( szName, name); //ok } void CEmployee::getName( char * name) { strcpy( name,szName); //ok } void CEmployee::averageSalary(CEmployee e1,CEmployee e2){ cout << e1.szName; //ok ,訪問同類其餘對象私有成員 salary = (e1.salary + e2.salary )/2; } int main() { CEmployee e; strcpy(e.szName,"Tom1234567889"); // 編譯錯,不能訪問私有成員 e.setName( "Tom"); // ok e.salary = 5000; //ok return 0; }
用struct定義類內存
和用"class"的惟一區別,就是未說明是公有仍是私有的成員,就是公有ci
struct CEmployee { char szName[30]; // 公有!! public : int salary; // 工資 void setName(char * name); void getName(char * name); void averageSalary(CEmployee e1,CEmployee e2); };
「隱藏」的目的get
強制對成員變量的訪問必定要經過成員函數進行,那麼之後成員變量的類型等屬性修改後,只須要更改爲員函數便可。
不然,全部直接訪問成員變量的語句都須要修改。
「隱藏」的做用
若是將上面的程序移植到內存空間緊張的手持設備上,但願將szName 改成 char szName[5],若szName不是私有,
那麼就要找出全部相似 strcpy(e.szName,"Tom1234567889");這樣的語句進行修改,以防止數組越界。這樣作很麻煩。
若是將szName變爲私有,那麼程序中就不可能出現(除非在類的內部)strcpy(e.szName,"Tom1234567889");
這樣的語句,全部對 szName的訪問都是經過成員函數來進行,好比:e.setName( 「Tom12345678909887」);
那麼,就算szName改短了,上面的語句也不須要找出來修改,只要改 setName成員函數,在裏面確保不越界就能夠了。
成員函數也能夠重載
成員函數能夠帶缺省參數。
#include <iostream> using namespace std; class Location { private : int x, y; public: void init( int x=0 , int y = 0 ); void valueX( int val ) { x = val ;} int valueX() { return x; } }; void Location::init( int X, int Y) { x = X; y = Y; } int main() { Location A,B; A.init(5); A.valueX(5); cout << A.valueX(); // 輸出:5 return 0; }
使用缺省參數要注意避免有函數重載時的二義性
class Location { private : int x, y; public: void init( int x =0, int y = 0 ); void valueX( int val = 0) { x = val; } int valueX() { return x; } }; Location A; A.valueX(); // 錯誤,編譯器沒法判斷調用哪一個valueX
成員函數的一種
1).名字與類名相同,能夠有參數,不能有返回值(void也不行)
2).做用是對對象進行初始化,如給成員變量賦初值
3).若是定義類時沒寫構造函數,則編譯器生成一個默認的無參數的構造函數
4).默認構造函數無參數,不作任何操做
5).若是定義了構造函數,則編譯器不生成默認的無參數的構造函數
6).對象生成時構造函數自動被調用。對象一旦生成,就不再能在其上執行構造函數
7).一個類能夠有多個構造函數
爲何須要構造函數:
1) 構造函數執行必要的初始化工做,有了構造函數,就不必專門再寫初始化函數,也不用擔憂忘記調用初始化函數。
2) 有時對象沒被初始化就使用,會致使程序出錯。
A.默認無參數構造函數
class Complex { private : double real, imag; public: void Set( double r, double i); }; //編譯器自動生成默認構造函數 Complex c1; //默認構造函數被調用 Complex * pc = new Complex; //默認構造函數被調用
B.有參數的構造函數
class Complex { private : double real, imag; public: Complex( double r, double i = 0); }; Complex::Complex( double r, double i) { real = r; imag = i; } Complex c1; // error, 缺乏構造函數的參數 Complex * pc = new Complex; // error, 沒有參數 Complex c1(2); // OK Complex c1(2,4), c2(3,5); Complex * pc = new Complex(3,4);
C.能夠有多個構造函數,參數個數或類型不一樣
class Complex { private : double real, imag; public: void Set( double r, double i ); Complex(double r, double i ); Complex(double r ); Complex(Complex c1, Complex c2); }; Complex::Complex(double r, double i) { real = r; imag = i; } Complex::Complex(double r) { real = r; imag = 0; } Complex::Complex (Complex c1, Complex c2); { real = c1.real+c2.real; imag = c1.imag+c2.imag; } Complex c1(3) , c2 (1,0), c3(c1,c2); // c1 = {3, 0}, c2 = {1, 0}, c3 = {4, 0};
D.構造函數最好是public的,private構造函數不能直接用來初始化對象
class CSample{ private: CSample() { } }; int main(){ CSample Obj; //err. 惟一構造函數是private return 0; }
課堂例題:
有類A以下定義: class A { int v; public: A ( int n) { v = n; } }; 下面哪條語句是編譯不會出錯的? A) A a1(3); B) A a2; C) A * p = new A(); D) A * a(3)
class CSample { int x; public: CSample() { cout << "Constructor 1 Called" << endl; } CSample(int n) { x = n; cout << "Constructor 2 Called" << endl; } }; int main(){ CSample array1[2]; cout << "step1"<<endl; CSample array2[2] = {4,5}; cout << "step2"<<endl; CSample array3[2] = {3}; cout << "step3"<<endl; CSample * array4 = new CSample[2]; delete []array4; return 0; }
輸出:
Constructor 1 Called Constructor 1 Called step1 Constructor 2 Called Constructor 2 Called step2 Constructor 2 Called Constructor 1 Called step3 Constructor 1 Called Constructor 1 Called
代碼示例:
class Test { public: Test( int n) { } //(1) Test( int n, int m) { } //(2) Test() { } //(3) }; Test array1[3] = { 1, Test(1,2) }; // 三個元素分別用(1),(2),(3)初始化 Test array2[3] = { Test(2,3), Test(1,2) , 1}; // 三個元素分別用(2),(2),(1)初始化 Test * pArray[3] = { new Test(4), new Test(1,2) }; //兩個元素分別用(1),(2) 初始化
課堂例題:
假設 A 是一個類的名字,下面的語句生成 了幾個類A的對象? A * arr[4] = { new A(), NULL,new A() }; A) 1 B) 4 C) 2
1.1). 只有一個參數,即對同類對象的引用。
形如 X::X( X& )或X::X(const X &), 兩者選一,後者能以常量對象做爲參數
1.2). 若是沒有定義複製構造函數,那麼編譯器生成默認複製構造函數。默認的複製 構造函數完成複製功能。
class Complex { private : double real,imag; }; Complex c1; //調用缺省無參構造函數 Complex c2(c1);//調用缺省的複製構造函數,將 c2 初始化成和c1同樣
1.3). 若是定義的本身的複製構造函數,則默認的複製構造函數不存在。
class Complex { public : double real,imag; Complex(){ } Complex( const Complex & c ) { real = c.real; imag = c.imag; cout << 「Copy Constructor called」; } }; Complex c1; Complex c2(c1);//調用本身定義的複製構造函數,輸出 Copy Constructor called
1.4). 不容許有形如 X::X( X )的構造函數。
class CSample { CSample( CSample c ) { } //錯,不容許這樣的構造函數 };
1)當用一個對象去初始化同類的另外一個對象時。
Complex c2(c1); Complex c2 = c1; //初始化語句,非賦值語句
2)若是某函數有一個參數是類 A 的對象,那麼該函數被調用時,類A的複製構造函數將被調用。
class A { public: A() { }; A( A & a) { cout << "Copy constructor called" <<endl; } }; void Func(A a1){ } int main(){ A a2; Func(a2); return 0; } // 程序輸出結果爲: Copy constructor called
3) 若是函數的返回值是類A的對象時,則函數返回時,A的複製構造函數被調用:
class A { public: int v; A(int n) { v = n; }; A( const A & a) { v = a.v; cout << "Copy constructor called" <<endl; } }; A Func() { A b(4); return b; } int main() { cout << Func().v << endl; return 0; } 輸出結果: Copy constructor called 4
注意:對象間賦值並不致使複製構造函數被調用
class CMyclass { public: int n; CMyclass() {}; CMyclass( CMyclass & c) { n = 2 * c.n ; } }; int main() { CMyclass c1,c2; c1.n = 5; c2 = c1; CMyclass c3(c1); cout <<"c2.n=" << c2.n << ","; cout <<"c3.n=" << c3.n << endl; return 0; } 輸出: c2.n=5,c3.n=10
void fun(CMyclass obj_ ) { cout << "fun" << endl; }
這樣的函數,調用時生成形參會引起復制構造函數調用,開銷比較大。
因此能夠考慮使用 CMyclass & 引用類型做爲參數。若是但願確保實參的值在函數中不該被改變,那麼能夠加上const 關鍵字:
void fun(const CMyclass & obj) { //變 函數中任何試圖改變 obj 值的語句都將是變成非法 }
課堂習題:
假設A 是一個類的名字,下面哪段程序不會用到A的複製構造函數?
A) A a1,a2; a1 = a2;
B) void func( A a) { cout << "good" << endl; }
C) A func( ) { A tmp; return tmp; }
D) A a1; A a2(a1);
1.1 什麼是類型轉換構造函數
1. 定義轉換構造函數的目的是實現類型的自動轉換。
2. 只有一個參數,並且不是複製構造函數的構造函數,通常就能夠看做是轉換構造函數。
3. 當須要的時候,編譯系統會自動調用轉換構造函數,創建一個無名的臨時對象(或臨時變量)。
1.2 類型轉換構造函數實例
class Complex { public: double real, imag; Complex( int i) {// 類型轉換構造函數 cout << "IntConstructor called" << endl; real = i; imag = 0; } Complex(double r,double i) {real = r; imag = i; } }; int main () { Complex c1(7,8); Complex c2 = 12; c1 = 9; // 9 被自動轉換成一個臨時Complex 對象 cout << c1.real << "," << c1.imag << endl; return 0; }
顯式類型轉換構造函數
class Complex { public: double real, imag; explicit Complex( int i) {//顯式類型轉換構造函數 cout << "IntConstructor called" << endl; real = i; imag = 0; } Complex(double r,double i) {real = r; imag = i; } }; int main () { Complex c1(7,8); Complex c2 = Complex(12); c1 = 9; // error, 9不能被自動轉換成一個臨時Complex對象 c1 = Complex(9) //ok cout << c1.real << "," << c1.imag << endl; return 0; }
課堂習題
類A定義以下: class A { int v; public: A(int i) { v = i; } A() { } }; 下面段程序不會引起類型轉換構造函數被調用? A) A a1(4) B) A a2 = 4; C) A a3; a3 = 9; D) A a1,a2; a1 = a2;
2.1 什麼是析構函數
1. 名字與類名相同,在前面加‘~’, 沒有參數和返回值,一個類最多隻能有一個析構函數。
2. 析構函數對象消亡時即自動被調用。能夠定義析構函數來在對象消亡前作善後工做,好比釋放分配的空間等。
3. 若是定義類時沒寫析構函數,則編譯器生成缺省析構函數。缺省析構函數什麼也不作。
4. 若是定義了析構函數,則編譯器不生成缺省析構函數。
2.2 析構函數實例
class String{ private : char * p; public: String () { p = new char[10]; } ~ String () ; }; String ::~ String() { delete [] p; }
2.3 析構函數和數組
class Ctest { public: ~Ctest() { cout<< "destructor called" << endl; } }; int main () { Ctest array[2]; cout << "End Main" << endl; return 0; }
對象數組生命期結束時,對象數組的每一個元素的析構函數都會被調用。
輸出:
End Main
destructor called
destructor called
2.4 析構函數和運算符 delete
// delete 運算致使析構函數調用。 Ctest * pTest; pTest = new Ctest; // 構造函數調用 delete pTest; // 析構函數調用 --------------------------------------------------------- pTest = new Ctest[3]; // 構造函數調用3次 次 delete [] pTest; // 析構函數調用3次 次 若new一個對象數組,那麼用delete釋放時應該寫 []。不然只delete一個對象(調用一次析構函數)
2.5 析構函數在對象做爲函數返回值返回後被調用
class CMyclass { public: ~CMyclass() { cout << "destructor" << endl; } }; CMyclass obj; CMyclass fun(CMyclass sobj ) { // 參數對象消亡也會致使析 // 構函數被調用 return sobj; // 函數調用返回時生成臨時對象返回 } int main(){ obj = fun(obj); // 函數調用的返回值(臨時對象)被 return 0; // 用事後,該臨時對象析構函數被調用 } 輸出: destructor destructor destructor
class Demo { int id; public: Demo(int i) { id = i; cout << "id=" << id << " constructed" << endl; } ~Demo() { cout << "id=" << id << " destructed" << endl; } }; Demo d1(1); void Func() { static Demo d2(2); Demo d3(3); cout << "func" << endl; } int main () { Demo d4(4); d4 = 6; // 臨時對象 d4 = 7; cout << "main" << endl; {Demo d5(5);} //局部對象,不加{}則在 main ends後釋放 Func(); cout << "main ends" << endl; return 0; }
輸出結果
id=1 constructed
id=4 constructed
id=6 constructed
id=6 destructed
id=7 constructed
id=7 destructed
main
id=5 constructed
id=5 destructed
id=2 constructed
id=3 constructed
func
id=3 destructed
main ends
id=7 destructed // Demo d4(4); 調用析構函數
id=2 destructed
id=1 destructed
課堂習題
假設A是一個類的名字,下面的程序片斷會調用類A的析構函數幾回? int main() { A * p = new A[2]; A * p2 = new A; // new 出來的對象只有delete纔會消亡 A a; delete [] p; } A) 1 B) 2 C) 3 D) 4
#include <iostream> using namespace std; class CMyclass { public: CMyclass() {}; CMyclass( CMyclass & c) { cout << "copy constructor" << endl; } ~CMyclass() { cout << "destructor" << endl; } }; void fun(CMyclass obj_ ) { cout << "fun" << endl; } CMyclass c; CMyclass Test( ) { cout << "test" << endl; return c; } int main(){ CMyclass c1; fun(c1); Test(); return 0; }
輸出結果
copy constructor
fun
destructor //參數消亡
test
copy constructor
destructor // 返回值臨時對象消亡
destructor // 局部變量消亡
destructor // 全局變量消亡
class A { public: int x; A(int x_):x(x_) { cout << x << " constructor called" << endl; } A(const A & a ) { // 本例中dev 須要此const 其餘編譯器不要 x = 2 + a.x; cout << "copy called" << endl; } ~A() { cout << x << " destructor called" << endl; } }; A f( ){ A b(10); return b; } int main( ){ A a(1); a = f(); // 複製構造函數初始化 return 0; }
Visual Studio輸出
結果:
1 constructor called
10 constructor called
10 destructor called
copy called
12 destructor called
12 destructor called
dev C++輸出結果:
1 constructor called
10 constructor called
10 destructor called
10 destructor called
說明dev出於優化目的並未生成返回值臨時對象。VS無此問題
RRR