C++對象模型學習——構造函數語意學

1、Defalut Constructor的構造函數

     C++ Standard [ISO-C++95]的Section12.1這麼說:ios

     對於Class X,若是沒有任何user-declared constructor,那麼會有一個default constructor被程序員

隱式(implicitly)聲明出來……一個隱式聲明出來的default constructor將是一個trivial(無用數組

的)constructor……安全

      以下例子:bash

#include <iostream>

class Foo
{
  public:
    int val;
    Foo *pnext;
};

int main()
{
  Foo bar;
  if( bar.val || bar.pnext )
  {
    std::cout << "參數未初始化爲0" << std::endl;
  }
  else
  {
    std::cout << "參數初始化爲0" << std::endl;
  }

  return 0;
}

     上面代碼的語義是要求Foo有一個default constructor,能夠將它的兩個members初始化爲函數

0。而上面代碼並不會合出一個default constructor。其見差距在於一個是程序須要,一個是編譯spa

器須要。程序若是有須要,那是程序員的責任,本例中要承擔責任的是設計class Foo的人。設計

       只有當編譯器須要它時,纔會合出一個default constructor,被合成出的constructor只執行編3d

譯器所需的行動。也就是說,即便須要位class Foo合成一個default constructor,那個指針

constructor也不會將兩個data members val和pnext初始化爲0。爲了上一段代碼正確執

行,class Foo的設計者必須提供一個顯示的default constructor,將兩個members適當地初始

化。

       下面4小節分別討論nontrivial default constructor的4種狀況:

     一、「帶有Default Constructor」的Member Class Object

         若是一個class沒有任何constructor,但它內含一個member object,然後者有default

constructor,那麼這個class的implicit default constructor就是「nontrivial」,編譯器須要爲該class

合成出一個default constructor。不過這個合成操做只有在constructor真正須要被調用時纔會發

生。

          不一樣的編譯模塊經過合成default constructor、destructor、assignment copy operator都以

inline方式完成,來避免編譯器合出多個default constructor。一個inline函數有靜態連接(static

linkage),不會被文件之外者看見。若是函數太複雜,不適合作成inline,就會合成一個

explicit non-inline static實例。

            以下例子,編譯器爲class Bar合成一個default constructor:

#include <iostream>

class Foo
{
  public:
    Foo(){ val = 0; pnext = NULL; }
    int val;
    Foo *pnext;
};

class Bar : public Foo
{
  public:
    Foo foo;
    char *str;
};

int main()
{
  Bar bar; // Bar::foo必須在此初始化

  if( bar.val || bar.pnext )
  {
    std::cout << "foo參數未初始化爲0" << std::endl;
  }
  else
  {
    std::cout << "foo參數初始化爲0" << std::endl;
  }

  if( bar.str )
  {
    std::cout << "Bar::str參數未初始化爲0" << std::endl;
  }
  else
  {
    std::cout << "Bar::str參數初始化爲0" << std::endl;
  }
}

       

  

      能夠經過編譯器生成的彙編代碼看到,的確生成了Bar的default constructor,而且調用了

Foo的default constructor,但它並不產生任何初始化Bar::str。初始化Bar::foo是編譯器的責任,

而初始化Bar::str則是程序員的責任。

       若是class A內含一個或一個以上的member class objects,那麼class A的每個constructor

必須調用每個member classes的default constructor。編譯器會擴張已存在的constructors,在

其中安插一些代碼,使得user code被執行以前,先調用必要的default constructors。以下例

子,在class Bar中加入程序員定義的default constructor:

class Bar : public Foo
{
  public:
    Bar(){ str = NULL; }
    Foo foo;
    char *str;
};

      

      

      能夠從上面彙編代碼看出,的確Bar的default constructor函數被擴張,在開始調用了

Foo::Foo()。

      對於有多個class member objects都要求constructor初始化操做,C++語言要求以member

class在class中的聲明順序來調用各個constructors。例如:

        能夠從彙編代碼看到Snow_White的default Constructor的確先調用了Dopey::Dopey(),

Sneezy::Sneezy(), Bashful::Bashful()。

        而若是給Snow_White加上default Constructor,以下:

class Snow_White
{
  public:
    Snow_White(){ mumble = 0; }
    Dopey dopey;
    Sneezy sneezy;
    Bashful bashful;

  int getValue(){ return mumble; }

  private:
    int mumble;
};

           能夠根據彙編代碼看到的確Snow_White的default constructor被擴張了。

       二、「帶有Default Constructor」的Base Class

          若是一個沒有任何constructors的class派生自一個「帶有default constructor」的base class,

那麼這個derived class的default constructor會被視爲nontrivial,並所以須要被合成出來。

#include <iostream>

class Dopey
{
  public:
    Dopey(){ val1 = 0; }
    int val1;
};

class Snow_White : public Dopey
{
  public:
  int getValue(){ return mumble; }

  private:
    int mumble;
};

int main()
{
  Snow_White snow_white;
  
  if( snow_white.val1 )
  {
    std::cout << "val參數未初始化爲0" << std::endl;
  }
  else
  {
    std::cout << "val參數初始化爲0" << std::endl;
  }

  if( snow_white.getValue() )
  {
    std::cout << "mumble參數未初始化爲0" << std::endl;
  }
  else
  {
    std::cout << "mumble參數初始化爲0" << std::endl;
  }

  return 0;
}

             

      

       能夠經過彙編代碼看到,Snow_White的default constructor被合成出並調用了Dopey的

default constructor。       

    三、「帶有一個Virtual Function」的Class

       另外兩種狀況也要合出default constructor:

       1)class聲明(或繼承)一個virtual function。

       2)class派生自一個繼承串鏈,其中有一個或更多的virtual base classes。

// Whistle類的虛表	
        .weak	_ZTV7Whistle //若兩個或兩個以上全局符號(函數或變量名)名字同樣,
                               而其中之一聲明爲weak symbol(弱符號),則這些全局符號不會引起重定義錯誤。
                               連接器會忽略弱符號,去使用普通的全局符號來解析全部對這些符號的引用,
                               但當普通的全局符號不可用時,連接器會使用弱符號。
        .section	.rodata._ZTV7Whistle,"aG",@progbits,_ZTV7Whistle,comdat
                            //  .section section_name [, "flags"[, %type[,flag_specific_arguments]]]
	.align 8
	.type	_ZTV7Whistle, @object
	.size	_ZTV7Whistle, 12
_ZTV7Whistle:
	.long	0
	.long	_ZTI7Whistle
	.long	_ZNK7Whistle4flipEv

        // Bell類的虛表
	.weak	_ZTV4Bell
	.section	.rodata._ZTV4Bell,"aG",@progbits,_ZTV4Bell,comdat
	.align 8
	.type	_ZTV4Bell, @object
	.size	_ZTV4Bell, 12
_ZTV4Bell:
	.long	0
	.long	_ZTI4Bell
	.long	_ZNK4Bell4flipEv
	.weak	_ZTV6Widget

        // Widget類的虛表
	.weak	_ZTV6Widget
	.section	.rodata._ZTV6Widget,"aG",@progbits,_ZTV6Widget,comdat
	.align 8
	.type	_ZTV6Widget, @object
	.size	_ZTV6Widget, 12
_ZTV6Widget:
	.long	0
	.long	_ZTI6Widget
	.long	__cxa_pure_virtual


        // Whistle的typeinfo name
	.weak	_ZTS7Whistle
	.section	.rodata._ZTS7Whistle,"aG",@progbits,_ZTS7Whistle,comdat
	.type	_ZTS7Whistle, @object
	.size	_ZTS7Whistle, 9
_ZTS7Whistle:
	.string	"7Whistle"
	.weak	_ZTI7Whistle

        // Whistle的typeinfo
	.weak	_ZTI7Whistle
	.section	.rodata._ZTI7Whistle,"aG",@progbits,_ZTI7Whistle,comdat
	.align 4
	.type	_ZTI7Whistle, @object
	.size	_ZTI7Whistle, 12
_ZTI7Whistle:
	.long	_ZTVN10__cxxabiv120__si_class_type_infoE+8
	.long	_ZTS7Whistle
	.long	_ZTI6Widget

      下面兩個擴展行動在編譯器發生:

      1)一個virtual function table會被編譯出來,內放class的virtual functions地址。

      2)在每個class object中,一個額外的pointer member(也就是vptr)會被編譯器合成出來,

            內含相關class vtbl的地址。

       爲了讓這個機制發揮功效,編譯器必須爲每個Widget(或其派生類的)object的vptr設定

初值,放置適當的virtual table地址。在定義的每個default constructor和constructor中安插一

些代碼。以下:

movl	8(%ebp), %eax
movl	$_ZTV6Widget+8, (%eax)

movl	8(%ebp), %eax
movl	$_ZTV4Bell+8, (%eax)

movl	8(%ebp), %eax
movl	$_ZTV7Whistle+8, (%eax)

    四、「帶有一個Virtual Base Class」的Class

#include <iostream>

class X { public: int i; };

class A : public virtual X { public: int j; };

class B : public virtual X { public: double d; };

class C : public A, public B { public: int k; };

void foo( A* pa ) 
{
  pa->i = 1024; 
  std::cout << pa->i << std::endl;
}

int main()
{
  foo( new A );
  foo( new C );

  return 0;
}

0804885a <_ZN1XC1Ev>:
 804885a:	55                   	push   %ebp
 804885b:	89 e5                	mov    %esp,%ebp
 804885d:	5d                   	pop    %ebp
 804885e:	c3                   	ret    
 804885f:	90                   	nop

08048860 <_ZN1AC2Ev>:
 8048860:	55                   	push   %ebp
 8048861:	89 e5                	mov    %esp,%ebp
 8048863:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048866:	8b 10                	mov    (%eax),%edx
 8048868:	8b 45 08             	mov    0x8(%ebp),%eax
 804886b:	89 10                	mov    %edx,(%eax)
 804886d:	5d                   	pop    %ebp
 804886e:	c3                   	ret    
 804886f:	90                   	nop

08048870 <_ZN1AC1Ev>:
 8048870:	55                   	push   %ebp
 8048871:	89 e5                	mov    %esp,%ebp
 8048873:	83 ec 18             	sub    $0x18,%esp
 8048876:	8b 45 08             	mov    0x8(%ebp),%eax
 8048879:	83 c0 08             	add    $0x8,%eax
 804887c:	89 04 24             	mov    %eax,(%esp)
 804887f:	e8 d6 ff ff ff       	call   804885a <_ZN1XC1Ev>
 8048884:	ba fc 89 04 08       	mov    $0x80489fc,%edx
 8048889:	8b 45 08             	mov    0x8(%ebp),%eax
 804888c:	89 10                	mov    %edx,(%eax)
 804888e:	c9                   	leave  
 804888f:	c3                   	ret    

08048890 <_ZN1BC2Ev>:
 8048890:	55                   	push   %ebp
 8048891:	89 e5                	mov    %esp,%ebp
 8048893:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048896:	8b 10                	mov    (%eax),%edx
 8048898:	8b 45 08             	mov    0x8(%ebp),%eax
 804889b:	89 10                	mov    %edx,(%eax)
 804889d:	5d                   	pop    %ebp
 804889e:	c3                   	ret    
 804889f:	90                   	nop

080488a0 <_ZN1CC1Ev>:
 80488a0:	55                   	push   %ebp
 80488a1:	89 e5                	mov    %esp,%ebp
 80488a3:	83 ec 18             	sub    $0x18,%esp
 80488a6:	8b 45 08             	mov    0x8(%ebp),%eax
 80488a9:	83 c0 18             	add    $0x18,%eax
 80488ac:	89 04 24             	mov    %eax,(%esp)
 80488af:	e8 a6 ff ff ff       	call   804885a <_ZN1XC1Ev>
 80488b4:	ba c4 89 04 08       	mov    $0x80489c4,%edx
 80488b9:	8b 45 08             	mov    0x8(%ebp),%eax
 80488bc:	89 54 24 04          	mov    %edx,0x4(%esp)
 80488c0:	89 04 24             	mov    %eax,(%esp)
 80488c3:	e8 98 ff ff ff       	call   8048860 <_ZN1AC2Ev>
 80488c8:	b8 c8 89 04 08       	mov    $0x80489c8,%eax
 80488cd:	8b 55 08             	mov    0x8(%ebp),%edx
 80488d0:	83 c2 08             	add    $0x8,%edx
 80488d3:	89 44 24 04          	mov    %eax,0x4(%esp)
 80488d7:	89 14 24             	mov    %edx,(%esp)
 80488da:	e8 b1 ff ff ff       	call   8048890 <_ZN1BC2Ev>
 80488df:	ba b4 89 04 08       	mov    $0x80489b4,%edx
 80488e4:	8b 45 08             	mov    0x8(%ebp),%eax
 80488e7:	89 10                	mov    %edx,(%eax)
 80488e9:	ba c0 89 04 08       	mov    $0x80489c0,%edx
 80488ee:	8b 45 08             	mov    0x8(%ebp),%eax
 80488f1:	89 50 08             	mov    %edx,0x8(%eax)
 80488f4:	c9                   	leave  
 80488f5:	c3                   	ret    
 80488f6:	66 90                	xchg   %ax,%ax
 80488f8:	66 90                	xchg   %ax,%ax
 80488fa:	66 90                	xchg   %ax,%ax
 80488fc:	66 90                	xchg   %ax,%ax
 80488fe:	66 90                	xchg   %ax,%ax

        能夠看到編譯器爲A,B,C合成了不一樣的默認構造函數。

2、Copy Constructor的構造操做

      有三種狀況,會以一個object的內容做爲另外一個class object的初值。

      最明顯的一種狀況固然就是對一個object作顯式的初始化操做:

class X { ... };

X x;

// 顯式地以一個object的內容做爲另外一個class object的初值
X xx = x;

      另外兩種狀況是當object被當作參數交給某個函數時:

extern void foo( X x );

void bar()
{
  X xx;
  
  // 以xx做爲foo()第一個參數的初值(隱式的初始化操做)
  foo( xx );
}

     以及當函數傳回一個class object時:

X foo_bar()
{
  X xx;
  // ...
  return xx;
}

      一、Default Memberwise Initialization

       當class object以「相同class的另外一個object」做爲初值,其內部是以所謂的default

memberwise initializtion手法完成的,也就是把每個內建的或派生的data member(例如一個

指針或一個數組)的值,從某個object拷貝一份到另外一個object身上。不過它不會拷貝其中的

member class object,而是遞歸的方式施行memberwise initialization。例如:

#include <iostream>

class String
{
  public:
    // 沒有explict cpoy constructor
  private:
    char *str;
    int len;
};

      一個String object的default memberwise initialization發生在這種狀況之下:

String noun( "book" );
String verb = noun;

// 其完成方式就好像個別設定每個members同樣
verb.str = noun.str;
verb.len = noun.len;

    若是一個String object被聲明爲另外一個class的member,像這樣:

class Word
{
  public:
    // ......沒有explicit copy constructor
  private:
    int _occurs;
    String _word; // 
}

     那麼一個Word object的default memberwise initialization會拷貝其內建的member _occurs,

而後再於String member object _word身上遞歸實施memberwise initialization。

     一個class object可用兩種方式複製獲得,一種是被初始化,另外一種是被指定

(assignment)。從概念上而言,這兩個操做分別是以copy constructor和copy assignment

operator完成的。

      二、Bitwise Copy Semantics(位逐次拷貝)

       在下面程序片斷中:

#include "Word.h"

Word noun("book");

void foo()
{
  Word verb = noun;
}

       很明顯verb是根據noun來初始化。若是class Word的設計者定義了一個copy

constructor,verb的初始化操做會調用它。但若是class沒有定義explicit copy constructor,那麼

編譯器是否會合成一個調用實例,這就得視該class是否展示「bitwise copy semamtics」而定。

// 如下聲明展示了bitwise copy semantics
class Word
{
  public:
    Word( const char* );
    ~Word( delete[] str );
    // ...
  private:
    int cnt;
    char *str;
}

       這種狀況下不須要合成出一個default copy constructor,由於上述聲明展示了「default copy

semantics」,而verb的初始化操做也就不須要以一個函數調用收場。

class Word
{
  public:
    Word( const String& );
    ~Word();
    // ...
  private:
    int cnt;
    String str;
};

// 其中String聲明瞭一個explicit copy constructor
class String
{
  public:
    String( const char* );
    String( const String& );
    ~String();
    // ...
};

        這種狀況下,編譯器必須合成一個copy constructor,以便調用member class String object

的copy constructor:

// 一個被合成出來copy constructor
// C++僞碼
inline Word::Word( const Word& wd )
{
  str.String::String( wd.str );
  cnt = wd.cnt;
}

      三、不要Bitwise Copy Semantics

       有4種狀況class不展示出「bitwise copy semantics」

       1)當class內含一個member object然後者的class聲明有一個copy constructor時(不管是被

class設計者顯式地聲明,就像前面的String那樣;或是被編譯器合成,像class Word那樣)

       2)當class繼承自一個base class然後者存在一個copy constructor(也是不管是被顯式聲明

或是被合成而得)。

       3)當class聲明瞭一個或多個virtual functions時。

       4)當class派生自一個繼承串鏈,其中有一個或多個virtual base classes時。

       下面是後兩種狀況的討論。

      四、從新設定Virtual Table的指針

#include <iostream>

class ZooAnimal
{
  public:
    ZooAnimal(){ an1 = 0; dr1 = 0; }
    virtual ~ZooAnimal(){ };
    virtual void animate(){ std::cout << "This is ZooAnimal'animate " << an1 << std::endl; }
    virtual void draw(){ std::cout << "This is ZooAnimal'draw " << dr1 << std::endl; }
    // ...

  private:
    float an1;
    int dr1;
};

class Bear : public ZooAnimal
{
  public:
    Bear(){ an2 = 0; dr2 = 0; da1 = 0; };
    void animate(){ std::cout << "This is Bear'animate " << an2 << std::endl; }
    void draw(){ std::cout << "This is Bear'draw " << dr2 << std::endl; }
    virtual void dance(){ std::cout << "This is Bear'dance " << da1 << std::endl; }
  
  private:
    float an2;
    int dr2; 
    int da1;
};


int main()
{
  Bear yogi;
  Bear winnie = yogi;

  std::cout << "For yogi: " << std::endl;
  yogi.animate();
  yogi.draw();

  std::cout << "For winnie: " << std::endl;
  winnie.animate();
  winnie.draw();
  winnie.dance();
}

     彙編代碼以下:

080488ad <main>:
 80488ad:	55                   	push   %ebp
 80488ae:	89 e5                	mov    %esp,%ebp
 80488b0:	53                   	push   %ebx
 80488b1:	83 e4 f0             	and    $0xfffffff0,%esp
 80488b4:	83 ec 40             	sub    $0x40,%esp
 80488b7:	8d 44 24 10          	lea    0x10(%esp),%eax
 80488bb:	89 04 24             	mov    %eax,(%esp)
 80488be:	e8 2b 02 00 00       	call   8048aee <_ZN4BearC1Ev> #調用Bear::Bear()
 80488c3:	8d 44 24 10          	lea    0x10(%esp),%eax
 80488c7:	89 44 24 04          	mov    %eax,0x4(%esp)
 80488cb:	8d 44 24 28          	lea    0x28(%esp),%eax
 80488cf:	89 04 24             	mov    %eax,(%esp)
 80488d2:	e8 45 03 00 00       	call   8048c1c <_ZN4BearC1ERKS_> #調用Bear::Bear(Bear const&)
 80488d7:	c7 44 24 04 c0 8d 04 	movl   $0x8048dc0,0x4(%esp)
 80488de:	08 
 80488df:	c7 04 24 a0 b0 04 08 	movl   $0x804b0a0,(%esp)
 80488e6:	e8 65 fe ff ff       	call   8048750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
 80488eb:	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)
 80488f2:	08 
 80488f3:	89 04 24             	mov    %eax,(%esp)
 80488f6:	e8 75 fe ff ff       	call   8048770 <_ZNSolsEPFRSoS_E@plt>
 80488fb:	8d 44 24 10          	lea    0x10(%esp),%eax
 80488ff:	89 04 24             	mov    %eax,(%esp)
 8048902:	e8 23 02 00 00       	call   8048b2a <_ZN4Bear7animateEv> #調用Bear::animate()
 8048907:	8d 44 24 10          	lea    0x10(%esp),%eax
 804890b:	89 04 24             	mov    %eax,(%esp)
 804890e:	e8 5b 02 00 00       	call   8048b6e <_ZN4Bear4drawEv> #調用Bear::draw()
 8048913:	c7 44 24 04 cb 8d 04 	movl   $0x8048dcb,0x4(%esp)
 804891a:	08 
 804891b:	c7 04 24 a0 b0 04 08 	movl   $0x804b0a0,(%esp)
 8048922:	e8 29 fe ff ff       	call   8048750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>  # 調用std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits
                                                                                                                        <char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
 8048927:	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)
 804892e:	08 
 804892f:	89 04 24             	mov    %eax,(%esp)
 8048932:	e8 39 fe ff ff       	call   8048770 <_ZNSolsEPFRSoS_E@plt>  # 調用std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream
                                                                                 <char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
 8048937:	8d 44 24 28          	lea    0x28(%esp),%eax
 804893b:	89 04 24             	mov    %eax,(%esp)
 804893e:	e8 e7 01 00 00       	call   8048b2a <_ZN4Bear7animateEv> # 調用Bear::animate()
 8048943:	8d 44 24 28          	lea    0x28(%esp),%eax
 8048947:	89 04 24             	mov    %eax,(%esp)
 804894a:	e8 1f 02 00 00       	call   8048b6e <_ZN4Bear4drawEv> #調用Bear::draw()
 804894f:	8d 44 24 28          	lea    0x28(%esp),%eax
 8048953:	89 04 24             	mov    %eax,(%esp)
 8048956:	e8 57 02 00 00       	call   8048bb2 <_ZN4Bear5danceEv> # 調用Bear::dance()
 804895b:	8d 44 24 28          	lea    0x28(%esp),%eax
 804895f:	89 04 24             	mov    %eax,(%esp)
 8048962:	e8 fd 02 00 00       	call   8048c64 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048967:	8d 44 24 10          	lea    0x10(%esp),%eax
 804896b:	89 04 24             	mov    %eax,(%esp)
 804896e:	e8 f1 02 00 00       	call   8048c64 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048973:	b8 00 00 00 00       	mov    $0x0,%eax
 8048978:	eb 24                	jmp    804899e <main+0xf1>
 804897a: 	89 c3                	mov    %eax,%ebx
 804897c:	8d 44 24 28          	lea    0x28(%esp),%eax
 8048980:	89 04 24             	mov    %eax,(%esp)
 8048983:	e8 dc 02 00 00       	call   8048c64 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048988:	 8d 44 24 10          	lea    0x10(%esp),%eax
 804898c:	89 04 24             	mov    %eax,(%esp)
 804898f:	e8 d0 02 00 00       	call   8048c64 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048994:	89 d8                	mov    %ebx,%eax
 8048996:	 89 04 24             	mov    %eax,(%esp)
 8048999: 	e8 02 fe ff ff       	call   80487a0 <_Unwind_Resume@plt>
 804899e:	 8b 5d fc             	mov    -0x4(%ebp),%ebx
 80489a1:	c9                   	leave  
 80489a2:	c3                   	ret    

# Bear::Bear()默認構造函數
08048aee <_ZN4BearC1Ev>:
 8048aee:	55                   	push   %ebp
 8048aef:	89 e5                	mov    %esp,%ebp
 8048af1:	83 ec 18             	sub    $0x18,%esp
 8048af4:	8b 45 08             	mov    0x8(%ebp),%eax
 8048af7:	89 04 24             	mov    %eax,(%esp)
 8048afa:	e8 ff fe ff ff       	call   80489fe <_ZN9ZooAnimalC1Ev> #調用ZooAnimal::ZooAnimal()
 8048aff:	8b 45 08             	mov    0x8(%ebp),%eax
 8048b02:	c7 00 e8 8d 04 08    	movl   $0x8048de8,(%eax)
 8048b08:	8b 55 08             	mov    0x8(%ebp),%edx
 8048b0b:	a1 d8 8d 04 08       	mov    0x8048dd8,%eax
 8048b10:	89 42 0c             	mov    %eax,0xc(%edx)
 8048b13:	8b 45 08             	mov    0x8(%ebp),%eax
 8048b16:	c7 40 10 00 00 00 00 	movl   $0x0,0x10(%eax)
 8048b1d:	8b 45 08             	mov    0x8(%ebp),%eax
 8048b20:	c7 40 14 00 00 00 00 	movl   $0x0,0x14(%eax)
 8048b27:	c9                   	leave  
 8048b28:	c3                   	ret    
 8048b29:	90                   	nop

#Bear::Bear(Bear const&)默認拷貝構造函數
08048c1c <_ZN4BearC1ERKS_>:
 8048c1c:	55                   	push   %ebp
 8048c1d:	89 e5                	mov    %esp,%ebp
 8048c1f:	83 ec 18             	sub    $0x18,%esp
 8048c22:	8b 55 0c             	mov    0xc(%ebp),%edx
 8048c25:	8b 45 08             	mov    0x8(%ebp),%eax
 8048c28:	89 54 24 04          	mov    %edx,0x4(%esp)
 8048c2c:	89 04 24             	mov    %eax,(%esp)
 8048c2f:	e8 c2 ff ff ff       	call   8048bf6 <_ZN9ZooAnimalC1ERKS_> #調用ZooAnimal::ZooAnimal(Bear const&)
 8048c34:	8b 45 08             	mov    0x8(%ebp),%eax
 8048c37:	c7 00 e8 8d 04 08    	movl   $0x8048de8,(%eax)
 8048c3d:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048c40:	8b 40 0c             	mov    0xc(%eax),%eax
 8048c43:	8b 55 08             	mov    0x8(%ebp),%edx
 8048c46:	89 42 0c             	mov    %eax,0xc(%edx)
 8048c49:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048c4c:	8b 50 10             	mov    0x10(%eax),%edx
 8048c4f:	8b 45 08             	mov    0x8(%ebp),%eax
 8048c52:	89 50 10             	mov    %edx,0x10(%eax)
 8048c55:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048c58:	8b 50 14             	mov    0x14(%eax),%edx
 8048c5b:	8b 45 08             	mov    0x8(%ebp),%eax
 8048c5e:	89 50 14             	mov    %edx,0x14(%eax)
 8048c61:	c9                   	leave  
 8048c62:	c3                   	ret    
 8048c63:	90                   	nop

       下面是虛表:

# ZooAnimal類的虛表
	.section	.rodata._ZTV9ZooAnimal,"aG",@progbits,_ZTV9ZooAnimal,comdat
	.align 8
	.type	_ZTV9ZooAnimal, @object
	.size	_ZTV9ZooAnimal, 24
_ZTV9ZooAnimal:
	.long	0
	.long	_ZTI9ZooAnimal
	.long	_ZN9ZooAnimalD1Ev
	.long	_ZN9ZooAnimalD0Ev
	.long	_ZN9ZooAnimal7animateEv
	.long	_ZN9ZooAnimal4drawEv

# Bear類的虛表
	.section	.rodata._ZTV4Bear,"aG",@progbits,_ZTV4Bear,comdat
	.align 8
	.type	_ZTV4Bear, @object
	.size	_ZTV4Bear, 28
_ZTV4Bear:
	.long	0
	.long	_ZTI4Bear
	.long	_ZN4BearD1Ev
	.long	_ZN4BearD0Ev
	.long	_ZN4Bear7animateEv
	.long	_ZN4Bear4drawEv
	.long	_ZN4Bear5danceEv

      從main函數的調用過程能夠看出yogi和winnie調用的Bear虛表所指的Bear::animate()函數和

Bear::draw()函數。

      當一個base class object以其derived class的object內容作初始化操做時,其vptr複製操做也

必須保證安全,以下:

int main()
{
  Bear yogi;
  ZooAnimal franny = yogi;  // 這會發生切割行爲

  std::cout << "For yogi: " << std::endl;
  yogi.animate();
  yogi.draw();

  std::cout << "For franny: " << std::endl;
  franny.animate();
  franny.draw();
}

       

080488ad <main>:
 80488ad:	55                   	push   %ebp
 80488ae:	89 e5                	mov    %esp,%ebp
 80488b0:	53                   	push   %ebx
 80488b1:	83 e4 f0             	and    $0xfffffff0,%esp
 80488b4:	83 ec 40             	sub    $0x40,%esp
 80488b7:	8d 44 24 28          	lea    0x28(%esp),%eax
 80488bb:	89 04 24             	mov    %eax,(%esp)
 80488be:	e8 1f 02 00 00       	call   8048ae2 <_ZN4BearC1Ev> # 調用Bear::Bear()
 80488c3:	8d 44 24 28          	lea    0x28(%esp),%eax
 80488c7:	89 44 24 04          	mov    %eax,0x4(%esp)
 80488cb:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 80488cf:	89 04 24             	mov    %eax,(%esp)
 80488d2:	e8 13 03 00 00       	call   8048bea <_ZN9ZooAnimalC1ERKS_> # 調用ZooAnimal::ZooAnimal(ZooAnimal const&)
 80488d7: 	c7 44 24 04 60 8d 04 	movl   $0x8048d60,0x4(%esp)
 80488de:	08 
 80488df: 	c7 04 24 a0 b0 04 08 	movl   $0x804b0a0,(%esp)
 80488e6: 	e8 65 fe ff ff       	call   8048750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
 80488eb: 	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)
 80488f2: 	08 
 80488f3: 	89 04 24             	mov    %eax,(%esp)
 80488f6: 	e8 75 fe ff ff       	call   8048770 <_ZNSolsEPFRSoS_E@plt>
 80488fb:	8d 44 24 28          	lea    0x28(%esp),%eax
 80488ff:       89 04 24             	mov    %eax,(%esp)
 8048902:	e8 17 02 00 00       	call   8048b1e <_ZN4Bear7animateEv> # 調用Bear::animate()
 8048907:       8d 44 24 28          	lea    0x28(%esp),%eax
 804890b:	89 04 24             	mov    %eax,(%esp)
 804890e:	e8 4f 02 00 00       	call   8048b62 <_ZN4Bear4drawEv> # 調用Bear::draw()
 8048913:	c7 44 24 04 6b 8d 04 	movl   $0x8048d6b,0x4(%esp)
 804891a:	08 
 804891b:	c7 04 24 a0 b0 04 08 	movl   $0x804b0a0,(%esp)
 8048922:	e8 29 fe ff ff       	call   8048750 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
 8048927:	c7 44 24 04 80 87 04 	movl   $0x8048780,0x4(%esp)
 804892e:	08 
 804892f:	89 04 24             	mov    %eax,(%esp)
 8048932:	e8 39 fe ff ff       	call   8048770 <_ZNSolsEPFRSoS_E@plt>
 8048937:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 804893b:	89 04 24             	mov    %eax,(%esp)
 804893e:	e8 17 01 00 00       	call   8048a5a <_ZN9ZooAnimal7animateEv> # 調用ZooAnimal::animate()
 8048943:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048947:	89 04 24             	mov    %eax,(%esp)
 804894a:	e8 4f 01 00 00       	call   8048a9e <_ZN9ZooAnimal4drawEv> # 調用ZooAnimal::draw()
 804894f:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048953:	89 04 24             	mov    %eax,(%esp)
 8048956:	e8 bb 00 00 00       	call   8048a16 <_ZN9ZooAnimalD1Ev> # 調用ZooAnimal::~ZooAnimal()
 804895b:	8d 44 24 28          	lea    0x28(%esp),%eax
 804895f:	89 04 24             	mov    %eax,(%esp)
 8048962:	e8 a9 02 00 00       	call   8048c10 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048967:	b8 00 00 00 00       	mov    $0x0,%eax
 804896c:	eb 24                	jmp    8048992 <main+0xe5>
 804896e:	89 c3                	mov    %eax,%ebx
 8048970:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048974:	89 04 24             	mov    %eax,(%esp)
 8048977:	e8 9a 00 00 00       	call   8048a16 <_ZN9ZooAnimalD1Ev> # 調用ZooAnimal::~ZooAnimal()
 804897c:	8d 44 24 28          	lea    0x28(%esp),%eax
 8048980:	89 04 24             	mov    %eax,(%esp)
 8048983:	e8 88 02 00 00       	call   8048c10 <_ZN4BearD1Ev> # 調用Bear::~Bear()
 8048988:	89 d8                	mov    %ebx,%eax
 804898a:	89 04 24             	mov    %eax,(%esp)
 804898d:	e8 0e fe ff ff       	call   80487a0 <_Unwind_Resume@plt>
 8048992:	8b 5d fc             	mov    -0x4(%ebp),%ebx
 8048995:	c9                   	leave  
 8048996:	c3                   	ret

      能夠看出franny的vptr指向ZooAnimal的virtual table,而非Bear的virtual table(它由yogi的

vptr指出)。

      五、處理Virtual Base Class Subobject

      一個class object若是以另外一個object做爲初值,然後者有一個virtual base class subobject,

那麼也會使「bitwise copy semantics」失效。

#include <iostream>

class ZooAnimal
{
  public:
    ZooAnimal(){ an1 = 0; dr1 = 0; }
    virtual ~ZooAnimal(){ };
    virtual void animate(){ std::cout << "This is ZooAnimal'animate " << std::endl; }
    virtual void draw(){ std::cout << "This is ZooAnimal'draw " << std::endl; }
    // ...

  private:
    float an1;
    int dr1;
};

class Raccoon : public virtual ZooAnimal
{
  public:
    Raccoon(){ ra1 = 0; }
    Raccoon( int val ){ ra1 = val;  }
    void animate(){ std::cout << "This is Raccoon'animate " << std::endl; }
    void draw(){ std::cout << "This is Raccoon'draw " << std::endl; }
  
  private:
    int ra1;
};

class RedPanda : public Raccoon
{
  public:
    RedPanda(){ re1 = 0; }
    RedPanda( int val ){ re1 = val; }
    void animate(){ std::cout << "This is RedPanda'animate " << std::endl; }
    void draw(){ std::cout << "This is RedPanda'draw " << std::endl; }
  
  private:
    int re1;
};

int main()
{
  RedPanda little_red;
  Raccoon little_critter = little_red;

  std::cout << "For little_red: " << std::endl;
  little_red.animate();
  little_red.draw(); 

  std::cout << "For little_critter: " << std::endl;
  little_critter.animate();
  little_critter.draw();

}

    

0804888d <main>:
 804888d:	55                   	push   %ebp
 804888e:	89 e5                	mov    %esp,%ebp
 8048890:	53                   	push   %ebx
 8048891:	83 e4 f0             	and    $0xfffffff0,%esp
 8048894:	83 ec 40             	sub    $0x40,%esp
 8048897:	8d 44 24 28          	lea    0x28(%esp),%eax
 804889b:	89 04 24             	mov    %eax,(%esp)
 804889e:	e8 7b 03 00 00       	call   8048c1e <_ZN8RedPandaC1Ev> # 調用RedPanda::RedPanda()
 80488a3:	8d 44 24 28          	lea    0x28(%esp),%eax
 80488a7:	89 44 24 04          	mov    %eax,0x4(%esp)
 80488ab:	8d 44 24 14          	lea    0x14(%esp),%eax
 80488af:	89 04 24             	mov    %eax,(%esp)
 80488b2:	e8 55 04 00 00       	call   8048d0c <_ZN7RaccoonC1ERKS_> # 調用Raccoon::Raccoon(Raccoon const&)
 80488b7:	c7 44 24 04 3b 8f 04 	movl   $0x8048f3b,0x4(%esp)
 80488be:	08
 ......


# Raccoon::Raccoon(Raccoon const&)
08048d0c <_ZN7RaccoonC1ERKS_>:
 8048d0c:	55                   	push   %ebp
 8048d0d:	89 e5                	mov    %esp,%ebp
 8048d0f:	83 ec 18             	sub    $0x18,%esp
 8048d12:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048d15:	8b 00                	mov    (%eax),%eax
 8048d17:	83 e8 0c             	sub    $0xc,%eax
 8048d1a:	8b 00                	mov    (%eax),%eax
 8048d1c:	89 c2                	mov    %eax,%edx
 8048d1e:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048d21:	01 c2                	add    %eax,%edx
 8048d23:	8b 45 08             	mov    0x8(%ebp),%eax
 8048d26:	83 c0 08             	add    $0x8,%eax
 8048d29:	89 54 24 04          	mov    %edx,0x4(%esp)
 8048d2d:	89 04 24             	mov    %eax,(%esp)
 8048d30:	e8 b1 ff ff ff       	call   8048ce6 <_ZN9ZooAnimalC1ERKS_> # 調用ZooAnimal::ZooAnimal(ZooAnimal const&)
 8048d35:	ba 2c 90 04 08       	mov    $0x804902c,%edx
 8048d3a:	8b 45 08             	mov    0x8(%ebp),%eax
 8048d3d:	89 10                	mov    %edx,(%eax)
 8048d3f:	ba 08 00 00 00       	mov    $0x8,%edx
 8048d44:	8b 45 08             	mov    0x8(%ebp),%eax
 8048d47:	01 c2                	add    %eax,%edx
 8048d49:	b8 50 90 04 08       	mov    $0x8049050,%eax
 8048d4e:	89 02                	mov    %eax,(%edx)
 8048d50:	8b 45 0c             	mov    0xc(%ebp),%eax
 8048d53:	8b 50 04             	mov    0x4(%eax),%edx
 8048d56:	8b 45 08             	mov    0x8(%ebp),%eax
 8048d59:	89 50 04             	mov    %edx,0x4(%eax)
 8048d5c:	c9                   	leave  
 8048d5d:	c3                   	ret

   咱們看到在4種狀況下,class再也不保持「bitwise copy semantics」,並且default copy

constructor若是未被聲明的話,會被視爲nontrivial。若是缺少一個已聲明的copy constructor,

編譯器爲了正確處理「以一個class object做爲另外一個class object的初值」,必須合成出一個copy

constructor。

相關文章
相關標籤/搜索