在類里面成員函數的初始值是多少了?(取決於創建對象的位置,是在堆、棧、還是在靜態存儲區中創建。)
例如:
#include <stdio.h> class Test { private: int i; int j; public : int get_i(void) {return i;} int get_j(void) {return j;} }; Test Ta;//在靜態存儲區中定義 Test類 int main(int argc, char *argv[]) { printf("Ta.i = %d\n",Ta.get_i());//Ta.i = 0 printf("Ta.j = %d\n",Ta.get_j());//Ta.j = 0 Test Tb;//在棧上定義類 printf("Tb.i = %d\n",Tb.get_i());//Tb.i = 隨機數 printf("Tb.j = %d\n",Tb.get_j());//Tb.j = 隨機數 Test *Tc = new Test;//在堆上定義類 printf("Tc->i = %d\n",Tc->get_i());//Tc.i = 隨機數 printf("Tc->j = %d\n",Tc->get_j());//Tc.i = 隨機數 return 0; }
運行結果:
Ta.i = 0 Ta.j = 0 Tb.i = 1808322352 Tb.j = 32766 Tc->i = 0 Tc->j = 0
可以看出,對象只是變量,所以在不同的地方定義變量,所的到的初始值也不同。
在堆上定義:為隨機數
在棧上定義:為隨機數
在靜態存儲區上定義:因為靜態存儲區中變量默認為0 ,所以為0
這樣在不同地方定義初始值就會不同,這樣是不允許的所以我們需要對變量進行初始化。這就引入了類的構造函數。
構造函數:
構造函數特點:
1、構造函數沒有任何返回類型的聲明。
2、構造函數在定義的時候被自動調用。
例如:
#include <stdio.h> class Test { private: int i; int j; public : int get_i(void) {return i;} int get_j(void) {return j;} Test() { printf("Test()\n"); i = 5; j = 10; } }; Test Ta; int main(int argc, char *argv[]) { printf("Ta.i = %d\n",Ta.get_i()); printf("Ta.j = %d\n",Ta.get_j()); Test Tb; printf("Tb.i = %d\n",Tb.get_i()); printf("Tb.j = %d\n",Tb.get_j()); Test *Tc = new Test; printf("Tc->i = %d\n",Tc->get_i()); printf("Tc->j = %d\n",Tc->get_j()); return 0; }
在類中加入構造函數。
運行結果:
Test() Ta.i = 5 Ta.j = 10 Test() Tb.i = 5 Tb.j = 10 Test() Tc->i = 5 Tc->j = 10
可以看出每次定義都調用了一次構造函數。
一個類中可以有多個構造函數構成重載,重載的概念在類中同樣適用。
例如:
#include <stdio.h> class Test { private: int i; int j; public : int get_i(void) {return i;} int get_j(void) {return j;} Test() { printf("Test()\n"); i = 5; j = 10; } Test(int v) { printf("Test(int v);v = %d\n",v); i = 20; j = 30; } }; int main(int argc, char *argv[]) { Test Ta; printf("Ta.i = %d\n",Ta.get_i()); printf("Ta.j = %d\n",Ta.get_j()); Test Tb(10); printf("Tb.i = %d\n",Tb.get_i()); printf("Tb.j = %d\n",Tb.get_j()); Test *Tc = new Test(30); printf("Tc->i = %d\n",Tc->get_i()); printf("Tc->j = %d\n",Tc->get_j()); return 0; }
運行結果:
Test() Ta.i = 5 Ta.j = 10 Test(int v);v = 10 Tb.i = 20 Tb.j = 30 Test(int v);v = 30 Tc->i = 20 Tc->j = 30
從結果中可以看出:
Test Ta;調用的是 Test() 這個構造函數。
Test Tb(10);和 Test *Tc = new Test(30);調用的是 Test(int v) 這個構造函數。
注意:對象的定義與對象的聲明是不同的。例如變量的定義與變量的聲明也是不同的。
對象定義:聲明對象的空間並調用構造函數。
對象的聲明:告訴編譯器存在這樣的一個變量。
構造函數的手動調用:
一般來說構造函數在定義對象的時候被自動調用,但是在一些特殊情況下需要手動調用。
例如構造對象數組。
實驗:創建一個數組類解決數組的安全性問題。
1、創建Intarray.h
#ifndef __INTARRAY_H #define __INTARRAY_H class intArray { private: int arrayLenght; int *Parray; public: intArray (int lenght);//構造函數 bool changeArray(int index,int val);//修改數組中的元素 int getLenght(void);//獲取數組長度 bool getArrayData(int index,int& val);//獲取數組中的元素 void free(); }; #endif
2、創建Intarray.cpp
#include "intArray.h" intArray::intArray (int lenght)//構造函數 { Parray = new int[lenght];//創建數組空間 for(int i=0; i<lenght; i++)//初始化 Parray[i] = 0; arrayLenght = lenght; } bool intArray::changeArray(int index,int val)//修改數組中的元素 { bool ret = (index>=0)&&(index < arrayLenght);//判斷是否越界 if(ret) { Parray[index] = val; } return ret; } int intArray::getLenght(void)//獲取數組長度 { return arrayLenght; } bool intArray::getArrayData(int index, int& val)//獲取數組中的元素 { bool ret = (index>=0)&&(index < arrayLenght);//判斷是否越界 if(ret) { val = Parray[index] ; } return ret; } void intArray::free()// { delete[] Parray; }
3、創建main.cpp
#include <stdio.h> #include "intArray.h" int main(int argc, char *argv[]) { int temp ; intArray TestArray(6); for(int i=0; i<TestArray.getLenght();i++) TestArray.changeArray(i,i); for(int i=0; i<TestArray.getLenght();i++) { if(TestArray.getArrayData(i,temp))
printf("getArrayData(%d) = %d\n",i,temp); } TestArray.free(); return 0; }
運行結果:
getArrayData(0) = 0 getArrayData(1) = 1 getArrayData(2) = 2 getArrayData(3) = 3 getArrayData(4) = 4 getArrayData(5) = 5
類中的特殊構造函數:
1、無參構造函數。(當類中沒有定義任何構造函數時,編譯器會默認的提供一個無參構造函數,函數體為空)
class_name(){}
2、拷貝構造函數。參數為const class_name& 的構造函數 (當類中沒有定義任何拷貝構造函數時,編譯器為默認提供一個拷貝構造函數,其功能為進行成員變量的賦值。)
例如:定義一個對象的時候使用另外一個對象對其進行初始化。
class_name class1;
class_name class2=class1;或者(class_name class2(class1);)
通過以上使用就需要用到拷貝構造函數,編譯器默認的拷貝構造函數保證的是兩個對象的物理狀態相同(淺拷貝)。也就是說這是一種 淺拷貝。那么有淺拷貝就必然有深拷貝(其作用是保證兩個對象在邏輯狀態上相同)。
例如代碼:
#include <stdio.h> class Test { private: int i; int j; public : int get_i(void) {return i;} int get_j(void) {return j;} }; int main(int argc, char *argv[]) { Test Ta; Test Tb(Ta); printf("Ta.i = %d\t",Ta.get_i()); printf("Ta.j = %d\n",Ta.get_j()); printf("Tb.i = %d\t",Tb.get_i()); printf("Tb.j = %d\n",Tb.get_j()); return 0; }
運行結果:
Ta.i = -1553435232 Ta.j = 22062 Tb.i = -1553435232 Tb.j = 22062
從運行結果中可以看出,對象Tb 與對象Ta中的變量i,j值完全相同。
修改代碼:添加拷貝構造函數。
#include <stdio.h> class Test { private: int i; int j; public : int get_i(void) {return i;} int get_j(void) {return j;} Test(){}; Test(const Test& t) { i = t.i; j = t.j; } }; int main(int argc, char *argv[]) { Test Ta; Test Tb(Ta); printf("Ta.i = %d\t",Ta.get_i()); printf("Ta.j = %d\n",Ta.get_j()); printf("Tb.i = %d\t",Tb.get_i()); printf("Tb.j = %d\n",Tb.get_j()); return 0; }
運行結果:從結果中可以看出結果同上面沒有加入拷貝構造函數時一致。也就是說編譯器給我們默認構造了一個拷貝構造函數。內容與下面代碼一致
Test(const Test& t) { i = t.i; j = t.j; }
Ta.i = 688973216 Ta.j = 22083 Tb.i = 688973216 Tb.j = 22083
其中類中成員沒有指代系統中的資源。所以看起來沒有什么問題。
修改代碼:增加int *p = new int;並打印出p的地址
#include <stdio.h> class Test { private: int i; int j; int *p ; public : int get_i(void) {return i;} int get_j(void) {return j;} int* get_p(void){return p;} void free(void){delete p;} Test(int v) { i=1; j =2; p = new int; *p = v; }; Test(const Test& t) { i = t.i; j = t.j; p = new int; *p = *t.p; } }; int main(int argc, char *argv[]) { Test Ta(2); Test Tb(Ta); printf("Ta.i = %d\t,Ta.j = %d\t,Ta.p = %p\n",Ta.get_i(),Ta.get_j(),Ta.get_p()); printf("Tb.i = %d\t,Tb.j = %d\t,Tb.p = %p\n",Tb.get_i(),Tb.get_j(),Tb.get_p()); Ta.free(); Tb.free(); return 0; }
運行結果:
Ta.i = 1 ,Ta.j = 2 ,Ta.p = 0x55d66fe34e70 Tb.i = 1 ,Tb.j = 2 ,Tb.p = 0x55d66fe34e90
如果在拷貝構造函數中去掉 p = new int; *p = *t.p;
運行結果:
Ta.i = 1 ,Ta.j = 2 ,Ta.p = 0x55dbf6d60e70 Tb.i = 1 ,Tb.j = 2 ,Tb.p = (nil)
申請的 p 指針為空。這顯然是不對的。
打印p所指向空間的值運行結果:
Ta.i = 1 ,Ta.j = 2 ,Ta.p = 0x563d1529ee70 ,*Ta.p=2 Tb.i = 1 ,Tb.j = 2 ,Tb.p = 0x563d1377d9fd ,*Ta.p=29590344 munmap_chunk(): invalid pointer Aborted (core dumped)
指針在釋放的過程中出現了錯誤。
在拷貝構造函數中 增加 p = new int; *p = *t.p;
運行結果:
Ta.i = 1 ,Ta.j = 2 ,Ta.p = 0x55b993806e70 ,*Ta.p=2 Tb.i = 1 ,Tb.j = 2 ,Tb.p = 0x55b993806e90 ,*Ta.p=2
關於深拷貝的說明: ——自定義拷貝函數,必然需要使用到深拷貝
到底什么時候需要用到深拷貝? ——對象中有成員指代了系統資源。
1、成員指向了動態內存空間。
2、成員打開了外部文件。
3、成員使用了系統中的網絡端口
我們上面的實驗使用到了動態內存空間。所以也會出現問題。需要給它加上自定義拷貝函數。
修改代碼如下:
intArray.cpp
intArray::intArray (const intArray& obj) { Parray = new int[obj.arrayLenght]; arrayLenght = obj.arrayLenght; for(int i=0;i<obj.arrayLenght;i++) Parray[i] = obj.Parray[i]; }
main.cpp
#include <stdio.h> #include "intArray.h" int main(int argc, char *argv[]) { int temp ; intArray TestArray(6); for(int i=0; i<TestArray.getLenght();i++) TestArray.changeArray(i,i); for(int i=0; i<TestArray.getLenght();i++) { if(TestArray.getArrayData(i,temp)) printf("getArrayData(%d) = %d\n",i,temp); } intArray TestArray1(TestArray); for(int i=0; i<TestArray1.getLenght();i++) TestArray1.changeArray(i,i); for(int i=0; i<TestArray1.getLenght();i++) { if(TestArray1.getArrayData(i,temp)) printf("getArrayData1(%d) = %d\n",i,temp); } if(TestArray.getArrayData(100,temp)) printf("getArrayData(%d) = %d\n",100,temp); TestArray.free(); TestArray1.free(); return 0; }
運行結果:
getArrayData(0) = 0 getArrayData(1) = 1 getArrayData(2) = 2 getArrayData(3) = 3 getArrayData(4) = 4 getArrayData(5) = 5 getArrayData1(0) = 0 getArrayData1(1) = 1 getArrayData1(2) = 2 getArrayData1(3) = 3 getArrayData1(4) = 4 getArrayData1(5) = 5