2009-11-25 42 views
9

Tôi có một thành viên trong lớp myMember là con trỏ myType. Tôi muốn gán thành viên này vào một hàm được khai báo là const. Tôi đang làm như sau:Nhiệm vụ của thành viên trong một chức năng const

void func() const 
{ 
    ... 
    const_cast<myType*>(myMember) = new myType(); 
    ... 
} 

Làm việc này hoạt động tốt trong VC++, nhưng GCC đưa ra lỗi với thông báo "yêu cầu giá trị được yêu cầu khi chuyển nhượng trái".

Làm cho thành viên có thể thay đổi cho phép tôi chỉ cần xóa const_cast và gán giá trị. Tuy nhiên, tôi không hoàn toàn chắc chắn rằng nó đi kèm với các tác dụng phụ khác.

Tôi có thể chỉ định thành viên của mình mà không phải làm cho thành viên có thể thay đổi được không? Làm sao? Có bất kỳ tác dụng phụ nào trong việc làm cho các thành viên có thể thay đổi được không?

Trả lời

8

Mã không thực sự hoạt động trong VC++ - bạn không cập nhật giá trị (hoặc ít nhất là không nên), do đó cảnh báo từ GCC. mã đúng là

const_cast<myType*&>(myMember) = new myType(); 

hoặc [từ phản ứng khác, nhờ: P]:

const_cast<ThisType*>(this)->myMember = new myType(); 

Làm cho nó có thể thay đổi một cách hiệu quả có nghĩa là bạn sẽ có được ngầm const_cast s trong const chức năng thành viên, mà nói chung là những gì bạn nên có chỉ đạo hướng tới khi bạn thấy mình đang tải các số const_cast s trên this. Không có 'tác dụng phụ để sử dụng mutable' khác hơn thế.

Như bạn có thể thấy từ các cuộc tranh luận kịch liệt xoay quanh câu hỏi này, việc sử dụng một cách rõ ràng mutable và rất nhiều const_cast s chắc chắn có thể là các triệu chứng có mùi xấu trong mã của bạn. Từ quan điểm khái niệm, bỏ đi chòm sao hoặc sử dụng mutable có thể có ý nghĩa lớn hơn nhiều. Trong một số trường hợp, điều chính xác để làm có thể là thay đổi phương thức thành không const, tức là sở hữu thực tế là nó đang sửa đổi trạng thái.

Tất cả phụ thuộc vào mức độ chính xác trong ngữ cảnh của bạn - bạn không muốn kết thúc chỉ cần phun mutable xung quanh như bụi pixie để làm công cụ, nhưng mutable được thiết kế để sử dụng nếu thành viên không phải là một phần của quan sát được trạng thái của đối tượng. Quan điểm nghiêm ngặt nhất của const-correctness sẽ giữ rằng không một chút trạng thái của đối tượng có thể được sửa đổi (ví dụ, điều này có thể là quan trọng nếu bạn là instance trong ROM ...) - trong những trường hợp bạn không muốn bất kỳ constness bị mất. Trong các trường hợp khác, bạn có thể có một số trạng thái bên ngoài được lưu trữ ở đâu đó bên ngoài đối tượng - ví dụ: bộ nhớ cache theo chủ đề cụ thể cũng cần được xem xét khi quyết định xem nó có phù hợp hay không.

+0

Cảm ơn. [min 15 chars] –

+1

Tôi nghĩ rằng trong khi điều này làm việc nó có thể là giải pháp tồi tệ nhất. Nó hoàn toàn ẩn những gì xảy ra (thay đổi trạng thái bên trong của một thể hiện bất biến) trong một const-cast trong hàm thành viên. "có thể thay đổi" sẽ rõ ràng hơn (hoặc sử dụng giao diện). –

+1

bỏ đi constness dẫn đến hành vi không xác định. Sử dụng có nguy cơ của riêng bạn. Một giải pháp tốt hơn sẽ là làm cho phương pháp không const. –

17

Kịch bản này - thay đổi trạng thái nội bộ được đóng gói không ảnh hưởng đến trạng thái bên ngoài (ví dụ: kết quả bộ nhớ đệm) - chính xác là từ khóa mutable.

+1

Biến đổi không phải là cách tốt nếu bạn muốn có quyền ghi vào thành viên chỉ bằng một phương thức const. – Staseg

+4

Trạng thái bên ngoài quan sát được của đối tượng không được thay đổi khi ghi vào đối tượng có thể thay đổi. Làm cho một thành viên có thể thay đổi chỉ để tránh lỗi trong hàm const là không hợp lệ nếu thay đổi có thể nhìn thấy bên ngoài và có thể dẫn đến hành vi không xác định. Sử dụng hợp pháp của mutable bao gồm bộ nhớ đệm, trong đó ví dụ như cùng một cuộc gọi hai lần trả về cùng một kết quả, nhưng nhanh hơn. – AshleysBrain

5
class Class{ 
int value; 
void func()const{ 
const_cast<Class*>(this)->value=123; 
} 
}; 
+0

Bạn cần đối tượng truyền thay vì một thành viên. – Staseg

+0

Cảm ơn. Tôi đang đi bởi mã đầu tiên của Ruben vì nó trông sạch hơn. Nếu có thể, tôi đánh dấu cả hai là "câu trả lời". –

6

const_cast gần như luôn là dấu hiệu của lỗi thiết kế. Trong ví dụ của bạn, func() không được là const hoặc myMember phải là mutable.

Người gọi func() sẽ mong đợi đối tượng của cô ấy không thay đổi; nhưng điều này có nghĩa là "không thay đổi theo cách cô ấy có thể nhận thấy"; đây là, không thay đổi trạng thái bên ngoài của nó.Nếu thay đổi myMember không thay đổi trạng thái bên ngoài đối tượng, đó là những gì từ khóa mutable dành cho; nếu không, func() không được là const, vì bạn sẽ phản bội chức năng đảm bảo của mình.

Hãy nhớ rằng mutable không phải là cơ chế để cải thiện tính chính xác của const; nó là một cơ chế để cải thiện nó.

+1

+1 "const_cast gần như luôn luôn là một dấu hiệu của thất bại thiết kế" –

+0

^Có, và đôi khi thất bại là trong một thư viện bạn đang sử dụng! Tôi sử dụng một trong đó có một con trỏ đến không 'const' nhưng (và tôi kiểm tra này thường xuyên :) không đột biến đối tượng. Đảm bảo trong kiến ​​thức này, vì vậy tôi có thể chuyển bất kỳ 'Obj const &' (bao gồm một tạm thời) cho thư viện đã nói, tôi bọc nó trong một hàm thực hiện một 'const_cast (& constObj)'. Xấu xí, có thể - nhưng truer đến thực tế của những gì thực sự xảy ra với 'Obj' và cho phép tôi làm những điều tốt hơn với thư viện ... ít nhất là cho đến khi tác giả quyết định sử dụng C++ nhiều hơn C [thở dài] –

+0

@underscore_d của việc sử dụng hợp pháp 'const_cast': xử lý với C++ không hoàn hảo. Nếu tôi có một xu cho mỗi người thợ săn đến không phải là một con trỏ đến chòm sao trên thế giới ... – Gorpik

1

Như Steve Gilham đã viết, mutable là câu trả lời đúng (và ngắn) cho câu hỏi của bạn. Tôi chỉ muốn gợi ý cho bạn theo một hướng khác. Có thể trong szenario của bạn để sử dụng giao diện (hoặc nhiều hơn một)? lẽ bạn có thể grok nó từ ví dụ sau:

class IRestrictedWriter // may change only some members 
{ 
public: 
    virtual void func() = 0; 
} 

class MyClass : virtual public IRestrictedWriter 
{ 
public: 
    virtual void func() 
    { 
    mValueToBeWrittenFromEverybody = 123; 
    } 

    void otherFunctionNotAccessibleViaIRestrictedWriter() 
    { 
    mOtherValue1 = 123; 
    mOtherValue2 = 345; 
    } 

    ... 
} 

Vì vậy, nếu bạn vượt qua đối với một số chức năng một IRestrictedReader * thay vì một const MyClass * nó có thể gọi func và do đó thay đổi mValueToBeWrittenFromEverybody trong khi mOtherValue1 là loại "const".

. Tôi thấy mutable luôn luôn là một chút của một hack (nhưng đôi khi sử dụng nó).

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