2012-12-26 26 views
10

Tôi đã tạo một lớp với hai phương thức get, một const và một non-const. Phương thức const là công khai, do đó người dùng có thể truy vấn vectơ. Phương pháp non-const được bảo vệ, vì vậy tôi có thể sử dụng nó để sửa đổi dữ liệu tôi cần.Tại sao C++ không cast thành const khi phương thức const là public và phương thức non-const được bảo vệ?

Khi tôi cố gắng sử dụng lớp, tuy nhiên, và gọi phương thức get, trình biên dịch phàn nàn rằng phương thức không được bảo vệ. Thay vào đó, tôi phải sử dụng const_cast để đưa đối tượng đến const, vì vậy tôi có thể thay thế phương thức công khai.

Có cách nào để giải quyết vấn đề này không? Tại sao trình biên dịch sẽ không tự tạo bản thân vì có một phương thức công khai? Nếu tôi loại bỏ các phiên bản được bảo vệ và chỉ để lại một const, nó hoạt động tốt, do đó, nó không diễn viên trong tình huống này. Đúc đến const luôn an toàn. Nó loại bỏ constness đó là một vấn đề.

Trả lời

3

Trình biên dịch xem xét khả năng truy cập sau nó quyết định chức năng thành viên nào mà nó muốn gọi. Tức là, các chức năng được bảo vệ và riêng tư vẫn là hiển thị, mặc dù chúng không phải là có thể truy cập.

Tại sao? Một lý do là nếu bạn thực hiện các chức năng không thể tiếp cận bị bỏ qua bởi độ phân giải quá tải, bạn có thể thay đổi chức năng nào được gọi đơn giản bằng cách thay đổi khả năng truy cập của nó. Với các quy tắc hiện tại, bạn chỉ có thể gây ra việc biên dịch mã để không biên dịch hoặc gây ra mã hiện không hoạt động để biên dịch, hoặc thay đổi một cái gì đó mà không ảnh hưởng đến ý nghĩa của mã. Bạn không thể thay đổi các specifier truy cập và âm thầm gây ra một hàm khác được gọi.

Như một ví dụ giả tạo, đây là một giao diện lớp khá khủng khiếp:

public: 
    // Returns the amount of change tendered for this transaction. 
    MoneyAmount change() const; 

private: 
    // Account for a change of currency. Charges standard moneychanger's fee. 
    MoneyAmount change(Currency toCurrency = Currency::USD); 

Nếu chức năng không thể tiếp cận đã được gỡ bỏ từ độ phân giải quá tải, mã khách hàng có thể gọi change() tốt. Và nếu sau đó hàm thứ hai change(Currency) được công khai và bị xóa đầu tiên, mã đó sẽ đột nhiên âm thầm gọi một hàm khác với một mục đích hoàn toàn khác. Các quy tắc hiện hành ngăn chặn một sự thay đổi của specifier truy cập từ việc thay đổi hành vi của một chương trình biên dịch.

0

Trong C++, chọn phương pháp (độ phân giải quá tải) xảy ra trước khi xem xét kiểm soát truy cập công cộng/riêng tư.

0

Sử dụng phương thức setter được bảo vệ thay vì (hoặc thành viên dữ liệu) thay vì phương thức getter const.

Nó không có sự khác biệt nếu bạn có s.th. như

class A { 
    SomeType foo_; 
protected: 
    SomeType& foo() { return foo_; } 

public: 
    const SomeType& foo() const { return foo_; } 
}; 

hoặc

class A { 
protected: 
    SomeType foo_; 

public: 
    const SomeType& foo() const { return foo_; } 
}; 
6

kiểm soát truy cập thành viên là điều cuối cùng xảy ra khi gọi một hàm thành viên. Nó xảy ra sau khi tra cứu tên, trích dẫn đối số mẫu, độ phân giải quá tải, v.v. Lý do tại sao nó được thực hiện cuối cùng là bởi vì nó đã được quyết định rằng việc thay đổi kiểm soát truy cập cho một thành viên không nên đột nhiên thay đổi việc thực thi mã máy khách.

Hãy tưởng tượng quyền truy cập đã được kiểm tra trước khi phân giải quá tải và bạn đã sử dụng thư viện và chức năng thành viên nhất định trong thư viện đó. Sau đó, các tác giả thư viện đã thực hiện chức năng riêng tư. Đột nhiên, mã của bạn bắt đầu sử dụng một tình trạng quá tải khác nhau và hoạt động theo một cách hoàn toàn khác. Các tác giả thư viện có thể dự định rằng bất kỳ ai sử dụng tình trạng quá tải của chức năng đó sẽ ngừng sử dụng nó, nhưng họ không có ý định thay đổi mã của mọi người. Tuy nhiên, khi tiêu chuẩn thực sự được xác định, mã của bạn bây giờ sẽ bắt đầu cho bạn một lỗi khi sử dụng một thành viên riêng tư, thay vì hành xử khác.

Giải pháp là chỉ cần thay đổi tên của chức năng thành viên được bảo vệ của bạn để nó không được xem xét.

0

Đó là hành vi tự nhiên của C++, nếu mã người gọi đối tượng của lớp không phải là const để không chuyển đổi, được định nghĩa là bảo vệ. Bạn cần định nghĩa đối tượng của lớp là const hoặc sử dụng const-cast trên đối tượng của lớp, điều này sẽ dẫn đến phiên bản const của phương thức sẽ được gọi.

#include <iostream> 
class Foo { 
    public: 
     const void bar() const { std::cout << " const version called\n";} 
    protected: 
     void bar() { std::cout << " non const version called\n";} 
}; 

int main(int argc, char** argv) 
{ 
    const Foo c; 
    c.bar(); // -> work 

    Foo c1; 
    c1.bar(); // compiler complain -> void Foo::bar() is protected 
    const_cast<const Foo&>(c1).bar(); // fine 
} 
Các vấn đề liên quan