簡體   English   中英

用 C++ 中的派生類型覆蓋成員函數

[英]Overriding a member function with derived type in C++

  • 是否可以使用類似於B::g()B::h()顯示的派生類來覆蓋參數?

  • 如果沒有,是否有一種明智的方法來實現同樣的目標?

  • 為什么B::i()不起作用是有道理的,但為什么其他的不起作用?

我的目標是在B::g()使用r作為類型Y

struct X {};
struct Y : X {};

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  // note: overridden function is 'virtual X A::i()'
    virtual X* j();
};

struct B final : A {
    void f(Y q) final;   // error: 'void B::f(Y)' marked 'final', but is not virtual
    void g(Y& r) final;  // error: 'void B::g(Y&)' marked 'final', but is not virtual
    void h(Y* s) final;  // error: 'void B::h(Y*)' marked 'final', but is not virtual

    Y i() final;   // error: invalid covariant return type for 'virtual Y B::i()'
    Y* j() final;  // works
};


您只能以仍然可以安全地使用派生類代替基類的方式覆蓋虛函數(例如,通過指向基類的指針)。

讓我們以您提供的類為例:

B b;
A *a = &b;

X x;
a->g(x);

在最后一行中,無法確保g()的調用者將按照B預期傳遞Y ,因為調用者使用的是基類的接口,這通常是虛函數和動態多態的全部意義。

另一方面,派生類中的返回類型更具體是安全的:

B b;
A *a = &b;
X *x = a->j();

即使j()實際上返回Y* ,最后一條語句仍然是類型安全的。 此 C++ 功能稱為協變返回類型

您還可以在此處閱讀有關類型系統中協變和逆變的更多信息。

對於覆蓋函數參數列表必須相同,但在您的情況下,它不是。 這就是為什么它被認為是一個新函數並且不會覆蓋父函數中的函數。

所以你需要有這樣的東西來覆蓋:

struct A {
    virtual void f(X q);
    virtual void g(X& r);
    virtual void h(X* s);

    virtual X i();  
    virtual X* j();
};

struct B final : A {
    void f(X q) override final;   
    void g(X& r) override final;  
    void h(X* s) override final;  

    X i() override final;   
    X* j() override final;  
    // Y* j() override final;  <-- Also works because of covariant return
};

但請記住,您仍然可以將Y傳遞給這些函數,因為Y繼承了X

作為最后一個類成員的旁注Y* j() override final; 也將起作用,因為它將是Covariant return 對於協變返回,您的返回需要是指針或引用。 但是你需要看看你是否真的需要它。

我的目標是在 B::g() 中使用 r 作為類型 Y。

您可以這樣做,但是B::g不會覆蓋A::g 如果是這種情況,您需要使用相同的參數類型:

struct A {
    virtual void g(X& r);
    virtual ~A(){}
};

struct B : A {
    void g(X& r) override;
};

要覆蓋標記為final您需要在開頭添加虛擬。

例如: virtual void f(Y q) final;

B::i()不起作用,因為返回類型與您嘗試覆蓋的函數的返回類型不同。

但是B::j()起作用的原因是因為返回類型是一個指針,從技術上講,您可以返回一個指向存在的每種類型的指針。

只有當覆蓋函數具有與繼承函數相同的簽名(參數類型、 const限定符等)時,才能覆蓋虛函數。

所以

 struct A
 {
      virtual void f(X &);
 };

 struct B : public A
 {
      virtual void f(X &);
 };

是可能的,但改變f()的簽名,如

 struct B : public A
 {
      virtual void f(Y &);
 };

實際上隱藏了繼承的f()而不是覆蓋它。 使用override標識符,編譯器將檢測到這不是覆蓋,並診斷錯誤。

如果正確覆蓋,可以將Y傳遞給A::f()B::f() ,因為您的Y派生自X 但它是不可能重寫A::f()使用具有不同類型的任何參數的函數A::f()

對於返回指向基類的指針(或者,引用)的函數有一種特殊情況——這可以被返回指向派生類的指針的函數覆蓋。 參數必須具有相同類型的約束。 所以你的B::j()可以工作,因為Y是從X派生的。 就編譯器而言,像這樣的調用

 X*x = pA->j();    // pA is of type A* but points at a B

沒問題,因為它仍然返回一個可以有效轉換為X *的指針

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM