2013-03-22 41 views
37

Xét following code (và thực tế là VirtualAlloc() returns a void*):Đúng cách đúc các loại con trỏ

BYTE* pbNext = reinterpret_cast<BYTE*>(
    VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE)); 

tại sao reinterpret_cast chọn thay vì static_cast?

Tôi từng nghĩ rằng reinterpret_cast là OK cho ví dụ: chuyển con trỏ đến và từ các loại số nguyên (ví dụ: DWORD_PTR), nhưng để truyền từ một số void* đến một số BYTE*, không phải là static_cast OK?

Có bất kỳ sự khác biệt (tinh tế?) Nào trong trường hợp cụ thể này hay chỉ là cả hai khuôn mẫu con trỏ hợp lệ?

Chuẩn C++ có ưu tiên cho trường hợp này hay không, gợi ý cách thay thế cho trường hợp này?

+7

Ở vị trí đầu tiên, tôi sẽ không cho rằng các nhà phát triển Microsoft tuân theo "thực tiễn tốt trên thực tế". 'static_cast' là tốt ở đây. –

+0

'static_cast' nên được ưu tiên, nhưng một số người thích' reinterpret_cast' vì tên hiển thị những gì bạn đang làm (bạn đang diễn giải lại mẫu bit). –

+2

(Dù sao, +1, hãy tận hưởng huy hiệu 'câu hỏi hay '!) –

Trả lời

27

Dàn diễn viên có thể chấp nhận đối với các con trỏ đến các loại cơ bản, vì vậy, bạn chính xác là static_cast là không sao.

Khi chuyển đổi giữa hai loại con trỏ, có thể địa chỉ bộ nhớ cụ thể được giữ trong con trỏ cần thay đổi.

Đó là nơi hai phôi khác nhau. static_cast sẽ thực hiện điều chỉnh thích hợp. reinterpret_cast sẽ tránh thay đổi giá trị được lưu trữ của con trỏ.

Vì lý do đó, đó là quy tắc chung tốt cho static_cast giữa các loại con trỏ trừ khi bạn biết rằng reinterpret_cast là mong muốn.

5

Sử dụng static_cast để truyền con trỏ đến và từ void* được đảm bảo để bảo vệ địa chỉ.

reinterpret_cast mặt khác đảm bảo rằng nếu bạn truyền con trỏ từ loại này sang loại khác và quay lại kiểu gốc, địa chỉ đó sẽ được giữ nguyên.

Mặc dù với hầu hết các triển khai, bạn sẽ thấy kết quả tương tự khi sử dụng một trong hai cách này, nên ưu tiên sử dụng static_cast.

Và với C++11 Tôi nhớ rằng, sử dụng reinterpret_cast cho void* có hành vi được xác định rõ. Trước đó hành vi này bị cấm.

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void. 

độ phân giải được đề xuất (tháng Tám, 2010):

Thay đổi 5.2.10 [expr.reinterpret.cast] đoạn 7 như sau:

Một con trỏ đối tượng có thể được chuyển đổi một cách rõ ràng để một đối tượng con trỏ của một loại khác nhau. Khi giá trị “con trỏ đến T1” là được chuyển thành loại “con trỏ tới cv T2”, kết quả là static_cast (static_cast (v)) nếu cả T1 và T2 là bố cục tiêu chuẩn (3.9 [ basic.types]) và các yêu cầu căn chỉnh của T2 không có độ lệch là so với T1, hoặc nếu một trong hai loại bị vô hiệu.

Chuyển đổi giá trị loại "con trỏ thành T1" thành loại "con trỏ thành T2" (trong đó T1 và T2 là loại đối tượng và các yêu cầu liên kết của T2 không chặt chẽ hơn so với T1) và quay lại loại ban đầu của nó mang lại giá trị con trỏ ban đầu. Kết quả của bất kỳ chuyển đổi con trỏ nào khác không được chỉ định là .

Thông tin thêm here.

Nhờ có Jesse Good cho liên kết.

+2

Đây là [link] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1120) về việc diễn giải lại diễn viên, nó đã bị cấm trước C++ 11. –

18

Bạn nên static_cast. Sử dụng static_cast trong trường hợp bạn đang hoàn tác chuyển đổi tiềm ẩn.

Trong trường hợp cụ thể này, tuy nhiên, không có sự khác biệt vì bạn đang chuyển đổi từ void*. Nhưng nói chung, reinterpret_cast ing giữa hai con trỏ đối tượng được xác định là (§5.2.10/7):

Một con trỏ đối tượng có thể được chuyển đổi rõ ràng thành con trỏ đối tượng thuộc loại khác. Khi một prvalue v kiểu “con trỏ đến T1” được chuyển thành kiểu “con trỏ đến cv T2”, kết quả là static_cast<cv T2*>(static_cast<cv void*>(v)) nếu cả hai T1T2 nhiều loại tiêu chuẩn bố trí và các yêu cầu liên kết của T2 đang không nghiêm ngặt hơn các của T1 hoặc nếu một trong hai loại là void. Chuyển đổi giá trị của loại “con trỏ thành T1” thành loại “con trỏ thành T2” (trong đó T1T2 là loại đối tượng và yêu cầu căn chỉnh T2 không chặt chẽ hơn so với số T1) và quay lại loại ban đầu. giá trị con trỏ. Kết quả của bất kỳ chuyển đổi con trỏ nào khác không được xác định.

Mỏ nhấn mạnh. Kể từ T1 cho bạn đã là void*, dàn diễn viên đến void* trong reinterpret_cast không làm gì cả. Điều này không đúng nói chung, đó là những gì Drew Dormann is saying:

#include <iostream> 

template <typename T> 
void print_pointer(const volatile T* ptr) 
{ 
    // this is needed by oversight in the standard 
    std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl; 
} 

struct base_a {}; 
struct base_b {}; 
struct derived : base_a, base_b {}; 

int main() 
{ 
    derived d; 

    base_b* b = &d; // implicit cast 

    // undo implicit cast with static_cast 
    derived* x = static_cast<derived*>(b); 

    // reinterpret the value with reinterpret_cast 
    derived* y = reinterpret_cast<derived*>(b); 

    print_pointer(&d); 
    print_pointer(x); 
    print_pointer(y); 
} 

Output:

00CBFD5B
00CBFD5B
00CBFD5C

(Lưu ý rằng vì y không thực sự điểm đến một số derived, sử dụng nó là hành vi không xác định.)

Ở đây, reinterpret_cast có giá trị khác vì nó đi qua void*. Đây là lý do tại sao bạn nên sử dụng static_cast khi có thể và reinterpret_cast khi cần.

+0

Hình minh họa đẹp! Về chuyển đổi 'void *' - tôi có hồi ức dai dẳng này * cụ thể * cho các chuyển đổi tới/từ 'void *', tiêu chuẩn cho phép 'reinterpret_cast' thay đổi giá trị. Và đó chỉ là một biến đổi chu kỳ 'X *' thành 'void *' thành 'X *' được bảo đảm để bảo toàn địa chỉ. Điều đó có đúng không? –

+0

@DrewDormann: Tôi không chắc chắn tôi theo dõi, bạn đang đề cập đến dòng nào? Đối với 'reinterpret_cast', tất cả các trường hợp với' void * '(hoặc là kiểu nguồn hoặc kiểu đích) đi qua mệnh đề tôi trích dẫn, do đó, điều duy nhất có liên quan từ điểm này là' static_cast' w.r.t. 'void *'. Hy vọng rằng làm rõ. – GManNickG

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