談談c++的初始化工作(3) (轉)

amyz發表於2007-08-15
談談c++的初始化工作(3) (轉)[@more@]

 

  我們還是先來看看上次遺留的問題。“為什麼(上次異常是三個,這次是六個,可以解釋嗎)?怎麼辦?”這其中的原因,我想您是明白的,我只做簡單的重複:)。程式碼段中:

  //bool IsSameMan(Human one,Human another)
  if(IsSameMan(lucy,lily))//(1)
 {
 std::cout< }else
 std::cout<

 }
  (1)句傳入兩個(lucy,lily),根據Human類的定義,試圖把它們的資源分別複製給one和 another,但毫無疑問出現了淺複製的問題。當區域性物件one和another析構時,問題表現出來了。至於為什麼是六個異常,呵呵,還是您來回答吧?
  知道原因,就容易解決。我們需要一個複製構造,來完成這項資源複製的工作,如下:

If add...in class Human(public):

Human(Human &human):ID(human.getID())
{
  name=new Name(human.getName()->getName());
}

OK,and you get :

 They are the same one.
Press any key to continue
  就沒問題了。

  這次,我們來看看組合與繼承中的初始化問題,最後再說明物件陣列初始化需要注意的地方。主要是繼承的例子。我想澄清一些想法,強調一些觀念。當然,如果有爭議的地方,歡迎您給我指出。先謝過了。
  大家知道,組合與繼承都是非常重要的耦合方式。典型的組合與繼承的例子,分別如下:

  class Point{
  int x,y;
  //...
  public:
  //...
  };

  class Shape{
  protected:
  Point p0;  //組合
  //...
  public:
  Shape(int x,int y);
  Shape(Point &point);
  //...
  };

  class Window:public Shape{  //繼承
  int width,height;
  //...
  public:
  Window(int _x,int _y,int _w,int _h);
  //...
  };
 
  在初始化時,可能是這樣的:

  Window::Window(int _x,int _y,int _w,int _h):Shape(_x,_y),width(_w),height(_h)
  {  }  //(1)
 
  組合與繼承的初始化,如果是我們自己來做這項工作,初始化的時機可只有這一個,(1)冒號後。這是c++的語法,沒有什麼可說的。
  但是,我想,到現在,有些認識,需要得到強調。
  首先,c++中的物件建立時必須得到初始化。一般的,我們要自己來做這項工作;但如果您沒有給出自己的建構函式,系統會給一個預設的建構函式,並去它(您知道它會做些什麼嗎?)。初始化是必須保證的。

  其次,複雜類(組合繼承邇來)的初始化工作中,建構函式呼叫有固定的順序。一般的c++教材都說的很詳細,我就不多說了。
  但有趣的是,複雜類中,若出現預設的系統初始化,都會發生些什麼?如果所有的父類都沒有寫建構函式,成員物件的類也沒有寫建構函式,又沒有常量成員和引用成員,一系列的初始化工作是按原來的順序,還是沒有初始化?這個問題也許沒有實際意義,但對理解初始化工作是很有意義的,我認為。

  我們以繼承的初始化為例子,一步一步來看。

  我們先來看看,如果不寫建構函式,系統會做什麼。
  類的定義。首先,Shape是一個抽象基類(介面)。我沒有為它寫任何建構函式,讓系統來吧。

//shape.h
#pragma once

class Shape
{
protected:
 int x,y;
public:
 virtual ~Shape(void);
 virtual void SetXY(int _x,int _y) = 0;
};
//shape.cpp
#include "shape.h"
#using
#include

Shape::~Shape(void)
{
}
 
  下面是Shape類的一個子類實現,Window。為了對比測試,它有兩個建構函式,一個是無任何引數,不做任何事情,當然,我們可以假設不知道它會不會呼叫父類Shape的建構函式。另一個是正常的建構函式,但從父類

繼承的成員沒有在其中初始化(看系統如何做)。
//window.h
#pragma once
#include "shape.h"

class Window :
 public Shape
{
 int width,height;
public:
 Window(void);
 Window(int _x,int _y,int _w,int _h);
 virtual ~Window(void);
 
 virtual void SetXY(int _x, int _y);

 void Test(void);
};
//window.cpp
#include
//建構函式1
Window::Window(void)
{
}
//建構函式2
Window::Window(int _x,int _y,int _w,int _h)
{
  width=_w;
 height=_h;
}

Window::~Window(void)
{
}

void Window::SetXY(int _x, int _y)
{
 x=_x;
 y=_y;
}
//
void Window::Test(void)
{
 std::cout<}


  這是公共的測試:
//fmain.cpp
#include
#include "window.h"
void main()
{
 Window win2;
 std::cout< win2.Test();

  Window win(10,10,50,50);
 std::cout< win.Test();
}

  執行,結果如下:

Test:win2:
x=1243328,y=1243040,width=1303984,height=1243296

Test:win:
x=1243040,y=1,width=50,height=50

Press any key to continue

  win2呼叫的是建構函式1,win呼叫的是建構函式2。win2的所有成員,值都似乎是隨機給的。而win中,只有width和height達到了預期效果,x和y和win2沒有什麼區別!
  暫行記住,這是沒有寫建構函式的結果。

  我們現在給Shape類新增建構函式,有兩個版本,作用同於上面Window類的建構函式。如下:
//shape.h
#pragma once

class Shape
{
protected:
 int x,y;
public:
  Shape(void);
  Shape(int _x,int _y);
 virtual ~Shape(void);
 virtual void SetXY(int _x,int _y) = 0;
};
//shape.cpp
#include "shape.h"
#using
#include
//建構函式1:什麼也不做
Shape::Shape(void)
{
}
//建構函式2:履行初始化工作
Shape::Shape(int _x,int _y)
{
  x=_x;
  y=_y;
}

Shape::~Shape(void)
{
}

  Window類的建構函式相應改正如下:
//建構函式1
Window::Window(void):Shape()
{
}
//建構函式2
Window::Window(int _x,int _y,int _w,int _h):Shape(_x,_y)
{
  width=_w;
  height=_h;
}

  測試檔案程式碼不變。執行,結果如下:

Test:win2:
x=1243328,y=1243040,width=1303984,height=1243296

Test:win:
x=10,y=10,width=50,height=50

Press any key to continue
 
  win2的情況沒有改觀,但win的初始化就是我們所要的!
  這說明了什麼?如果您不明白,可以回頭對比看看。

  系統的預設初始化,即使它給的值,是當時所在的地址的所有值(不可預料),但那仍然是初始化!

 說到預設初始化,在欲建立物件陣列時要特別注意,如果沒有確省的建構函式,物件陣列是無法建立的,因為沒有合適的建構函式可呼叫!(如果不能初始化,是做出錯處理的)

  Shape和Window類的建構函式相應改正如下:

//建構函式1:

Shape::Shape(void)
{

   x=0;y=0;
}
//建構函式1
Window::Window(void):Shape()
{

  width=0;height=0;
}
  主函式中程式碼換為:

Window win[5];
for(int i=0;i<5;i++)
{
  std::cout< win[i].Test();
}

 則結果為:
Test:0:
x=0,y=0,width=0,height=0

Test:1:
x=0,y=0,width=0,height=0

Test:2:
x=0,y=0,width=0,height=0

Test:3:
x=0,y=0,width=0,height=0

Test:4:
x=0,y=0,width=0,height=0

Press any key to continue

 但是,你把Shape類的建構函式1註釋掉(既刪除預設建構函式),再用同樣的測試檔案編譯時,編譯出錯:

  Window.cpp(5) : error C2512: “Shape” : 沒有合適的預設建構函式可用

  總之我想,這樣的觀念是必須的:
  初始化工作必須完成。任何情況下,系統完成初始化工作的機理是不變的(建構函式呼叫順序),系統不會偷懶:)。我們需要設計良好的初始化過程。

  這次遺留的問題是,所有的這些問題,在組合的情況下,也會發生嗎?在物件陣列初始化時,如果我們希望每個物件的初始化狀態都不同,應該如何做?如果您感覺不夠清晰,願您能寫程式碼測試一下,跟著結果思考:)。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-958280/,如需轉載,請註明出處,否則將追究法律責任。

相關文章