2009-11-29 22 views
9
class Foo { 
    protected: 
    QPoint& bar() const; 

    private: 
    QPoint m_bar; 
}; 

QPoint& Foo::bar() const { 
    return m_bar; 
} 

Tôi đã nhận lỗi này:Làm cách nào để trả lại tài liệu tham khảo cho thành viên lớp học đúng cách?

lỗi: khởi tạo không hợp lệ của tài liệu tham khảo của loại 'QPoint &' từ biểu hiện của loại 'const QPoint'

Tuy nhiên nó hoạt động nếu tôi thay đổi nó như thế này:

QPoint& Foo::bar() const { 
    return (QPoint&) m_bar; 
} 

1) Tôi không hiểu tại sao trình biên dịch lại nói rằng QPoint của tôi là const.

2) Có được phép bỏ dàn diễn viên ở đó không?

Trả lời

21

Trong hàm không phải thành viên của lớp Foo con trỏ this là loại Foo* const - tức là, con trỏ là const, nhưng không phải là trường hợp nó trỏ đến. Tuy nhiên, trong hàm thành viên const, con trỏ this thuộc loại const Foo* const. Điều này có nghĩa là đối tượng nó trỏ đến là không đổi. Do đó, trong ví dụ của bạn, khi bạn sử dụng this->m_bar (trong đó m_bar chỉ là dạng ngắn), thì m_bar là thành viên của một đối tượng không đổi - đó là lý do bạn không thể trả về nó dưới dạng tham chiếu không phải const. Điều này thực sự có ý nghĩa từ POV thiết kế: Nếu đối tượng Foo là đối tượng không đổi và bạn được phép gọi Foo::bar cho các đối tượng không đổi, nếu điều này sẽ trả về tham chiếu không const cho một số nội dung mà bạn có thể fiddle với, bạn sẽ có thể thay đổi trạng thái của một đối tượng liên tục.

Bây giờ bạn phải nhìn vào thiết kế của bạn và tự hỏi mình làm thế nào bạn đã đến thời điểm này và ý định thực sự của bạn là gì. Nếu thành viên m_bar đó không thực sự là một phần của trạng thái của đối tượng (ví dụ, nó chỉ dành cho mục đích gỡ lỗi), thì bạn có thể xem xét làm cho nó mutable. Nếu nó là một phần của trạng thái của đối tượng, thì bạn phải tự hỏi tại sao bạn muốn trả về tham chiếu không const cho một số dữ liệu nội bộ của một đối tượng không đổi. Làm cho hàm thành viên không phải là const hoặc trả về tham chiếu const hoặc quá tải hàm thành viên:

class Foo { 
public: 
    const QPoint& bar() const {return m_bar;} 
     QPoint& bar()  {return m_bar;} 
    // ... 
}; 
+2

Cảm ơn bạn. Tôi nghĩ rằng const chức năng vòng loại đã có nghĩa là để chỉ đảm bảo rằng các chức năng chính nó sẽ không sửa đổi các đối tượng. Tôi muốn m_bar có thể sửa đổi được nên tôi đã xóa vòng loại. – Dimitris

+0

Thực ra nó ngăn không cho chức năng sửa đổi đối tượng, nhưng nó làm như vậy bằng cách chỉ đưa một đối tượng const cho hàm. Tuy nhiên, hàm đó không thể đưa ra một tham chiếu không const là nhiều hơn một tác dụng phụ ngẫu nhiên: Bạn không nên lấy các tham chiếu không const cho một đối tượng không đổi theo bất kỳ cách nào. – sbi

3

const trên hàm cho trình biên dịch biết rằng hàm không sửa đổi bất kỳ biến thành viên nào. Tuy nhiên, kể từ khi bạn trở về một tham chiếu đến biến thành viên nó không thể đảm bảo rằng bất kỳ hơn và do đó có lỗi biên dịch.

Trình biên dịch không mong đợi biến thành viên của bạn là const nhưng sự trở về từ hàm sẽ là const. Hủy bỏ các const từ chức năng def.

4

Điều bạn đang cố gắng làm là không không. Bạn không muốn trả lại một QPoint & từ chức năng thanh của bạn vì điều này phá vỡ đóng gói bởi vì người gọi có thể sửa đổi m_bar ra từ bên dưới QPoint. (may mắn là bạn có hàm được khai báo là const hoặc bạn sẽ không gặp lỗi ở đây và bạn vẫn sẽ phá vỡ đóng gói).

gì bạn muốn trong trường hợp này là

const QPoint &Foo::bar() const 
{ 
    return m_bar; 
} 

Chỉnh sửa dựa trên người dùng bình luận:

IMHO, điều này sẽ được giải quyết tốt hơn bằng cách thêm một setX để Foo thay vì gọi một tổ chức phi-const hàm từ một tham chiếu không const trả về bởi hàm accessor mà thực sự là const. Quá tải các chức năng accessor để loại bỏ các const chỉ cần đặt một ban nhạc hỗ trợ trên các vấn đề thực sự và chỉ giấu một thực tế là đóng gói là bị tổn hại. Thêm setX để Foo khắc phục sự cố đóng gói và cũng cắm giao diện bị rò rỉ mà bạn đang tạo bằng cách trả về tham chiếu không phải const cho dữ liệu cá nhân.

Ví dụ, nếu bạn đặt foo.bar().setX(100); ở nhiều nơi trên ứng dụng của bạn và sau đó thay đổi loại QPoint một mà không thực hiện setX hoặc đơn giản là đổi tên chức năng setX nói setPointX bạn có vấn đề b/c bây giờ bạn có để đi xung quanh một đổi tên/refactor tất cả các cuộc gọi. Việc tạo setX trên Foo làm cho mã dễ gọi hơn foo.setX(100)foo.bar().setX(100), là chính xác và đóng gói dữ liệu của bạn. Nếu bạn thay đổi QPoint thành các tọa độ 2 x và y trong Foo, không có gì ngoài lớp của bạn sẽ phải thay đổi b/c bạn có đóng gói tốt.

+0

Foo :: bar() là một người truy cập. Tôi muốn có thể sửa đổi QPoint như foo.bar(). SetX (100); Tôi nghĩ rằng trình độ chức năng const được dùng để đảm bảo rằng bản thân hàm sẽ không sửa đổi đối tượng. Cảm ơn bạn anyway! – Dimitris

+0

Tôi có phản hồi được cập nhật dựa trên phản hồi của bạn. –

3

thể sử dụng

const QPoint& Foo::bar() const { 
    return m_bar; 
} 

hoặc

QPoint& Foo::bar() { 
    return m_bar; 
} 

Tôi đoán bạn cũng có thể tuyên bố m_bar như:

mutable QPoint m_bar; 

EDIT: quốc phòng có thể thay đổi

@tstenner:
có thể thay đổi vị trí của nó. Nó không phải là 'ác' chắc chắn không nhiều hơn void * hoặc đúc. Giả sử một cái gì đó như sau:

class foo { 
    SomeType bar; 

public: 
    foo() : bar() { } 

    const SomeType& bar() const { return a; } 
}; 

Trong trường hợp này, thanh được LUÔN LUÔN xây dựng, ngay cả khi thanh() không bao giờ được gọi. Điều này có thể là tốt, nhưng nếu SomeType có một hàm tạo tốn kém, nó có thể thích hợp hơn để cho phép sự khởi tạo lười biếng của thanh.

xem xét:

class foo2 { 
    mutable SomeType* bar; 
    mutable bool cached; 

public: 
    foo2() : bar(0), cached(false) { } 

    const SomeType& bar() const { 
     if(! cached) { 
      cached = true; 
      bar = new SomeType(); 
     } 
     return *bar; 
    } 
}; 

này cho phép foo2 để bắt chước một lớp instantiated không lười biếng, trong khi được lười biếng khởi tạo. Cấp, sự biến đổi của foo2 có ý nghĩa đối với an toàn luồng, nhưng chúng có thể được khắc phục bằng khóa nếu cần thiết.

+2

Bạn không muốn sử dụng có thể thay đổi, vì nó là điều xấu. – tstenner

+1

Mutable nên được sử dụng ít, nhưng nó có sử dụng mà không phải là điều ác. Ví dụ nếu bạn có hàm truy cập/thành viên const cần đọc một đối tượng được truy cập bởi nhiều luồng và nó được đồng bộ thông qua một mutex lớp, bạn sẽ cần phải biến mutex thành các hàm const để đọc đối tượng một cách an toàn. –

2

Nếu nó cho phép bạn trả lại tham chiếu đến biến, thì bạn có thể sửa đổi một phiên bản const của lớp Foo. Hãy xem xét mã như vậy:

void baz(const Foo &foo) 
{ 
    QPoint &ref = foo.bar(); //if it was allowed... 
    ref = 5; // ...you could have modified a const object foo! 
} 

Do đó trình biên dịch ngăn bạn làm điều đó.

Bạn nên khai báo phương thức trả lại const QPoint& hoặc để sửa đổi hiểu biết của bạn về những gì thực sự là const.

Ai đó có thể khuyên bạn sử dụng mutable. Đừng. mutable từ khóa là cho phép thực hiện lớp để sửa đổi biến thành viên nội bộ của các đối tượng const (ví dụ, cho mục đích ghi nhớ), thay vì để lộ biến không const (!) Vào mã ngoài.

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