2017-07-27 62 views
8

Tôi đang học C++ và đang học về từ khóa ảo. Tôi đã lùng sục internet cố gắng hiểu nó vô ích. Tôi đã đi vào trình soạn thảo của tôi và đã làm thử nghiệm sau đây, hy vọng nó sẽ in ra thông báo cơ sở hai lần (vì tôi đã theo ấn tượng rằng từ khóa ảo là cần thiết để ghi đè lên các hàm). Tuy nhiên, nó in ra hai thông điệp khác nhau. Ai đó có thể giải thích cho tôi tại sao chúng ta cần từ khóa ảo nếu chúng ta có thể chỉ đơn giản là ghi đè lên các hàm và dường như vẫn có hành vi đa hình? Có lẽ ai đó có thể giúp tôi và những người khác trong tương lai hiểu được ảo so với việc ghi đè. (Kết quả tôi nhận được là "Tôi là cơ sở", tiếp theo là "Tôi là nguồn gốc").C++ từ khóa ảo vs chức năng ghi đè

#include <iostream> 

using namespace std; 
class Base{ 
public: 
    void printMe(){ 
     cout << "I am the base" << endl; 
    } 
}; 
class Derived: public Base{ 
public: 
    void printMe(){ 
     cout << "I am the derived" << endl; 
    } 
}; 
int main() { 
    Base a; 
    Derived b; 
    a.printMe(); 
    b.printMe(); 
    return 0; 
} 
+2

Lưu ý: 'sử dụng không gian tên std;' là thói quen xấu để xâm nhập và nếu bạn có thể dừng bây giờ, bạn có thể tránh được rất nhiều cơn đau đầu trong tương lai. Tiền tố 'std ::' có lý do: Nó tránh xung đột với các lớp, cấu trúc và các biến của riêng bạn. – tadman

+3

Thử 'Base * p = new Derived; p-> printMe(); 'có và không có' virtual'. – HolyBlackCat

+7

Để làm rõ - hành vi đa hình đạt được khi truy cập một đối tượng thông qua một con trỏ hoặc một tham chiếu đến lớp cơ sở của nó. – DeiDei

Trả lời

11

Hãy xem ví dụ sau. Dòng quan trọng để minh họa nhu cầu cho virtualoverridec->printMe();. Lưu ý rằng loại cBase*, tuy nhiên do đa hình, nó có thể gọi chính xác phương thức ghi đè từ lớp dẫn xuất.

#include <iostream> 

class Base{ 
public: 
    virtual void printMe(){ 
     std::cout << "I am the base" << std::endl; 
    } 
}; 

class Derived: public Base{ 
public: 
    void printMe() override { 
     std::cout << "I am the derived" << std::endl; 
    } 
}; 

int main() { 
    Base a; 
    Derived b; 
    a.printMe(); 
    b.printMe(); 
    Base* c = &b; 
    c->printMe(); 
    return 0; 
} 

Đầu ra là

I am the base 
I am the derived 
I am the derived 
+3

Mặc dù, bạn chưa giải thích mục đích của từ khóa 'override' là gì. –

+0

Tôi đã làm (chỉ có tôi), nhưng mọi người có xu hướng bỏ phiếu cho câu trả lời đầu tiên họ thấy. –

2

Bạn không nhìn thấy hành vi ở đây vì bạn đã tuyên bố b là loại Derived quá trình biên dịch biết những gì các chức năng để sử dụng. Để lộ lý do tại sao virtual là cần thiết mà bạn cần phải trộn mọi thứ lên:

int main() { 
    Base a; 
    Base *b = new Derived(); 

    a.printMe(); 
    b->printMe(); 

    delete b; 

    return 0; 
} 

Bây giờ b là loại Base* có nghĩa là nó sẽ sử dụng các chức năng trên Base cộng bất cứ điều gì trong bảng chức năng ảo. Điều này phá vỡ việc triển khai của bạn. Bạn có thể sửa lỗi bằng cách khai báo chính xác mọi thứ virtual.

+0

Bạn có thể giải thích việc bạn sử dụng từ khóa mới trên dòng 3 không? Tôi là một chút gỉ về cách thức hoạt động mới trong bối cảnh này. Tôi biết bạn đang tạo một con trỏ của loại cơ sở; thì bạn có gán đủ bộ nhớ để giữ một lớp dẫn xuất không? Dòng 3 hoạt động như thế nào? –

+0

Đó chỉ là phân bổ C++ thẳng lên sử dụng 'mới'. Nó rất giống với 'malloc' của C nhưng với nhiều thông tin tích hợp hơn, cộng với nó gọi tự động khởi tạo. Một cuốn sách tham khảo C++ tốt nên bao gồm các khái niệm cơ bản của 'mới' và' xóa'. Nếu bạn không có, cái [của tác giả của C++] (http://www.stroustrup.com/4th.html) là một nơi tốt để bắt đầu. – tadman

+1

@tadman: Như tôi đã nhận xét về câu trả lời khác, tôi nghĩ rằng chúng ta nên không sử dụng naked 'new' và' delete' trong mã ví dụ cho người mới, vì chúng thường được xem là thực hành không tốt trong C++ hiện đại. Một tham chiếu (hoặc 'b = & a') sẽ làm cho điểm này là tốt. –

6

Với mã mà bạn có, nếu bạn làm điều này

Derived derived; 
Base* base_ptr = &derived; 
base_ptr->printMe(); 

Bạn nghĩ gì sẽ xảy ra? Nó sẽ không in ra I am the derived vì phương pháp này không phải là ảo và công văn được thực hiện tắt loại tĩnh của đối tượng gọi (ví dụ: Base). Nếu bạn thay đổi nó thành ảo thì phương thức được gọi sẽ phụ thuộc vào kiểu động của đối tượng chứ không phải kiểu tĩnh.

2

override là một từ khóa mới được thêm vào trong C++ 11.

Bạn nên sử dụng nó vì:

  • trình biên dịch sẽ kiểm tra nếu một lớp cơ sở có chứa một virtual phương pháp phù hợp. Điều này là quan trọng vì một số lỗi đánh máy trong tên phương thức hoặc trong danh sách các đối số của nó (quá tải được cho phép) có thể dẫn đến ấn tượng rằng một cái gì đó đã bị ghi đè khi nó thực sự không.

  • nếu bạn sử dụng override cho một phương pháp, trình biên dịch sẽ báo cáo lỗi nếu phương pháp khác bị ghi đè mà không sử dụng từ khóa override. Điều này giúp phát hiện các ghi đè không mong muốn khi xảy ra xung đột biểu tượng.

  • virtual không có nghĩa là "ghi đè". Trong lớp học doent sử dụng "ghi đè" từ khóa hơn để ghi đè lên một phương pháp bạn chỉ có thể viết phương pháp này bỏ qua "ảo" từ khóa, ghi đè sẽ xảy ra ngầm.Các nhà phát triển đã viết virtual trước C++ 11 để cho biết ý định ghi đè của họ. Đơn giản chỉ cần đặt virtual có nghĩa là: phương pháp này có thể được ghi đè trong một lớp con.

+0

"ảo không có nghĩa là" ghi đè ". Nếu bạn bỏ qua, ghi đè sẽ vẫn hoạt động".? Tôi đoán bạn có nghĩa là nếu bạn bỏ qua nó trong các lớp học có nguồn gốc không nếu bạn bỏ qua trong cơ sở – ROX

+0

có điều này là những gì tôi có ý nghĩa. Tôi đã cải thiện văn bản rõ ràng hơn. –

Các vấn đề liên quan