2011-11-30 41 views
8

Tôi đã có một phương thức const trong lớp của mình, không thể thay đổi thành non-const. Trong phương pháp này, tôi cần phải gọi một phương pháp không const nhưng trình biên dịch không cho phép tôi làm điều đó.Làm thế nào để gọi một phương pháp không const từ một phương pháp const?

Có cách nào xung quanh nó không? Dưới đây là một ví dụ đơn giản của mã của tôi:

int SomeClass::someMethod() const { 
    QColor saveColor = color(); 
    setColor(QColor(255,255,255)); // Calling non-const method 

    // .... 

    setColor(saveColor); // restore color 

    return 1; 
} 
+3

Nó sẽ không được chống lại các nguyên tắc chung là một hàm const? – DumbCoder

+2

@DumbCoder, từ bên ngoài, nó là một hàm const trong đó không có gì hiển thị cho khách hàng đã được thay đổi. –

+0

Một phương pháp không const ngụ ý rằng nó đang thay đổi một số dữ liệu trong đối tượng của bạn. Nếu 'someMethod()' gọi một phương thức không phải là const, thì nó đang gián tiếp thay đổi dữ liệu. Vì vậy, thực sự, 'someMethod()' đơn giản không nên là 'const'. –

Trả lời

5

Một trong những thách thức khi thực hiện const-độ chính xác là bạn không thể làm điều đó nửa chừng. Đó là hoặc là tất cả hoặc không có gì. Nếu bạn cố gắng làm điều đó nửa chừng, bạn kết thúc ở một nơi khó khăn như bạn đang ở đây. Bạn kết thúc với một lớp học tốt đẹp const-sai được sử dụng bởi một số điên cũ, thường di sản (hoặc được viết bởi một crummudgeon cũ) mã đó không phải là const-chính xác và nó chỉ không hoạt động. Bạn đang tự hỏi nếu const-đúng là giá trị tất cả các rắc rối.

I need to call a non-const method [from a const method] 

Bạn không thể - không trực tiếp. Cũng không nên bạn. Tuy nhiên, có một cách khác ...

Rõ ràng bạn không thể gọi phương thức không phải là const từ phương thức const. Nếu không, const sẽ không có ý nghĩa khi được áp dụng cho các chức năng của thành viên.

Chức năng thành viên const có thể thay đổi biến thành viên được đánh dấu mutable, nhưng bạn đã chỉ ra rằng điều này là không thể trong trường hợp của bạn.

Bạn có thể thử bỏ đi const bằng cách thực hiện một cái gì đó như SomeClass* me = const_cast<SomeClass*>(this); nhưng A) Điều này thường sẽ dẫn đến UB hoặc 2) Nó vi phạm toàn bộ ý tưởng của const-độ chính xác.

Một điều bạn có thể làm, nếu những gì bạn đang thực sự cố gắng thực hiện sẽ hỗ trợ điều này, là tạo một đối tượng proxy không const và không làm const. Để wit:

#include <iostream> 
#include <string> 
using namespace std; 

class Gizmo 
{ 
public: 
    Gizmo() : n_(42) {}; 
    void Foo() const; 
    void Bar() { cout << "Bar() : " << n_ << "\n"; } 
    void SetN(int n) { n_ = n; }; 
    int GetN() const { return n_; } 
private: 
    int n_; 
}; 

void Gizmo::Foo() const 
{ 
    // we want to do non-const'y things, so create a proxy... 
    Gizmo proxy(*this); 
    int save_n = proxy.GetN(); 
    proxy.SetN(save_n + 1); 
    proxy.Bar(); 
    proxy.SetN(save_n); 
} 

int main() 
{ 
    Gizmo gizmo; 
    gizmo.Foo(); 
} 
+0

Làm thế nào nó hữu ích? Tôi bối rối. Proxy ở đây không phải là proxy, vì nó không đại diện cho đối tượng gốc. Nó chỉ là một vật thể địa phương không có sức mạnh phi thường. Làm việc với proxy không tương đương với làm việc với đối tượng gốc. – Nawaz

+2

@nawaz: nếu nó giúp op có được công việc của mình được thực hiện một cách phù hợp tiêu chuẩn, hữu ích của nó. –

11

Bạn có thể sử dụng const_cast trên this con trỏ,

int SomeClass::someMethod() const { 
    const_cast<SomeClass*>(this)->setColor(...);// Calling non-const method 
    //whatever 
} 

nhưng nếu bạn làm điều đó cho một đối tượng mà ban đầu được công bố const bạn chạy vào hành vi không xác định.

Vì vậy, đây:

SomeClass object; 
object.someMethod(); 

là okay, nhưng điều này:

const SomeClass object; 
object.someMethod(); 

mang lại hành vi không xác định.

Giải pháp thực tế là chức năng const của bạn không được là const ngay từ đầu.

+1

Tôi cho rằng người ta có thể sử dụng một nhà máy và nhà thầu tư nhân, để đảm bảo rằng không có 'const' trường hợp của' SomeClass'. Nhưng trừ khi điều đó đã có, đó là một sự thay đổi lớn hơn nhiều so với giao diện lớp học so với những thay đổi mà người hỏi bị cấm thực hiện, vì vậy có lẽ không có nhiều sự giúp đỡ. –

5

Làm cách nào để gọi phương thức không const từ phương pháp const?

Bạn không nên. Bạn có thể gặp phải hành vi không xác định nếu bạn bỏ đi cấu trúc của this, sử dụng const_cast. Việc sử dụng const_cast sẽ đóng miệng của trình biên dịch lên, nhưng đó không phải là một giải pháp. Nếu bạn cần làm, thì điều đó có nghĩa là chức năng const không nên là const ngay từ đầu. Làm cho nó không phải là const.

Hoặc, bạn nên làm điều gì đó khác, điều này sẽ không yêu cầu bạn gọi hàm không const từ chức năng const. Giống như, không gọi hàm setColor? Giống như, tách hàm const thành nhiều hơn một hàm (nếu bạn có thể làm điều đó)? Hay cái gì khác?

Trong trường hợp cụ thể của bạn, nếu setColor chỉ đặt một số biến thành viên, nói m_color, sau đó bạn có thể khai báo nó mutable:

mutable QColor m_color; 

và sau đó đặt nó trong chức năng const của bạn, mà không gọi setColor chức năng, và không có đang thực hiện const_cast.

+0

Đó không phải là một câu trả lời cho câu hỏi như tôi vừa viết Tôi không thể thay đổi nó thành không const. –

+0

@Laurent: Xem bản chỉnh sửa. – Nawaz

+2

@Laurent, có khả năng bạn có thể, nếu bạn có thể làm cho các thành viên chạm vào chức năng đó 'mutable'? – Nim

6

Nếu bạn cần thay đổi một số trạng thái nội bộ bên trong một const -method bạn cũng có thể ban bố tình trạng bị ảnh hưởng mutable:

class Foo { 
public: 
    void doStuff() const { bar = 5; } 
private: 
    mutable int bar; 
}; 

này dành cho những trường hợp bạn có những thứ như mutexes là thành viên của lớp học của bạn. Nhận và phát hành một mutex không ảnh hưởng đến trạng thái hiển thị của máy khách, nhưng bị cấm về mặt kỹ thuật trong một phương thức const. Giải pháp là để đánh dấu mutex mutable. Trường hợp của bạn trông tương tự, mặc dù tôi nghĩ rằng lớp học của bạn yêu cầu một số tái cấu trúc để giải pháp này được áp dụng.

Ngoài ra, bạn có thể muốn đọc this answer để xem cách bạn có thể thực hiện trạng thái tạm thời thay đổi ngoại lệ này an toàn khi sử dụng RAII.

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