2011-01-12 55 views
8

Đây là một mẫu mã mà làm phiền tôi:Cách truy cập phương thức được bảo vệ trong lớp cơ sở từ lớp dẫn xuất?

class Base { 
    protected: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
    private: 
    Base *b; /* Initialized by constructor, not shown here 
       Intended to store a pointer on an instance of any derived class of Base */ 

    protected: 
    virtual void foo() { /* Some implementation */ }; 
    virtual void foo2() { 
     this->b->foo(); /* Compilator sets an error: 'virtual void Base::foo() is protected' */ 
    } 
}; 

Làm thế nào để bạn truy cập vào các chức năng overrided được bảo vệ?

Cảm ơn sự giúp đỡ của bạn. : o)

+7

Tôi không nghĩ rằng việc triển khai của bạn là hoàn toàn đúng. Tại sao bạn có một thể hiện của Base như một biến thành viên? this-> b-> foo() sẽ cố gắng gọi một phương thức ảo thuần túy. – GWW

+1

Chương trình này không được biên dịch. Bạn không thể khởi tạo một lớp trừu tượng .... Trừ khi 'b' trỏ đến một cá thể của một số lớp khác có nguồn gốc từ' Base'. – 341008

+0

Tôi bỏ qua độ chính xác: thuộc tính Derived :: b nhằm lưu trữ bất kỳ cá thể nào của các lớp dẫn xuất từ ​​Base –

Trả lời

8

Thành viên được bảo vệ trong lớp cơ sở chỉ có thể truy cập được bởi đối tượng hiện tại.
Vì vậy, bạn được phép gọi this->foo(), nhưng bạn không được phép gọi this->b->foo(). Điều này độc lập với việc liệu Derived có cung cấp triển khai cho foo hay không.

Lý do đằng sau hạn chế này là nếu không sẽ dễ dàng phá vỡ quyền truy cập được bảo vệ. Bạn chỉ cần tạo một lớp như Derived và đột nhiên bạn cũng có quyền truy cập vào các phần của các lớp khác (như OtherDerived) được cho là không thể truy cập được đối với người ngoài.

+0

Cảm ơn, bây giờ tôi hiểu rõ lý do của sự hạn chế ... Đó sẽ là một lỗ hổng bảo mật ... một lỗ hổng lớn! –

+5

Xin đừng nghĩ đó là lỗ hổng bảo mật. Các công cụ sửa đổi truy cập không cung cấp bất kỳ bảo mật nào, bạn chỉ có thể đọc vị trí bộ nhớ nếu bạn muốn dữ liệu. – DrYap

5

Thông thường, bạn sẽ làm điều đó bằng cách sử dụng Base::foo(), trong đó đề cập đến lớp cơ sở của phiên bản hiện tại.

Tuy nhiên, nếu mã của bạn cần thực hiện theo cách bạn đang cố gắng và không được phép, thì bạn cần phải đặt foo() ở chế độ công khai hoặc đặt Derived là bạn của Base.

0

Bạn gọi hàm cơ sở một cách rõ ràng với toán tử phạm vi (Base :: foo()). Nhưng trong trường hợp này, lớp cơ sở không định nghĩa foo (nó thuần ảo), do đó, thực sự không có hàm nào để thực thi khi bạn nói this->b->foo(); vì b là một con trỏ trỏ tới Base và không có Derived.

+1

Nhưng mã OP không tham chiếu đến lớp cơ sở của cá thể hiện tại. Anh ta đang truy cập một thể hiện khác, có lẽ là một lớp dẫn xuất thực hiện chức năng thuần ảo. (Nếu không, ví dụ không thể được tạo ra.) –

+0

@ Jonathan Wood Tôi hiểu những gì bạn đang nói, nhưng chỉ cần đi từ mã anh ấy đăng, có vẻ như anh ấy đang cố gắng tạo một lớp cơ sở trừu tượng (Base) và gọi một cách thuần khiết chức năng ảo (Base :: foo()), không có (như GWW và 341008 cũng được đề cập ở trên). – Gemini14

0

Làm cách nào để bạn truy cập vào chức năng được ghi đè được bảo vệ ?

--- từ đâu?

Bạn chỉ có thể truy cập thành viên được bảo vệ thông qua kế thừa (ngoài các phương thức của cùng một lớp). Ví dụ: bạn có một số class Derived1 kế thừa từ Derived, thì đối tượng của Derived1 có thể gọi foo().

EDIT: MSDN article về thông số truy cập được bảo vệ.

1

Đó là một chút mong manh, nhưng với các lớp bạn đã xác định ở đây, điều này sẽ không hoạt động?

virtual void foo2() { 
    reinterpret_cast<Derived *>(this->b)->foo(); 
} 

Các điểm reinterpret_cast tại VTABLE cho đối tượng cơ sở và gọi nó qua người truy cập thành viên này.

2

Một giải pháp là khai báo chức năng bảo vệ tĩnh trong Base chuyển hướng cuộc gọi đến chức năng riêng tư/được bảo vệ (foo trong ví dụ).

phép nói rằng:

class Base { 
protected: 
    static void call_foo(Base* base) { base->foo(); } 
private: 
    virtual void foo() = 0; 
}; 

class Derived : public Base { 
private: 
    Base* b; 
protected: 
    virtual void foo(){/* Some implementation */}; 
    virtual void foo2() 
    { 
     // b->foo(); // doesn't work 
     call_foo(b); // works 
    } 
}; 

Bằng cách này, chúng tôi không phá vỡ đóng gói bởi vì thiết kế của Base có thể làm cho một sự lựa chọn rõ ràng để cho phép tất cả các lớp thừa kế để gọi foo vào nhau, trong khi tránh đặt foo vào giao diện công khai hoặc chuyển tất cả các lớp con có thể là Base thành bạn bè.

Ngoài ra, phương pháp này hoạt động bất kể foo có phải là ảo hay không hoặc liệu nó là riêng tư hay được bảo vệ.

Here là liên kết tới phiên bản đang chạy của mã ở trên và here một phiên bản khác của cùng một ý tưởng với logic kinh doanh hơn một chút.

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