Tôi có thể gọi hàm ảo của lớp cơ sở nếu tôi ghi đè lên nó không?


340

Nói rằng tôi có các lớp học FooBarthiết lập như thế này:

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
        std::cout << x << std::endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        // I would like to call Foo.printStuff() here...
        std::cout << y << std::endl;
    }
};

Như được chú thích trong mã, tôi muốn có thể gọi hàm của lớp cơ sở mà tôi ghi đè. Trong Java có super.funcname()cú pháp. Điều này có thể có trong C ++ không?



1
Đối với nhân viên Google: lưu ý rằng bạn có thể gặp sự cố như tôi đã làm với việc lưu trữ nó dưới dạng biến thành viên lớp không phải là con trỏ. Xem câu trả lời của tôi ở đây: stackoverflow.com/questions/4798966/ Tôi đã tham gia mới / xóa để sửa chữa.
Andrew

Câu trả lời:


448

Cú pháp C ++ giống như thế này:

class Bar : public Foo {
  // ...

  void printStuff() {
    Foo::printStuff(); // calls base class' function
  }
};

11
Có bất kỳ vấn đề có thể làm với điều này? Có phải là thực hành xấu?
Robben_Ford_Fan_boy

36
@David: Không, hoàn toàn bình thường khi làm điều này, mặc dù nó có thể phụ thuộc vào lớp thực tế của bạn nếu nó thực sự hữu ích. Chỉ gọi phương thức lớp cơ sở nếu làm điều gì đó bạn muốn xảy ra;).
sth

20
cẩn thận đây là mùi mã khi khách hàng của bạn BẮT BUỘC phải làm điều đó! (được gọi call super requirement, hãy kiểm tra nó ở đây: en.wikipedia.org/wiki/Call_super )
v.oddou

8
@ v.oddou: Không có gì sai khi gọi siêu hạng khi nó hữu ích. Trang mà bạn liên kết chỉ là về một số vấn đề tiềm năng cụ thể liên quan đến thừa kế. Nó thậm chí còn tự nói rằng không có gì sai khi gọi siêu lớp nói chung: "Lưu ý rằng đó là yêu cầu gọi cha mẹ là mô hình chống. Có nhiều ví dụ trong mã thực trong đó phương thức trong lớp con vẫn có thể muốn chức năng của lớp cha, thường là nơi nó chỉ làm tăng thêm chức năng cha. "
sth

26
Nó có thể rõ ràng với hầu hết, nhưng để hoàn thiện, hãy nhớ không bao giờ làm điều này trong các hàm tạo và hàm hủy.
TigerCoding

123

Đúng,

class Bar : public Foo
{
    ...

    void printStuff()
    {
        Foo::printStuff();
    }
};

Nó giống như supertrong Java, ngoại trừ nó cho phép gọi các triển khai từ các cơ sở khác nhau khi bạn có nhiều kế thừa.

class Foo {
public:
    virtual void foo() {
        ...
    }
};

class Baz {
public:
    virtual void foo() {
        ...
    }
};

class Bar : public Foo, public Baz {
public:
    virtual void foo() {
        // Choose one, or even call both if you need to.
        Foo::foo();
        Baz::foo();
    }
};

7
Đây là một câu trả lời tốt hơn so với câu hỏi được chọn. Cảm ơn.
Nhà vật lý điên

Đa thừa kế? Ôi trời ơi!
lamino

69

Đôi khi bạn cần gọi triển khai của lớp cơ sở, khi bạn không ở hàm dẫn xuất ... Nó vẫn hoạt động:

struct Base
{
    virtual int Foo()
    {
        return -1;
    }
};

struct Derived : public Base
{
    virtual int Foo()
    {
        return -2;
    }
};

int main(int argc, char* argv[])
{
    Base *x = new Derived;

    ASSERT(-2 == x->Foo());

    //syntax is trippy but it works
    ASSERT(-1 == x->Base::Foo());

    return 0;
}

2
Lưu ý rằng bạn cần cả hai xloại Base*và sử dụng Base::Foocú pháp để làm việc này
TTimo

27

Chỉ trong trường hợp bạn làm điều này cho rất nhiều chức năng trong lớp của bạn:

class Foo {
public:
  virtual void f1() {
    // ...
  }
  virtual void f2() {
    // ...
  }
  //...
};

class Bar : public Foo {
private:
  typedef Foo super;
public:
  void f1() {
    super::f1();
  }
};

Điều này có thể tiết kiệm một chút bằng văn bản nếu bạn muốn đổi tên Foo.


1
Cách thú vị để làm điều đó, nhưng sẽ không hoạt động với nhiều kế thừa.
Nhà vật lý điên

5
Và nếu bạn có nhiều hơn 1 lớp cơ sở thì sao? Dù sao, thật ngớ ngẩn và thường vô ích khi cố gắng uốn cong C ++ để trông giống như một số ngôn ngữ khác. Chỉ cần nhớ tên của cơ sở và gọi nó bằng cách đó.
gạch dưới

6

Nếu bạn muốn gọi một hàm của lớp cơ sở từ lớp dẫn xuất của nó, bạn có thể chỉ cần gọi bên trong hàm bị ghi đè bằng cách đề cập đến tên lớp cơ sở (như Foo :: printStuff () ).

mã ở đây

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
        Foo::printStuff();/////also called the base class method
    }
};

int main()
{
    Bar *b=new Bar;
    b->printStuff();
}

Một lần nữa, bạn có thể xác định trong thời gian chạy hàm nào sẽ gọi bằng cách sử dụng đối tượng của lớp đó (dẫn xuất hoặc cơ sở). Nhưng điều này đòi hỏi hàm của bạn ở lớp cơ sở phải được đánh dấu là ảo.

mã dưới đây

#include <iostream>
using namespace std;

class Foo
{
public:
    int x;

    virtual void printStuff()
    {
         cout<<"Base Foo printStuff called"<<endl;
    }
};

class Bar : public Foo
{
public:
    int y;

    void printStuff()
    {
        cout<<"derived Bar printStuff called"<<endl;
    }
};

int main()
{

    Foo *foo=new Foo;
    foo->printStuff();/////this call the base function
    foo=new Bar;
    foo->printStuff();
}

0

kiểm tra điều này...

#include <stdio.h>

class Base {
public:
   virtual void gogo(int a) { printf(" Base :: gogo (int) \n"); };    
   virtual void gogo1(int a) { printf(" Base :: gogo1 (int) \n"); };
   void gogo2(int a) { printf(" Base :: gogo2 (int) \n"); };    
   void gogo3(int a) { printf(" Base :: gogo3 (int) \n"); };
};

class Derived : protected Base {
public:
   virtual void gogo(int a) { printf(" Derived :: gogo (int) \n"); };
   void gogo1(int a) { printf(" Derived :: gogo1 (int) \n"); };
   virtual void gogo2(int a) { printf(" Derived :: gogo2 (int) \n"); };
   void gogo3(int a) { printf(" Derived :: gogo3 (int) \n"); };       
};

int main() {
   std::cout << "Derived" << std::endl;
   auto obj = new Derived ;
   obj->gogo(7);
   obj->gogo1(7);
   obj->gogo2(7);
   obj->gogo3(7);
   std::cout << "Base" << std::endl;
   auto base = (Base*)obj;
   base->gogo(7);
   base->gogo1(7);
   base->gogo2(7);
   base->gogo3(7);

   std::string s;
   std::cout << "press any key to exit" << std::endl;
   std::cin >> s;
   return 0;
}

đầu ra

Derived
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Derived :: gogo2 (int)
 Derived :: gogo3 (int)
Base
 Derived :: gogo (int)
 Derived :: gogo1 (int)
 Base :: gogo2 (int)
 Base :: gogo3 (int)
press any key to exit

cách tốt nhất là sử dụng hàm base :: như nói @sth


Như đã giải thích trong Câu hỏi này , điều này không nên hoạt động vì tính protectedkế thừa. Để truyền con trỏ lớp cơ sở, bạn phải sử dụng kế thừa công khai.
Sản phẩm tối


Hấp dẫn. Sau khi đọc câu trả lời này, tôi nghĩ với quyền thừa kế được bảo vệ, thực tế là Derogen có nguồn gốc từ Base sẽ chỉ hiển thị với chính lớp đó và không thể nhìn thấy bên ngoài.
Sản phẩm tối

0

Vâng, bạn có thể gọi nó. Cú pháp C ++ để gọi hàm lớp cha trong lớp con là

class child: public parent {
  // ...

  void methodName() {
    parent::methodName(); // calls Parent class' function
  }
};

Đọc thêm về chức năng ghi đè .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.