2010-08-31 23 views
29
#include<iostream> 

using namespace std; 
class base 
{ 
public: 
    virtual void add() { 
     cout << "hi"; 
    } 
}; 

class derived : public base 
{ 
private: 
    void add() { 
     cout << "bye"; 
    } 
}; 

int main() 
{ 
    base *ptr; 
    ptr = new derived; 
    ptr->add(); 
    return 0; 
} 

Output là byeTại sao tôi có thể truy cập một hàm thành viên riêng có nguồn gốc thông qua một con trỏ lớp cơ sở đến một đối tượng có nguồn gốc?

Tôi không có một vấn đề với cách này được thực hiện. Tôi hiểu bạn sử dụng vtables và vtable có nguồn gốc chứa địa chỉ của hàm add() mới. Nhưng add() là private nên trình biên dịch tạo ra một lỗi khi tôi cố gắng truy cập nó bên ngoài lớp? Bằng cách nào đó nó không có vẻ đúng.

+4

Ghi đè và truy cập thông số là các khái niệm trực giao. – sbi

+0

vtables là chi tiết triển khai. –

Trả lời

32

add() chỉ riêng trong derived, nhưng kiểu tĩnh bạn có là base* - do đó hạn chế truy cập của base áp dụng.
Nói chung, bạn thậm chí không thể biết tại thời điểm biên dịch loại động nào của con trỏ đến base sẽ là, có thể ví dụ: thay đổi dựa trên đầu vào của người dùng.

Đây là mỗi C++ 03 §11.6:

Các quy tắc truy cập (khoản 11) cho một hàm ảo được xác định bởi tuyên bố của mình và không bị ảnh hưởng bởi các quy tắc cho một chức năng mà sau này ghi đè nó.
[...] Truy cập được chọn tại điểm gọi sử dụng loại biểu thức được sử dụng để biểu thị đối tượng mà hàm thành viên được gọi là [...]. Sự truy cập của hàm thành viên trong lớp mà nó được định nghĩa [...] nói chung không được biết.

+0

+1 - Cảm ơn bạn đã nhận xét rõ ràng về câu trả lời của tôi. –

+0

@Georg Fritzsche Bạn có thể vui lòng cho tôi liên kết C++ 03 từ nơi bạn đã sao chép liên kết này không. Tôi googled nhưng không tìm thấy, muốn nghiên cứu C++ –

+0

@Jai: ["Tôi tìm tài liệu chuẩn C hoặc C++ hiện tại ở đâu?"] (Http://stackoverflow.com/questions/81656/where-do-i-find -the-current-c-or-c-standard-documents) –

5

Để thêm một chút để trả lời Georg:

Hãy nhớ rằng trình biên dịch không kiểm soát và không thể đảm bảo bất cứ điều gì về các lớp thừa kế. Ví dụ, tôi có thể gửi loại của tôi trong một thư viện và lấy được từ nó trong một chương trình hoàn toàn mới. Làm thế nào là trình biên dịch thư viện phải biết rằng bắt nguồn có thể có một specifier truy cập khác nhau? Kiểu dẫn xuất không tồn tại khi thư viện được biên dịch.

Để hỗ trợ điều này, trình biên dịch sẽ phải biết các bộ định danh truy cập trong thời gian chạy và ném một ngoại lệ nếu bạn cố truy cập một thành viên riêng tư.

10

Các công cụ sửa đổi truy cập, chẳng hạn như public, privateprotected chỉ được thực thi trong khi biên soạn. Khi bạn gọi hàm thông qua một con trỏ đến lớp cơ sở, trình biên dịch không biết rằng con trỏ trỏ tới một cá thể của lớp dẫn xuất. Theo các quy tắc mà trình biên dịch có thể phỏng đoán từ biểu thức này, cuộc gọi này là hợp lệ.

Nó thường là một lỗi ngữ nghĩa để giảm khả năng hiển thị của một thành viên trong lớp dẫn xuất. Các ngôn ngữ lập trình hiện đại như Java và C# từ chối biên dịch mã như vậy, bởi vì một thành viên có thể nhìn thấy trong lớp cơ sở luôn có thể truy cập được trong lớp dẫn xuất thông qua một con trỏ cơ sở.

+1

+1 Câu trả lời hay nhất. –

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