2010-03-26 47 views
5

Một đồng nghiệp hỏi về một số mã như thế này ban đầu có các mẫu trong đó.Tại sao tôi có thể gọi con trỏ hàm thành viên không phải là const từ một phương thức const?

Tôi đã xóa các mẫu, nhưng câu hỏi cốt lõi vẫn là: tại sao điều này biên dịch OK?

#include <iostream> 

class X 
{ 
public: 
    void foo() { std::cout << "Here\n"; } 
}; 

typedef void (X::*XFUNC)() ; 

class CX 
{ 
public: 
    explicit CX(X& t, XFUNC xF) : object(t), F(xF) {}  
    void execute() const { (object.*F)(); } 
private: 
    X& object; 
    XFUNC F; 
}; 

int main(int argc, char* argv[]) 
{ 
    X x; 
    const CX cx(x,&X::foo); 
    cx.execute(); 
    return 0; 
} 

Cho rằng CX là một đối tượng const, và chức năng thành viên thực hiện là const, do đó bên CX :: thực hiện con trỏ này là const.

Nhưng tôi có thể gọi hàm không phải thành viên thông qua con trỏ hàm thành viên.

Chức năng thành viên có trỏ một lỗ được ghi chép trong cấu trúc của thế giới không?

Vấn đề gì (có lẽ là hiển nhiên đối với người khác) mà chúng tôi đã bỏ lỡ?

Trả lời

7

Trong bối cảnh này object là một tài liệu tham khảo đến một X, không phải là một tài liệu tham khảo sang const X. Bộ định tính const sẽ được áp dụng cho thành viên (ví dụ: tham chiếu, nhưng tham chiếu không được là const), không áp dụng cho đối tượng được tham chiếu.

Nếu bạn thay đổi định nghĩa lớp của bạn để không sử dụng một tài liệu tham khảo:

// ... 
private: 
    X object; 
// ... 

bạn nhận được lỗi bạn đang mong đợi.

+0

Tuy nhiên, với phương thức 'const', người ta chỉ có thể gọi các phương thức' const' trên các đối tượng thành viên, đúng không? –

+3

Thành viên là tham chiếu, không phải là đối tượng được tham chiếu. –

7

Số const Ness của execute() chỉ ảnh hưởng đến con trỏ của lớp học this. Nó làm cho loại this a const T* thay vì chỉ T*. Đây không phải là một 'sâu' const mặc dù - nó chỉ có nghĩa là các thành viên mình không thể thay đổi, nhưng bất cứ điều gì họ trỏ đến hoặc tham chiếu vẫn có thể. Không thể thay đổi thành viên object của bạn vì không thể sắp xếp lại tham chiếu để trỏ đến bất kỳ nội dung nào khác. Tương tự, bạn không phải là thay đổi thành viên F, chỉ cần coi nó là con trỏ hàm thành viên. Vì vậy, đây là tất cả cho phép, và OK.

Thực tế là bạn làm cho ví dụ của bạn về CX const không thay đổi bất cứ điều gì: một lần nữa, đề cập đến các thành viên ngay lập tức không được phép sửa đổi, nhưng một lần nữa bất cứ điều gì họ trỏ đến vẫn có thể. Bạn vẫn có thể gọi hàm thành viên const trên các đối tượng const để không thay đổi ở đó.

Để minh họa:

class MyClass 
{ 
public: 
    /* ... */ 

    int* p; 

    void f() const 
    { 
     // member p becomes: int* const p 
     *p = 5; // not changing p itself, only the thing it points to - allowed 
     p = NULL; // changing content of p in const function - not allowed 
    } 
}; 
+1

Điều quan trọng cần lưu ý là về cơ bản bạn có thể xử lý 'T &' thành 'T * const'. Dù sao thì tôi đồng ý rằng việc tuyên truyền nông của 'const' là lạ, nhưng sau đó chúng tôi cũng có bản sao nông theo mặc định ... –

3

Các dụ object của class X không phải là const. Nó chỉ được tham chiếu bởi một đối tượng là const. Const-ness đệ quy áp dụng cho subobjects, không phải đối tượng tham chiếu.

Bằng logic thay thế, phương pháp const sẽ không thể sửa đổi bất kỳ điều gì. Điều đó được gọi là "hàm thuần túy", một khái niệm không tồn tại trong tiêu chuẩn hiện tại C++.

1

Bạn đang gọi foo trên object, không phải trên this.

Kể từ object được khai báo là một X&, trong một CX liên tục, nó thực sự là một X& const (mà không phải là giống như const X&) cho phép bạn gọi các phương thức const phi vào nó.

+0

Tôi cảm thấy như bạn nên đề cập đến" X & const "là không hợp lý trong ngôn ngữ, vì có không có thứ như 'tham chiếu lại'. –

0

Một cách hữu ích để suy nghĩ về nó có thể là đối tượng X của bạn không phải là thành viên của CX.

+0

Bạn có thể nghĩ về nó như thế này, nhưng nó không quan trọng nếu nó là hay không. Lớp đó có thể có một cá thể X ('đối tượng') là một thành viên và cũng là một thành viên tham chiếu để tham chiếu đến điều đó. Sau đó, bạn sẽ thấy rằng bạn không thể làm '(object. * F)();', nhưng vẫn có thể làm '(reference. * F)();' mặc dù nó tham chiếu chính xác cùng một điều mà ** là ** một thành viên của CX. – UncleBens

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