Ghi đè các phương thức không ảo


81

Hãy giả sử tình huống này trong Visual C ++ 2010:

#include <iostream>
#include <conio.h>

using namespace std;

class Base
{
public:
    int b;
    void Display()
    {
        cout<<"Base: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Base: Virtual display."<<endl;
    };
};

class Derived : public Base
{
public:
    int d;
    void Display()
    {
        cout<<"Derived: Non-virtual display."<<endl;
    };
    virtual void vDisplay()
    {
        cout<<"Derived: Virtual display."<<endl;
    };
};

int main()
{
    Base ba;
    Derived de;

    ba.Display();
    ba.vDisplay();
    de.Display();
    de.vDisplay();

    _getch();
    return 0;
};

Về mặt lý thuyết, đầu ra của ứng dụng nhỏ này sẽ là:

  • Cơ sở: Màn hình không ảo.
  • Cơ sở: Màn hình ảo.
  • Cơ sở: Màn hình không ảo.
  • Có nguồn gốc: Màn hình ảo.

bởi vì phương thức Hiển thị của lớp Cơ sở không phải là một phương thức ảo nên lớp Phát sinh sẽ không thể ghi đè nó. Đúng?

Vấn đề là khi tôi chạy ứng dụng, nó in ra:

  • Cơ sở: Màn hình không ảo.
  • Cơ sở: Màn hình ảo.
  • Có nguồn gốc: Hiển thị không ảo.
  • Có nguồn gốc: Màn hình ảo.

Vì vậy, tôi đã không hiểu khái niệm về các phương thức ảo hoặc điều gì đó kỳ lạ xảy ra trong Visual C ++.

Ai đó có thể giúp tôi với một lời giải thích?


bạn hoàn toàn sẽ có Cơ sở: Màn hình không ảo. khi thay đổi dòng của bạn thành de.Base::Display().
v.oddou

Câu trả lời:


122

Đúng, bạn đang hiểu lầm một chút.

Phương thức cùng tên trên lớp dẫn xuất sẽ ẩn phương thức cha trong trường hợp này. Bạn sẽ tưởng tượng rằng nếu không phải như vậy, việc cố gắng tạo một phương thức có cùng tên với một phương thức không phải ảo của lớp cơ sở sẽ gây ra lỗi. Nó được phép và nó không phải là một vấn đề - và nếu bạn gọi phương thức trực tiếp như bạn đã làm, nó sẽ được gọi là tốt.

Tuy nhiên, không phải là ảo, các cơ chế tra cứu phương thức C ++ cho phép đa hình sẽ không được sử dụng. Vì vậy, ví dụ: nếu bạn đã tạo một thể hiện của lớp dẫn xuất nhưng được gọi là phương thức 'Hiển thị' thông qua một con trỏ đến lớp cơ sở, thì phương thức của cơ sở sẽ được gọi, trong khi đối với 'vDisplay', phương thức dẫn xuất sẽ được gọi.

Ví dụ: hãy thử thêm các dòng sau:

Base *b = &ba;
b->Display();
b->vDisplay();
b = &de;
b->Display();
b->vDisplay();

... và quan sát kết quả như mong đợi:

Cơ sở: Màn hình không ảo.
Cơ sở: Màn hình ảo.
Cơ sở: Màn hình không ảo.
Có nguồn gốc: Màn hình ảo.


Xin chào @ sje397, cảm ơn bạn đã trả lời. Bạn có thể viết một ví dụ về việc gọi phương thức, như bạn đã nói, thông qua một con trỏ đến lớp cơ sở không? Cảm ơn bạn!
Leif Lazar

cũng như tôi đã nói, bạn CŨNG có thể gọi phương thức cơ sở (không ảo) từ thể hiện dẫn xuất, sử dụng cú pháp phân giải phạm vi.
v.oddou

Vì vậy, để chắc chắn, tôi có thể định nghĩa một phương thức trong lớp cơ sở và ghi đè nó trong lớp dẫn xuất, bất kể có khai báo nó là ảo hay không. Sự khác biệt duy nhất là nếu một con trỏ cơ sở trỏ đến một đối tượng lớp dẫn xuất thì việc gọi phương thức đó sẽ gọi phương thức của lớp cơ sở 'nếu nó không phải là ảo, và phương thức của lớp dẫn xuất' nếu nó là ảo. Có đúng không? Có sự khác biệt nào khác không?
SexyBeast

@Cupidvogel Đúng, chính xác. Khai báo nó là 'ảo' có nghĩa là C ++ sẽ sử dụng các cơ chế để hỗ trợ tính đa hình và kiểm tra xem có phiên bản dẫn xuất nào của phương thức hay không khi bạn gọi thông qua con trỏ lớp cơ sở. Tôi không thể nghĩ ra sự khác biệt nào khác.
sje397

Chỉ thay đổi tệp tiêu đề là đủ? Hoặc, nguồn phải được biên dịch với từ khóa "ảo"?
Paul Knopf

13

Có bạn đã hiểu sai một chút:

Các chức năng ảo thuần túy:

virtual void fun1()=0 -> phải được ghi đè trong lớp dẫn xuất

Các chức năng ảo:

virtual void fun2() -> có thể được ghi đè

Các chức năng bình thường:

void fun3() -> đừng ghi đè nó

Để đạt được tính đa hình thời gian chạy, bạn cần ghi đè các hàm ảo trong c ++


5

Tôi nghĩ cũng có thể tốt hơn nếu xem xét nó trong bối cảnh ràng buộc tĩnh và động.

Nếu phương thức không phải là ảo (nó đã được mặc định trong C ++ không giống như Java), thì phương thức liên kết với trình gọi của nó tại thời điểm biên dịch, điều này không thể biết được đối tượng thực sự sẽ được trỏ vào thời gian chạy. Vì vậy, loại biến là tất cả những gì quan trọng là 'Cơ sở'.

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.