C++ 對象的構造


在類里面成員函數的初始值是多少了?(取決於創建對象的位置,是在堆、棧、還是在靜態存儲區中創建。)

  例如:  

#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

 

  
 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM