天天看點

繼承(加深)-構造函數調用順序與函數重定義

一、派生類的構造函數與析構函數

        從基類派生子類時,基類的構造函數不能繼承到派生類中,是以我們在定義派生類的構造函數時除了對自己的資料成員進行初始化外,還必須負責調用基類構造函數。如果派生類中有子對象,還應包含對子對象初始化的構造函數。

派生類構造函數一般的執行順序為:

(1)最先調用基類的構造函數,多個基類則按派生類聲明時列出的次序,從左到右調用,而不是初始化清單中的次序。

(2)再調用對象成員(子對象)的構造函數,按類聲明中對象成員出現的次序調用,而不是初始化清單中的次序。

(3)最後執行派生類的構造函數。

下面我們看一段程式:

#include<iostream>
using namespace std;
class A
{
public:
  A()
  {
    cout << "A Constructor1" << endl;
  }
  A(int i)
  {
    x1 = i;
    cout << "A Constructor2" << endl;
  }
  void displaya()
  {
    cout << "x1=" << x1 << endl;
  }
private:
  int x1;
};

class B :public A                      //若為多繼承,按從左到右的順序調用
{
public:
  B()
  {
    cout << "B Constructor1" << endl;
  }
  B(int i)
    :A(i + 10)
  {
    x2 = i;
    cout << "B Constructor2" << endl;
  }
  void displayb()
  {
    displaya();
    cout << "x2=" << x2 << endl;
  }
private:
  int x2;
};

int main()
{
  B b(2);
  b.displayb();

  system("pause");
  return 0;
}      

運作結果為:

繼承(加深)-構造函數調用順序與函數重定義

上述程式中,類B是類A的派生類,該成員初始化清單的順序是:先是基類A的構造函數,再是派生類B中子對象的構造函數,最後是B的構造函數。

如果類中包含對象成員呢?我們看看下面的程式:

class A
{
public:
  A()
  {
    cout << "class A" << endl;
  }
};
class B
{
public:
  B()
  {
    cout << "class B" << endl;
  }
};
class C
{
  A a;
public:
  C()
  {
    cout << "class C" << endl;
  }
};
class D :public C
{
  B b;
public:
  D()
  {
    cout << "class D" << endl;
  }
};

int main()
{
  D d;

  system("pause");
  return 0;
}      

運作結果如下:

繼承(加深)-構造函數調用順序與函數重定義

可以看出:

類C派生出類D,但類C包含一個類A的對象a,類D中包含一個類B的對象b,是以語句“D d”,先執行基類C中對象a的構造函數,再執行基類C的構造函數,接着是D中對象b的構造函數,最後執行類D的構造函數。

析構函數,其順序與執行構造函數時的順序正好相反。即次序如下:

(1)最先執行派生類的析構函數

(2)再調用對象成員(子對象)的析構函數,按類聲明中對象成員出現的逆序調用,而不是初始化清單中的次序。

 (3)最後調用基類的析構函數,多個基類則按派生類聲明時列出的逆序(從右到左)調用。

二、繼承成員的重定義

繼承成員的重定義是指重新修改繼承成員函數的實作。

我們先來看一段代碼:

class A
{
  int i;
public:
  void set(int x)
  {
    i = x;
  }
  void disp()
  {
    cout << "i=" << i << endl;
  }
};
class B
{
  int j;
public:
  void set(int y)
  {
    j = y;
  }
  void disp()
  {
    cout << "j=" << j << endl;
  }
};

int main()
{
  A a;
  B b;
  a.set(1);                 //調用類A的set函數
  a.disp();
  b.set(2);                 //調用類B的set函數
  b.disp();

  system("pause");
  return 0;
}      

運作結果為:

繼承(加深)-構造函數調用順序與函數重定義

這是因為如果在派生類中增加一個函數原型與繼承成員函數一模一樣的成員函數,則該函數實作的函數體是對繼承成員函數的重定義。

如果一個派生類的對象調用這個函數,首先在派生類中查找是否有該函數的定義,有則調用派生類中成員函數,否則才在所有的祖先類中查找。

三、友元關系不能繼承

友元關系不能繼承,也就是說基類友元不能通路子類私有和保護成員。除非在子類中也聲明為友元。

四、靜态成員資料

基類定義了static成員,則整個繼承體系裡面隻有一個這樣的成員。無論派生出多少個子類,都隻有 一個static成員執行個體。

我們來看一個例子:

class Person
{
public:
  Person()
  {
    ++_count;
  }
protected:
  string _name;
public:
  static int _count;
};

int Person::_count = 0;

class Student :public Person
{
protected:
  int _num;
};

void TestPerson()
{
  Person p;
  Student s1;
  Student s2;
  Student s3;

  cout << "人數:" << Person::_count << endl;
  Student::_count = 0;
  cout << "人數:" << Person::_count << endl;

}
int main()
{
  TestPerson();

  system("pause");
  return 0;
}      
繼承(加深)-構造函數調用順序與函數重定義

繼續閱讀