2016-01-04 15 views
8

Tôi có hai phiên bản của cùng một hàm thành viên tĩnh: một tham số con trỏ tới tham số và lấy tham số con trỏ tới tham số. Tôi muốn tránh trùng lặp mã.
Sau khi đọc một số câu hỏi stack overflow (những là tất cả về không tĩnh chức năng thành viên mặc dù) tôi đã đưa ra với điều này:các phiên bản const và non-const của các hàm thành viên * tĩnh *

class C { 
private: 
    static const type* func(const type* x) { 
    //long code 
    } 

    static type* func(type* x) { 
     return const_cast<type*>(func(static_cast<const type*>(x))); 
    } 
public: 
    //some code that uses these functions 
}; 

(Tôi biết tung hứng với con trỏ nói chung là một ý tưởng tồi, nhưng tôi m thực hiện một cấu trúc dữ liệu)

tôi tìm thấy một số mã trong libstdC++ trông như thế này:.
Chú ý: đây không phải là chức năng thành viên

static type* local_func(type* x) 
{ 
    //long code 
} 

type* func(type* x) 
{ 
    return local_func(x); 
} 

const type* func(const type* x) 
{ 
    return local_func(const_cast<type*>(x)); 
} 

Trong phương pháp tiếp cận đầu tiên, mã nằm trong một hàm nhận tham số con trỏ tới tham số.
Trong cách tiếp cận thứ hai, mã nằm trong một hàm nhận tham số con trỏ tới không tham số.
Phương pháp nào thường nên được sử dụng? Cả hai đều đúng không?

+6

nhận thuật ngữ đúng. Các thành viên lớp tĩnh ** không thể ** là const. – SergeyA

+0

Bạn cũng có thể sử dụng các mẫu để tránh trùng lặp mã, mặc dù có thể quá mức nếu 'loại' được mã hóa cứng. – Manik

+2

@SergeyA Tôi đã viết "(bằng cách này tôi có nghĩa là họ lấy và trả lại con trỏ đến các đối tượng const/non-const)" – Xack

Trả lời

1

Quy tắc quan trọng nhất là chức năng giao diện (phương thức công khai, chức năng tự do không phải là một trong không gian tên chi tiết, v.v.), không được bỏ đi độ const của đầu vào của nó. Scott Meyer là một trong những người đầu tiên nói chuyện về việc ngăn chặn sự trùng lặp sử dụng const_cast, đây là một ví dụ điển hình (How do I remove code duplication between similar const and non-const member functions?):

struct C { 
    const char & get() const { 
    return c; 
    } 
    char & get() { 
    return const_cast<char &>(static_cast<const C &>(*this).get()); 
    } 
    char c; 

};

Điều này đề cập đến các phương pháp thể hiện chứ không phải là các hàm tĩnh/miễn phí, nhưng nguyên tắc thì giống nhau. Bạn nhận thấy rằng phiên bản không const thêm const để gọi phương thức khác (đối với một phương thức thể hiện, con trỏ this là đầu vào). Sau đó nó bỏ đi constness ở cuối; điều này là an toàn vì nó biết đầu vào ban đầu không phải là const.

Thực hiện điều này theo cách khác sẽ rất nguy hiểm. Nếu bạn cast đi constness của một tham số chức năng bạn nhận được, bạn đang dùng một rủi ro lớn trong UB nếu đối tượng được truyền cho bạn thực sự là const. Cụ thể là, nếu bạn gọi bất kỳ phương pháp nào thực sự làm biến đổi đối tượng (rất dễ xảy ra do tai nạn bây giờ bạn đã bỏ đi constness), bạn có thể dễ dàng nhận được UB:

Tiêu chuẩn C++, mục § 5.2. 11/7 [const cast]

[Ghi chú: Tùy thuộc vào loại đối tượng, thao tác ghi thông qua con trỏ, lvalue hoặc con trỏ đến thành viên dữ liệu từ một số const_cast hành vi. —end note]

Nó không phải là xấu trong phương pháp riêng tư/chức năng thực hiện bởi vì có lẽ bạn cẩn thận kiểm soát như thế nào/khi nó được gọi, nhưng tại sao làm theo cách này? Nguy hiểm hơn là không có lợi ích. Khái niệm, thường là trường hợp khi bạn có một phiên bản const và không const của cùng một hàm, bạn chỉ cần truyền theo tham chiếu nội bộ của đối tượng (vector::operator[] là một ví dụ kinh điển), và không thực sự làm thay đổi bất cứ điều gì, có nghĩa là nó sẽ an toàn theo cách bạn viết.Nhưng nó vẫn còn nguy hiểm hơn để bỏ đi các constness của đầu vào; mặc dù bạn có thể không có khả năng làm hỏng nó, hãy tưởng tượng một thiết lập nhóm nơi bạn viết nó sai cách và nó hoạt động tốt, và sau đó một người nào đó thay đổi việc thực hiện để thay đổi một cái gì đó, cho bạn UB.

Nói tóm lại, trong nhiều trường hợp nó có thể không tạo sự khác biệt thực tế, nhưng có một cách chính xác để làm điều đó đó là nghiêm chỉnh tốt hơn so với phương án: thêm constness đến đầu vào, và loại bỏ constness từ đầu ra .

+0

"const_cast ngay lập tức gọi UB vì nó bỏ đi độ chói của đối tượng const" ... không, đó không phải UB. Trên thực tế, cố gắng sửa đổi một đối tượng 'const' là UB (thông qua bất kỳ phương thức nào, kể cả sau khi sử dụng' const_cast', nhưng chính bản thân nó không sửa đổi). –

+0

@BenVoigt Lỗi của tôi, bạn đã đúng. Tôi tò mò sau đó, giả sử bạn có con trỏ không const này đến một đối tượng const. Điều gì nếu bây giờ bạn gọi một phương thức, bạn sẽ nhận được quá tải không const của một phương thức trên một đối tượng const. Đây có phải là UB tự động không? Hay nó sau đó lần lượt phụ thuộc vào phương pháp làm gì? Những con rùa đều lặn xuống dưới? ;-) –

+0

Tôi tin tưởng tinh thần của những gì tôi đã viết vẫn chính xác, tôi muốn sửa nó để cung cấp cho nó sự ủng hộ chính xác về mặt kỹ thuật với sự giúp đỡ của bạn. –

0

Tôi thực sự chỉ thấy phiên bản đầu tiên của bạn trước đây, vì vậy từ kinh nghiệm của tôi, nó là thành ngữ phổ biến hơn.

Phiên bản đầu tiên có vẻ đúng với tôi trong khi phiên bản thứ hai có thể dẫn đến hành vi không xác định nếu (A) bạn chuyển đối tượng const thực cho hàm và (B) long code ghi vào đối tượng đó. Cho rằng trong trường hợp đầu tiên trình biên dịch sẽ cho bạn biết nếu bạn đang cố gắng để viết cho đối tượng tôi sẽ không bao giờ khuyên bạn nên lựa chọn 2 vì nó được. Tuy nhiên, bạn có thể xem xét một hàm độc lập nhận/trả về const.

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