Tôi đã nghe nói rằng static_cast
chức năng này nên được ưu tiên hơn so với kiểu đúc kiểu C hoặc kiểu hàm đơn giản. Điều này có đúng không? Tại sao?
Tôi đã nghe nói rằng static_cast
chức năng này nên được ưu tiên hơn so với kiểu đúc kiểu C hoặc kiểu hàm đơn giản. Điều này có đúng không? Tại sao?
Câu trả lời:
Nguyên nhân chủ yếu là phôi C cổ điển làm cho không có sự phân biệt giữa những gì chúng ta gọi là static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, và dynamic_cast<>()
. Bốn điều này hoàn toàn khác nhau.
A static_cast<>()
thường an toàn. Có một chuyển đổi hợp lệ trong ngôn ngữ hoặc một nhà xây dựng thích hợp làm cho nó có thể. Lần duy nhất có một chút rủi ro là khi bạn bỏ xuống một lớp kế thừa; bạn phải chắc chắn rằng đối tượng thực sự là hậu duệ mà bạn cho là nó, có nghĩa là bên ngoài ngôn ngữ (giống như một lá cờ trong đối tượng). A dynamic_cast<>()
là an toàn miễn là kết quả được kiểm tra (con trỏ) hoặc một ngoại lệ có thể được đưa vào tài khoản (tham chiếu).
Mặt khác reinterpret_cast<>()
(hoặc a const_cast<>()
) luôn nguy hiểm. Bạn nói với trình biên dịch: "hãy tin tôi: tôi biết điều này không giống như một foo
(điều này trông như thể nó không thể thay đổi), nhưng nó là".
Vấn đề đầu tiên là hầu như không thể biết được cái nào sẽ xảy ra trong dàn diễn viên kiểu C mà không nhìn vào các đoạn mã lớn và phân tán và biết tất cả các quy tắc.
Hãy giả sử như sau:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Bây giờ, hai cái này được biên dịch theo cùng một cách:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
Tuy nhiên, hãy xem mã gần như giống hệt nhau này:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
Như bạn có thể thấy, không có cách nào dễ dàng để phân biệt giữa hai tình huống mà không biết nhiều về tất cả các lớp liên quan.
Vấn đề thứ hai là dàn diễn viên kiểu C quá khó định vị. Trong các biểu thức phức tạp, có thể rất khó để nhìn thấy các phôi kiểu C. Hầu như không thể viết một công cụ tự động cần định vị các phôi kiểu C (ví dụ như một công cụ tìm kiếm) mà không có giao diện trình biên dịch C ++ đầy đủ. Mặt khác, thật dễ dàng để tìm kiếm "static_cast <" hoặc "reinterpret_cast <".
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
Điều đó có nghĩa là, không chỉ các diễn viên kiểu C nguy hiểm hơn, mà việc tìm ra tất cả chúng để đảm bảo rằng chúng đúng là rất khó khăn.
static_cast
để bỏ xuống một hệ thống phân cấp thừa kế, nhưng đúng hơn dynamic_cast
. Điều đó sẽ trả về con trỏ null hoặc con trỏ hợp lệ.
static_cast
trong tình huống đó. dynamic_cast
có thể an toàn hơn, nhưng nó không phải luôn luôn là lựa chọn tốt nhất. Đôi khi bạn biết rằng một con trỏ trỏ đến một kiểu con nhất định, có nghĩa là mờ đối với trình biên dịch và a static_cast
nhanh hơn. Trong ít nhất một số môi trường, dynamic_cast
yêu cầu hỗ trợ trình biên dịch tùy chọn và chi phí thời gian chạy (bật RTTI) và bạn có thể không muốn bật nó chỉ cho một vài kiểm tra mà bạn có thể tự thực hiện. RTTI của C ++ chỉ là một giải pháp khả thi cho vấn đề.
static_cast
. Tương đương C reinterpret_cast
là *(destination_type *)&
, tức là lấy địa chỉ của đối tượng, chuyển địa chỉ đó sang một con trỏ đến một loại khác, sau đó hủy bỏ hội nghị. Ngoại trừ trường hợp các kiểu ký tự hoặc các kiểu cấu trúc nhất định mà C xác định hành vi của cấu trúc này, nó thường dẫn đến hành vi không xác định trong C.
int
(và int
một mình), tại sao sử dụng static_cast<int>
so với (int)
lợi ích duy nhất dường như là với các biến lớp và con trỏ. Yêu cầu bạn giải thích về điều này.
int
dynamic_cast
không áp dụng, nhưng tất cả các lý do khác đều có. Ví dụ: nói let của v
là một tham số chức năng khai báo là float
, sau đó (int)v
là static_cast<int>(v)
. Nhưng nếu bạn thay đổi tham số thành float*
, (int)v
lặng lẽ trở thành reinterpret_cast<int>(v)
trong khi static_cast<int>(v)
bất hợp pháp và được trình biên dịch bắt chính xác.
Một mẹo thực dụng: bạn có thể dễ dàng tìm kiếm từ khóa static_cast trong mã nguồn của mình nếu bạn có kế hoạch dọn dẹp dự án.
int
tham số duy nhất .
Tóm lại :
static_cast<>()
cung cấp cho bạn khả năng kiểm tra thời gian biên dịch, dàn diễn viên C-Style thì không.static_cast<>()
có thể được phát hiện dễ dàng ở bất cứ đâu trong mã nguồn C ++; ngược lại, diễn viên C_Style khó phát hiện hơn.- Ý định được truyền đạt tốt hơn nhiều bằng cách sử dụng phôi C ++.
Giải thích thêm :
Các diễn viên tĩnh thực hiện chuyển đổi giữa các loại tương thích . Nó tương tự như dàn diễn viên theo phong cách C, nhưng hạn chế hơn. Ví dụ, ép kiểu C sẽ cho phép một con trỏ nguyên trỏ đến một char.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Vì điều này dẫn đến một con trỏ 4 byte trỏ đến 1 byte bộ nhớ được phân bổ, việc ghi vào con trỏ này sẽ gây ra lỗi thời gian chạy hoặc sẽ ghi đè lên một số bộ nhớ liền kề.
*p = 5; // run-time error: stack corruption
Ngược lại với kiểu đúc C, ép kiểu tĩnh sẽ cho phép trình biên dịch kiểm tra xem các kiểu dữ liệu con trỏ và con trỏ có tương thích hay không, cho phép lập trình viên bắt được phép gán con trỏ không chính xác này trong quá trình biên dịch.
int *q = static_cast<int*>(&c); // compile-time error
Đọc thêm về:
Sự khác biệt giữa truyền kiểu static_cast <> và kiểu C
và
Truyền thông thường so với static_cast so với Dynamic_cast
static_cast<>()
dễ đọc hơn. Ý tôi là, đôi khi nó là như vậy, nhưng hầu hết thời gian - đặc biệt là về các loại số nguyên cơ bản - nó chỉ dài dòng khủng khiếp và không cần thiết. Ví dụ: Đây là một hàm hoán đổi các byte của một từ 32 bit. Nó gần như không thể đọc được bằng cách sử dụng static_cast<uint##>()
phôi, nhưng khá dễ hiểu khi sử dụng (uint##)
phôi. Ảnh mã: imgur.com/NoHbGve
always
. (nhưng hầu hết các lần có) Chắc chắn có những trường hợp diễn viên kiểu c dễ đọc hơn. Đó là một trong những lý do khiến phong cách c vẫn còn sống và đá trong c ++ imho. :) Nhân tiện, đó là một ví dụ rất hay
(uint32_t)(uint8_t)
) để đạt được các byte đó ngoài mức thấp nhất được đặt lại. Cho rằng có bitwise và ( 0xFF &
). Sử dụng phôi là che giấu ý định.
Câu hỏi lớn hơn so với việc chỉ sử dụng đúc kiểu héo static_cast hoặc C vì có những điều khác nhau xảy ra khi sử dụng phôi kiểu C. Các toán tử đúc C ++ được dự định để làm cho các hoạt động này rõ ràng hơn.
Trên bề mặt phôi kiểu static_cast và C xuất hiện cùng một thứ, ví dụ như khi truyền một giá trị này sang giá trị khác:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Cả hai đều đúc giá trị số nguyên thành gấp đôi. Tuy nhiên khi làm việc với con trỏ mọi thứ trở nên phức tạp hơn. vài ví dụ:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
Trong ví dụ này (1) có thể OK vì đối tượng được chỉ đến bởi A thực sự là một thể hiện của B. Nhưng nếu bạn không biết tại thời điểm đó trong mã thì điều gì thực sự chỉ đến? (2) có thể hoàn toàn hợp pháp (bạn chỉ muốn xem xét một byte của số nguyên), nhưng cũng có thể là một lỗi trong trường hợp một lỗi sẽ tốt, như (3). Các toán tử đúc C ++ được dự định để phơi bày các vấn đề này trong mã bằng cách cung cấp các lỗi thời gian biên dịch hoặc thời gian chạy khi có thể.
Vì vậy, để "đúc giá trị" nghiêm ngặt, bạn có thể sử dụng static_cast. Nếu bạn muốn truyền đa hình thời gian chạy của con trỏ, hãy sử dụng Dynamic_cast. Nếu bạn thực sự muốn quên về các loại, bạn có thể sử dụng reintinteret_cast. Và để ném const ra khỏi cửa sổ, có const_cast.
Họ chỉ làm cho mã rõ ràng hơn để có vẻ như bạn biết những gì bạn đang làm.
static_cast
có nghĩa là bạn không thể vô tình const_cast
hoặc reinterpret_cast
, đó là một điều tốt.
Xem Giới thiệu C ++ hiệu quả
Đó là về mức độ an toàn mà bạn muốn áp đặt.
Khi bạn viết (bar) foo
(tương đương với reinterpret_cast<bar> foo
nếu bạn chưa cung cấp toán tử chuyển đổi loại), bạn đang nói với trình biên dịch bỏ qua loại an toàn và chỉ cần làm như đã nói.
Khi bạn viết, static_cast<bar> foo
bạn yêu cầu trình biên dịch ít nhất kiểm tra xem chuyển đổi loại có ý nghĩa hay không, và đối với các loại tích phân, để chèn một số mã chuyển đổi.
EDIT 2014 / 02-26
Tôi đã viết câu trả lời này hơn 5 năm trước và tôi đã hiểu sai. (Xem bình luận.) Nhưng nó vẫn được nâng cấp!
static_cast<bar>(foo)
, với dấu ngoặc đơn. Tương tự cho reinterpret_cast<bar>(foo)
.
Diễn viên C Style rất dễ bỏ lỡ trong một khối mã. Diễn viên phong cách C ++ không chỉ thực hành tốt hơn; họ cung cấp một mức độ linh hoạt lớn hơn nhiều.
reinterpret_cast cho phép tích hợp chuyển đổi loại con trỏ, tuy nhiên có thể không an toàn nếu sử dụng sai.
static_cast cung cấp chuyển đổi tốt cho các loại số, ví dụ như từ enums sang ints hoặc ints sang float hoặc bất kỳ loại dữ liệu nào bạn tự tin về loại. Nó không thực hiện bất kỳ kiểm tra thời gian chạy.
mặt khác, Dynamic_cast sẽ thực hiện các kiểm tra này gắn cờ mọi chuyển nhượng hoặc chuyển đổi mơ hồ. Nó chỉ hoạt động trên con trỏ và tài liệu tham khảo và phát sinh chi phí.
Có một vài người khác nhưng đây là những người chính bạn sẽ đi qua.
static_cast, ngoài việc thao tác con trỏ đến các lớp, cũng có thể được sử dụng để thực hiện chuyển đổi được xác định rõ ràng trong các lớp, cũng như để thực hiện chuyển đổi tiêu chuẩn giữa các loại cơ bản:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, mặc dù, khi (int)d
rất ngắn gọn và dễ đọc hơn? (Ý tôi là trong trường hợp của các loại cơ bản, không phải con trỏ đối tượng.)
(int)d
khi int{d}
dễ đọc hơn nhiều? Trình xây dựng, hoặc giống như chức năng nếu bạn có ()
, cú pháp gần như không quá nhanh để chuyển thành một mê cung ác mộng của dấu ngoặc đơn trong các biểu thức phức tạp. Trong trường hợp này, nó sẽ int i{d}
thay vì int i = (int)d
. IMO tốt hơn nhiều. Điều đó nói rằng, khi tôi chỉ cần một biểu thức tạm thời, tôi sử dụng static_cast
và chưa bao giờ sử dụng các hàm tạo của hàm tạo, tôi không nghĩ vậy. Tôi chỉ sử dụng (C)casts
khi vội vàng viết gỡ lỗi cout
...