C++重載、重寫、重定義區別

重載(overload)

概念:

函數有同樣的名稱,但是參數列表不相同的情形,這樣的同名不同參數的函數之間,互相稱之爲重載函數。

基本條件:

  • 函數名必須相同;
  • 函數參數必須不相同,可以是參數類型或者參數個數不同;
  • 函數返回值可以相同,也可以不相同;

注意:

  • 只能通過不同的參數樣式進行重載,例如:不同的參數類型,不同的參數個數,不同的參數順序;
  • 不能通過訪問權限、返回類型、拋出的異常進行重載;
  • 重載的函數應該在相同的作用域下;

驗證程序:

class A {
 public:
  void Func1(int arg1) {
    std::cout << "func 1" << std::endl;
  }

  // OK: 通過參數類型不同重載 Func1()
  void Func1(double arg1) {
    std::cout << "func 2" << std::endl;
  }

  // OK: 通過參數個數不同重載 Func1()
  void Func1(int arg1, int arg2) {
    std::cout << "func 3" << std::endl;
  }

  // OK: 重載函數返回值可以不同
  bool Func1(int arg1, double arg2) {
    std::cout << "func 4" << std::endl;
    return true;
  }

  // ERROR: 不能只通過返回值來進行重載
  /*bool Func1(int arg1) {
    return true;
  } */
};

int _tmain(int argc, _TCHAR* argv[])
{
  A a;
  a.Func1(1);
  a.Func1(1.0);
  a.Func1(1, 2);
  a.Func1(1, 2.0);
  return 0;
}

運行結果:

func 1
func 2
func 3
func 4

重寫(override)

概念:

也稱爲覆蓋,子類重新定義父類中有相同名稱和參數的虛函數,主要在繼承關係中出現。

基本條件:

  • 重寫的函數和被重寫的函數必須爲virtual函數,分別位於基類和派生類中;
  • 重寫的函數和被重寫的函數函數名和函數參數必須一致;
  • 重寫的函數和被重寫的函數返回值相同,或者都返回指針或引用,並且派生類虛函數所返回的指針或引用的類型是基類中被替換的虛函數所返回的指針或引用的類型的子類型。

注意:

  • 重寫的函數所拋出的異常必須和被重寫的函數所拋出的異常一致,或者是其子類;
  • 重寫的函數的訪問修飾符可以不同於被重寫的函數,如基類的virtual函數的修飾符爲private,派生類改爲public或protected也是可以的。
  • 靜態方法不能被重寫,也就是static和virtual不能同時使用。
  • 重寫的函數可以帶virtual關鍵字,也可以不帶。

驗證程序:

class A {
};

class B : public A {
};

class C {
 public:
  virtual void Func1() {
    std::cout << "class C: func 1" << std::endl;
  }

  virtual A* Func2() {
    std::cout << "class C: func 2" << std::endl;
    return new A;
  }

  // ERROR:靜態函數不能被聲明爲virtual,也就沒辦法被重寫。
  // static virtual void FuncStatic() {}

  //由於Func3被聲明爲private,所以需要通過public函數來調用
  void ShowFunc3() {
    Func3();
  }

  virtual void Func4() {
    std::cout << "class C: func 4" << std::endl;
  }

 private:
  virtual void Func3() {
    std::cout << "class C: func 3" << std::endl;
  }
};

class D : public C {
 public:
  // OK: 重寫C類Func1,可以不帶virtual關鍵字
  void Func1() {
    std::cout << "class D: func 1" << std::endl;
  }

  // OK: 當返回值爲指針或者引用時,返回值可以是父類返回值類型的子類
  virtual B* Func2() {
    std::cout << "class D: func 2" << std::endl;
    return new B;
  }

  // ERROR: 除上面的情況,返回值類型要和父類一直
  /*virtual bool Func2() {
  }*/

  // OK: 重寫的函數的訪問修飾符可以不同於被重寫的函數
  virtual void Func3() {
    std::cout << "class D: func 3" << std::endl;
  }

 private:
  // OK: 重寫的函數的訪問修飾符可以不同於被重寫的函數
  virtual void Func4() {
    std::cout << "class D: func 4" << std::endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  C* c = new D;
  c->Func1();
  c->Func2();
  c->ShowFunc3();
  c->Func4();
  return 0;
}

輸出:

class D: func 1
class D: func 2
class D: func 3
class D: func 4

重定義(redefining)

概念:

也叫隱藏,子類重新定義父類中的非虛函數,屏蔽了父類的同名函數

基本條件:

  • 被隱藏的函數之間作用域不相同

注意:

  • 子類和父類的函數名稱相同,但參數不同,此時不管父類函數是不是virtual函數,都將被隱藏。
  • 子類和父類的函數名稱相同,參數也相同,但是父類函數不是virtual函數,父類的函數將被隱藏。

驗證程序:

class A {
public:
  void Func1(int a) {
      std::cout << "class A: Func1" << std::endl;
  }

  virtual void Func2(int a) {
      std::cout << "class A: Func2" << std::endl;
  }
};

class B : public A {
public:
  // 將會重定義父類的方法
  void Func1(int a) {
      std::cout << "class B: Func1" << std::endl;
  }

  // 將會重寫(override)父類方法
  void Func2(int a) {
    std::cout << "class B: Func2-1" << std::endl;
  }

  // 將會重定義父類的方法
  void Func2(int a, int b) {
    std::cout << "class B: Func2-2" << std::endl;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  B* b = new B;
  b->Func1(1);
  b->Func2(1);
  b->Func2(1, 1);
  return 0;
}

輸出:

class B: Func1
class B: Func2-1
class B: Func2-2

注意:
如果想使用父類中的函數,可以通過using 父類::函數名 的方式重現。

總結

重載、重寫、重定義書面上的區別,以及各自的規則沒有太大意義,而且這些名詞本身都是翻譯過來的,不同的地方翻譯也不盡相同,如果初學C++,弄清每個概念的實際意義,以及爲什麼這麼設計纔是最重要的。

參考資料
關於C++中繼承、重載、掩蓋
C++ 重載,重定義(覆蓋),重寫名字隱藏

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章