ฉันเคยได้ยินว่าstatic_cast
ควรใช้ฟังก์ชั่น C-style หรือ Simple-style casting มันเป็นเรื่องจริงเหรอ? ทำไม?
ฉันเคยได้ยินว่าstatic_cast
ควรใช้ฟังก์ชั่น C-style หรือ Simple-style casting มันเป็นเรื่องจริงเหรอ? ทำไม?
คำตอบ:
เหตุผลหลักคือการที่ปลดเปลื้อง C คลาสสิกทำให้ความแตกต่างระหว่างสิ่งที่เราเรียกว่าไม่มีstatic_cast<>()
, reinterpret_cast<>()
, และconst_cast<>()
dynamic_cast<>()
สี่สิ่งนี้แตกต่างอย่างสิ้นเชิง
static_cast<>()
มักจะปลอดภัย มีการแปลงที่ถูกต้องในภาษาหรือตัวสร้างที่เหมาะสมที่ทำให้มันเป็นไปได้ ครั้งเดียวที่มันมีความเสี่ยงเล็กน้อยคือเมื่อคุณทิ้งคลาสที่สืบทอด คุณต้องตรวจสอบให้แน่ใจว่าวัตถุนั้นแท้จริงแล้วเป็นลูกหลานที่คุณอ้างว่าเป็นวัตถุภายนอกโดยใช้ภาษา (เช่นการตั้งค่าสถานะในวัตถุ) A dynamic_cast<>()
มีความปลอดภัยตราบใดที่มีการตรวจสอบผลลัพธ์ (ตัวชี้) หรือข้อยกเว้นที่เป็นไปได้จะถูกนำมาพิจารณา (การอ้างอิง)
ในทางกลับกันreinterpret_cast<>()
(หรือ a const_cast<>()
) เป็นอันตรายเสมอ คุณบอกคอมไพเลอร์: "เชื่อฉัน: ฉันรู้ว่านี่มันไม่เหมือนfoo
(นี่ดูราวกับว่ามันไม่แน่นอน) แต่มันเป็น"
ปัญหาแรกคือแทบจะเป็นไปไม่ได้เลยที่จะบอกว่าจะเกิดเหตุการณ์ใดในนักแสดงสไตล์ C โดยไม่ต้องดูโค้ดที่มีขนาดใหญ่และแยกย้ายกันและรู้กฎทั้งหมด
สมมติว่าสิ่งเหล่านี้:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
ตอนนี้ทั้งสองนี้จะถูกรวบรวมในลักษณะเดียวกัน:
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
อย่างไรก็ตามเราจะเห็นโค้ดที่เหมือนกันเกือบทั้งหมด:
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!!!
อย่างที่คุณเห็นไม่มีวิธีง่ายๆในการแยกความแตกต่างระหว่างสองสถานการณ์โดยไม่ต้องรู้มากเกี่ยวกับชั้นเรียนทั้งหมดที่เกี่ยวข้อง
ปัญหาที่สองคือรูปแบบ C นั้นยากที่จะค้นหา ในการแสดงออกที่ซับซ้อนอาจเป็นเรื่องยากมากที่จะเห็นรูปแบบ C แทบจะเป็นไปไม่ได้ที่จะเขียนเครื่องมืออัตโนมัติที่จำเป็นต้องค้นหาตำแหน่ง C-style (ตัวอย่างเช่นเครื่องมือค้นหา) โดยไม่ต้องคอมไพเลอร์ C ++ เต็มหน้า ในทางกลับกันการค้นหา "static_cast <" หรือ "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.
นั่นหมายความว่าไม่เพียง แต่จะเป็นสไตล์ C เท่านั้นที่มีอันตรายมากกว่า แต่มันยากที่จะหาพวกเขาทั้งหมดเพื่อให้แน่ใจว่าถูกต้อง
static_cast
สำหรับการหล่อลงลำดับชั้นมรดก dynamic_cast
แต่ ที่จะส่งกลับทั้งตัวชี้โมฆะหรือตัวชี้ที่ถูกต้อง
static_cast
ในสถานการณ์นั้น dynamic_cast
อาจปลอดภัยกว่า แต่ก็ไม่ใช่ตัวเลือกที่ดีที่สุดเสมอไป บางครั้งคุณก็รู้ว่าตัวชี้ชี้ไปที่ประเภทย่อยที่กำหนดโดยวิธีทึบแสงให้กับคอมไพเลอร์และ a static_cast
เร็วขึ้น อย่างน้อยในบางสภาพแวดล้อมdynamic_cast
จำเป็นต้องมีการสนับสนุนคอมไพเลอร์เพิ่มเติมและค่าใช้จ่ายรันไทม์ (เปิดใช้งาน RTTI) และคุณอาจไม่ต้องการเปิดใช้งานเพียงตรวจสอบสองสามอย่างที่คุณสามารถทำได้ด้วยตัวเอง RTTI ของ C ++ เป็นวิธีแก้ปัญหาที่เป็นไปได้เพียงวิธีเดียวเท่านั้น
static_cast
++ เทียบเท่าซีreinterpret_cast
เป็น*(destination_type *)&
คือการอยู่ของวัตถุที่หล่ออยู่ที่ชี้ไปยังประเภทที่แตกต่างกันแล้ว dereferencing ยกเว้นในกรณีของชนิดอักขระหรือชนิดของโครงสร้างที่ C กำหนดพฤติกรรมของโครงสร้างนี้โดยทั่วไปแล้วจะส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดใน C.
int
(และint
เพียงอย่างเดียว) เหตุใดจึงใช้static_cast<int>
กับ(int)
เป็นประโยชน์อย่างเดียวที่ดูเหมือนจะอยู่กับตัวแปรคลาสและพอยน์เตอร์ ขอให้คุณทำอย่างละเอียดเกี่ยวกับเรื่องนี้
int
dynamic_cast
ใช้ไม่ได้ แต่เหตุผลอื่น ๆ ทั้งหมดยืน ตัวอย่างเช่นสมมติว่าv
เป็นพารามิเตอร์ที่ฟังก์ชั่นการประกาศให้เป็นfloat
แล้วคือ(int)v
static_cast<int>(v)
แต่ถ้าคุณเปลี่ยนพารามิเตอร์float*
, (int)v
เงียบ ๆ กลายเป็นreinterpret_cast<int>(v)
ในขณะที่static_cast<int>(v)
ผิดกฎหมายและถูกจับได้อย่างถูกต้องโดยคอมไพเลอร์
หนึ่งเคล็ดลับเชิงปฏิบัติ: คุณสามารถค้นหาคำหลัก static_cast ได้อย่างง่ายดายในซอร์สโค้ดของคุณหากคุณวางแผนที่จะทำให้โครงการเป็นระเบียบเรียบร้อย
int
พารามิเตอร์เดียว
ในระยะสั้น :
static_cast<>()
ช่วยให้คุณมีความสามารถในการตรวจสอบเวลาคอมไพล์ C-Style cast ไม่ได้static_cast<>()
สามารถเห็นได้อย่างง่ายดายทุกที่ในรหัสที่มา C ++; ในทางตรงกันข้ามการโยน C_Style นั้นยากที่จะมองเห็น- ความตั้งใจจะถ่ายทอดได้ดีขึ้นโดยใช้ C + + casts
คำอธิบายเพิ่มเติม :
คงโยนแปลงดำเนินการระหว่างประเภทที่เข้ากันได้ มันคล้ายกับตัวละครสไตล์ C แต่มีข้อ จำกัด มากกว่า ยกตัวอย่างเช่นตัวละครสไตล์ C จะอนุญาตให้ตัวชี้จำนวนเต็มชี้ไปที่อักขระ
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
เนื่องจากสิ่งนี้ส่งผลให้ตัวชี้แบบ 4 ไบต์ชี้ไปที่ 1 ไบต์ของหน่วยความจำที่จัดสรรการเขียนไปยังตัวชี้นี้จะทำให้เกิดข้อผิดพลาดขณะทำงานหรือจะเขียนทับหน่วยความจำที่อยู่ติดกัน
*p = 5; // run-time error: stack corruption
ตรงกันข้ามกับตัว C-style คาสต์แบบสแตติกจะอนุญาตให้คอมไพเลอร์ตรวจสอบว่าชนิดข้อมูลของตัวชี้และพอยต์เข้ากันได้ซึ่งช่วยให้โปรแกรมเมอร์จับการกำหนดตัวชี้ที่ไม่ถูกต้องนี้ในระหว่างการรวบรวม
int *q = static_cast<int*>(&c); // compile-time error
อ่านเพิ่มเติมเกี่ยวกับ:
อะไรคือความแตกต่างระหว่าง static_cast <> การหล่อสไตล์ C
และ
โยนปกติเทียบกับ static_cast dynamic_cast
static_cast<>()
อ่านได้มากกว่า ฉันหมายถึงบางครั้งมันเป็น แต่ส่วนใหญ่ - โดยเฉพาะอย่างยิ่งในประเภทจำนวนเต็มขั้นพื้นฐาน - เป็นเพียง verbose อย่างน่ากลัวและไม่จำเป็น ตัวอย่างเช่น: นี่คือฟังก์ชันที่สลับไบต์ของคำ 32 บิต มันแทบจะเป็นไปไม่ได้เลยที่จะอ่านโดยใช้การstatic_cast<uint##>()
ปลดเปลื้อง แต่ค่อนข้างง่ายที่จะเข้าใจการใช้การ(uint##)
ปลดเปลื้อง รูปภาพของรหัส: imgur.com/NoHbGve
always
อย่างใดอย่างหนึ่ง (แต่ส่วนใหญ่ใช่แล้ว) มีบางกรณีที่การโยนสไตล์ c นั้นอ่านได้ง่ายกว่า นั่นคือเหตุผลหนึ่งในการคัดเลือกนักแสดงสไตล์คยังคงมีอยู่และเตะใน c ++ imho :) โดยวิธีที่เป็นตัวอย่างที่ดีมาก
(uint32_t)(uint8_t)
) เพื่อให้บรรลุไบต์ที่นอกเหนือจากต่ำสุดจะถูกรีเซ็ต สำหรับที่มีค่าบิตและ ( 0xFF &
) การใช้งานการปลดเปลื้องทำให้เสียความตั้งใจ
คำถามนั้นใหญ่กว่าเพียงแค่ใช้ wither static_cast หรือการหล่อสไตล์ C เนื่องจากมีสิ่งต่าง ๆ ที่เกิดขึ้นเมื่อใช้การหล่อแบบ C ตัวดำเนินการแคสต์ C ++ นั้นมีจุดประสงค์เพื่อทำให้การดำเนินการเหล่านี้ชัดเจนยิ่งขึ้น
บนพื้นผิว static_cast และ C สไตล์การร่ายปรากฏขึ้นในสิ่งเดียวกันตัวอย่างเช่นเมื่อส่งค่าหนึ่งไปยังอีกค่า:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
ทั้งสองตัวนี้โยนค่าจำนวนเต็มเป็นสองเท่า อย่างไรก็ตามเมื่อทำงานกับสิ่งต่างๆที่มีความซับซ้อนมากขึ้น ตัวอย่างบางส่วน:
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
ในตัวอย่างนี้ (1) อาจตกลงเพราะวัตถุที่ชี้โดย A นั้นเป็นตัวอย่างของ B แต่ถ้าคุณไม่ทราบจุดนั้นในรหัสสิ่งที่ชี้ไปที่จริง (2) อาจถูกกฎหมายอย่างสมบูรณ์ (คุณเพียงต้องการดูหนึ่งไบต์ของจำนวนเต็ม) แต่ก็อาจเป็นข้อผิดพลาดในกรณีที่ข้อผิดพลาดจะดีเช่น (3) ตัวดำเนินการแคสต์ C ++ มีวัตถุประสงค์เพื่อเปิดเผยปัญหาเหล่านี้ในรหัสโดยให้ข้อผิดพลาดในการคอมไพล์หรือเวลาทำงานเมื่อเป็นไปได้
ดังนั้นสำหรับ "การคัดเลือกค่า" อย่างเข้มงวดคุณสามารถใช้ static_cast ถ้าคุณต้องการการชี้ polymorphic แบบชี้ขาดของเวลาทำงานให้ใช้ dynamic_cast หากคุณต้องการลืมประเภทจริงๆคุณสามารถใช้ reintrepret_cast และเพื่อที่จะโยน const ออกไปนอกหน้าต่างนั่นก็คือ const_cast
พวกเขาทำให้โค้ดชัดเจนยิ่งขึ้นเพื่อให้ดูเหมือนว่าคุณรู้ว่าคุณกำลังทำอะไรอยู่
static_cast
หมายความว่าคุณไม่สามารถตั้งใจconst_cast
หรือreinterpret_cast
ซึ่งเป็นสิ่งที่ดี
ดูการแนะนำC ++ ที่มีประสิทธิภาพ
มันเกี่ยวกับความปลอดภัยประเภทที่คุณต้องการกำหนด
เมื่อคุณเขียน(bar) foo
(ซึ่งเทียบเท่ากับreinterpret_cast<bar> foo
ถ้าคุณยังไม่ได้ให้บริการตัวแปลงประเภท) คุณกำลังบอกคอมไพเลอร์ให้เพิกเฉยต่อความปลอดภัยของประเภทและเพียงทำตามที่ได้บอกไว้
เมื่อคุณเขียนstatic_cast<bar> foo
คุณจะขอให้คอมไพเลอร์ตรวจสอบอย่างน้อยว่าการแปลงประเภทมีเหตุผลและสำหรับประเภทที่สำคัญให้แทรกรหัสการแปลงบางส่วน
แก้ไข 2014-02-26
ฉันเขียนคำตอบนี้มานานกว่า 5 ปีแล้วและฉันเข้าใจผิด (ดูความคิดเห็น) แต่ก็ยังได้รับ upvotes!
static_cast<bar>(foo)
มีวงเล็บด้วย reinterpret_cast<bar>(foo)
สำหรับเดียวกัน
การลอกแบบ C นั้นง่ายที่จะพลาดในบล็อคของโค้ด การร่ายสไตล์ C ++ ไม่เพียงเป็นการฝึกฝนที่ดีเท่านั้น พวกเขามีความยืดหยุ่นในระดับที่สูงกว่ามาก
reinterpret_cast อนุญาตให้อินทิกรัลสำหรับการแปลงประเภทตัวชี้อย่างไรก็ตามอาจไม่ปลอดภัยหากใช้ในทางที่ผิด
static_cast ให้การแปลงที่ดีสำหรับประเภทที่เป็นตัวเลขเช่นจาก enums เป็น ints หรือ ints ถึง float หรือชนิดข้อมูลใด ๆ ที่คุณมั่นใจในประเภท มันไม่ได้ทำการตรวจสอบเวลาทำงานใด ๆ
dynamic_cast จะทำการตรวจสอบเหล่านี้เพื่อตั้งค่าสถานะการมอบหมายหรือการแปลงที่ไม่ชัดเจน มันใช้งานได้กับพอยน์เตอร์และการอ้างอิงและเกิดค่าใช้จ่าย
มีสองสามคนอื่น ๆ แต่เหล่านี้เป็นคนหลักที่คุณจะเจอ
static_cast นอกเหนือจากการใช้พอยน์เตอร์พอยน์เตอร์ไปยังคลาสแล้วยังสามารถใช้ในการดำเนินการแปลงที่กำหนดไว้อย่างชัดเจนในคลาสรวมทั้งทำการแปลงมาตรฐานระหว่างประเภทพื้นฐาน:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
แม้ว่าเมื่อใด(int)d
จะกระชับและอ่านมากขึ้น? (ฉันหมายถึงในกรณีที่เป็นประเภทพื้นฐานไม่ใช่ตัวชี้วัตถุ)
(int)d
เมื่อint{d}
อ่านมากขึ้น? คอนสตรัคเตอร์หรือฟังก์ชั่นเหมือนถ้าคุณมี()
ไวยากรณ์ไม่เร็วนักที่จะตกอยู่ในวงเล็บของเขาวงกตในฝันร้ายในการแสดงออกที่ซับซ้อน ในกรณีนี้มันจะเป็นแทนint i{d}
int i = (int)d
IMO ที่ดียิ่งกว่า ที่กล่าวว่าเมื่อฉันต้องการชั่วคราวในการแสดงออกฉันใช้static_cast
และไม่เคยใช้ตัวสร้าง casts ฉันไม่คิด ฉันใช้(C)casts
เมื่อเขียนดีบักอย่างรวดเร็วเท่านั้นcout
...