อ่าน 11 นาที
ไวยากรณ์การจัดวาง
ไวยากรณ์การจัดวาง (Placement syntax) ใน ภาษาโปรแกรม C++ หมายถึง ไวยากรณ์ สำหรับการระบุ การจัดการหน่วยความจำ ของแต่ละอ็อบเจ็กต์อย่างชัดเจน (เช่น "ตำแหน่ง" ของอ็อบเจ็กต์ใน...
ไวยากรณ์การจัดวาง
| ไลบรารีมาตรฐาน C++ |
|---|
| คอนเทนเนอร์ |
| ไลบรารีมาตรฐาน C |
ไวยากรณ์การจัดวาง (Placement syntax)ใน ภาษาโปรแกรม C++หมายถึงไวยากรณ์สำหรับการระบุการจัดการหน่วยความจำของแต่ละอ็อบเจ็กต์อย่างชัดเจน (เช่น "ตำแหน่ง" ของอ็อบเจ็กต์ในหน่วยความจำ ) โดยปกติแล้ว เมื่อสร้างอ็อบเจ็กต์แบบไดนามิก ฟังก์ชันการจัดสรรจะถูกเรียกใช้ในลักษณะที่จะจัดสรรหน่วยความจำให้กับอ็อบเจ็กต์และเริ่มต้นใช้งานอ็อบเจ็กต์ภายในหน่วยความจำที่จัดสรรใหม่นั้น ไวยากรณ์การจัดวางช่วยให้นักเขียนโปรแกรมสามารถใส่พารามิเตอร์เพิ่มเติมให้กับฟังก์ชันการจัดสรรได้ การใช้งานทั่วไปคือการใส่พอยเตอร์ไปยังพื้นที่จัดเก็บที่เหมาะสมซึ่งสามารถเริ่มต้นใช้งานอ็อบเจ็กต์ได้ ซึ่งเป็นการแยกการจัดสรรหน่วยความจำออกจากการสร้างอ็อบเจ็กต์
เวอร์ชัน "การจัดวาง" ของ ตัวดำเนินการ newและdeleteฟังก์ชันเรียกว่าการจัดวางnewและการจัดวางdelete[ 1 ] นิพจน์ไม่newว่า จะเป็นการ จัดวางหรือไม่ก็ตาม จะเรียกnewฟังก์ชันซึ่งเรียกอีกอย่างว่าฟังก์ชันจัดสรร ซึ่งมีชื่อว่าoperator newในทำนองเดียวกันdeleteนิพจน์จะเรียกdeleteฟังก์ชันซึ่งเรียกอีกอย่างว่าฟังก์ชันยกเลิกการจัดสรร ซึ่งมีoperator deleteชื่อว่า[ 2 ] [ 3 ]
นิพจน์ ใดๆnewที่ใช้ไวยากรณ์การจัดวางถือเป็นnewนิพจน์การจัดวาง และฟังก์ชันใดๆoperator newที่operator deleteรับพารามิเตอร์มากกว่าพารามิเตอร์แรกที่จำเป็น ( size_t) ถือเป็นการจัดวางnewหรือdeleteฟังก์ชัน การจัดวาง [ 4 ] ฟังก์ชัน การจัดวางnewรับพารามิเตอร์อินพุตสองตัวได้แก่ size_tและvoid*
ประวัติศาสตร์
ใน C++ เวอร์ชันก่อนหน้า ไม่มีสิ่งที่เรียกว่าplacement newแต่ผู้พัฒนาใช้การกำหนดค่าแบบชัดเจนthisภายในคอนสตรัคเตอร์เพื่อให้ได้ผลลัพธ์ที่คล้ายกัน[ 5 ] แนวปฏิบัตินี้ล้าสมัยและถูกลบออกไปใน C++98 และหนังสือ The C++ Programming Languageฉบับที่สามของ Stroustrup ก็ไม่ได้กล่าวถึงเทคนิคนี้
การแสดงออก
ไวยากรณ์ C++ สำหรับnewนิพจน์ที่ไม่ใช่การจัดวางคือ[ 2 ]
new new-type-id ( optional-initializer-expression-list )
ไวยากรณ์การจัดวางจะเพิ่มรายการนิพจน์ทันทีหลังnewคำหลัก รายการนิพจน์นี้คือการจัดวาง ซึ่งสามารถมีนิพจน์ได้หลายรายการ[ 2 ] [ 3 ] [ 6 ]
new ( expression-list ) new-type-id ( optional-initializer-expression-list )
ฟังก์ชัน
ฟังก์ชัน การจัดวางnewเป็นการโอเวอร์โหลดของฟังก์ชันที่ไม่ใช่การจัดวาง การnewประกาศฟังก์ชันที่ไม่ใช่การจัดวาง สำหรับ นิพจน์newที่ไม่ใช่อาร์เรย์และอาร์เรย์ ตามลำดับ มีดังนี้: [ 7 ] [ 8 ]new
void * operator new ( size_t n ); void * operator new []( size_t n );ไลบรารีมาตรฐาน C++มีฟังก์ชันโอเวอร์โหลดสองตำแหน่งสำหรับฟังก์ชันเหล่านี้ การประกาศมีดังนี้: [ 7 ] [ 8 ]
void * operator new ( size_t n , const nothrow_t & t ) noexcept ; void * operator new ( size_t n , void * p ) noexcept ; void * operator new []( size_t n , const nothrow_t & t ) noexcept ; void * operator new []( size_t n , void * p ) noexcept ;ในการโอเวอร์โหลดทั้งหมด พารามิเตอร์แรกของoperator newฟังก์ชันจะเป็นประเภทsize_tซึ่งเมื่อเรียกใช้ฟังก์ชัน จะถูกส่งผ่านเป็นอาร์กิวเมนต์ที่ระบุจำนวนหน่วยความจำเป็นไบต์ที่จะจัดสรร ฟังก์ชันทั้งหมดต้องส่งคืนประเภทvoid*ซึ่งเป็นตัวชี้ไปยังพื้นที่จัดเก็บที่ฟังก์ชันจัดสรร[ 2 ]
นอกจากนี้ยังมีdeleteฟังก์ชันการจัดวาง ซึ่งเป็นเวอร์ชันโอเวอร์โหลดของฟังก์ชันที่ไม่ใช่การจัดวาง ฟังก์ชัน deleteที่ไม่ใช่การจัดวางdeleteจะถูกประกาศดังนี้: [ 7 ] [ 8 ]
void operator delete ( void * p ) noexcept ; void operator delete []( void * p ) noexcept ;ไลบรารีมาตรฐานมีโอเวอร์โหลดการจัดวางสองรายการสำหรับฟังก์ชันเหล่านี้ การประกาศมีดังนี้: [ 7 ] [ 8 ]
void operator delete ( void * p , const nothrow_t & t ) noexcept ; void operator delete ( void * p , void * q ) noexcept ; void operator delete []( void * p , const nothrow_t & t ) noexcept ; void operator delete []( void * p , void * q ) noexcept ;ในการโอเวอร์โหลดทั้งหมด พารามิเตอร์แรกของoperator deleteฟังก์ชันจะเป็นประเภทvoid*ซึ่งเป็นที่อยู่ของพื้นที่จัดเก็บที่จะยกเลิกการจัดสรร[ 2 ]
ตัวดำเนินการnewและdeleteต้องอยู่ในเนมสเปซ ส่วนกลาง และไม่สามารถมีการเชื่อมโยงแบบคงที่ได้[ 2 ]
ใช้
ไวยากรณ์การจัดวางมีประโยชน์หลักสี่ประการ ได้แก่ การจัดวางตามค่าเริ่มต้น การป้องกันข้อยกเว้น ตัวจัดสรรหน่วยความจำแบบกำหนดเองและการ ดีบัก
ตำแหน่งเริ่มต้น
โอเวอร์โหลดการจัดวางของoperator newและoperator deleteที่ใช้พารามิเตอร์เพิ่มเติมvoid*จะถูกใช้สำหรับการจัดวางเริ่มต้น หรือที่เรียกว่าการจัดวางตัวชี้ คำจำกัดความของไลบรารีมาตรฐาน ซึ่งโปรแกรม C++ ไม่อนุญาตให้แทนที่หรือเขียนทับ มีดังนี้: [ 7 ] [ 8 ] [ 9 ]
void * operator new ( size_t n , void * p ) noexcept { return p ; }void * operator new []( size_t n , void * p ) noexcept { return p ; }void operator delete ( void * p , void * q ) noexcept {} void operator delete []( void * p , void * q ) noexcept {}การกำหนดค่าเริ่มต้นมีประโยชน์หลายอย่าง
Bjarne Stroustrupได้สังเกตไว้ในหนังสือThe Design and Evolution of C++ ของเขา ว่า การจัดวางตัวชี้newเป็นสิ่งจำเป็นสำหรับฮาร์ดแวร์ที่คาดหวังวัตถุบางอย่างที่ที่อยู่ฮาร์ดแวร์เฉพาะ นอกจากนี้ยังจำเป็นสำหรับการสร้างวัตถุที่ต้องอยู่ในพื้นที่หน่วยความจำเฉพาะ เช่น พื้นที่ที่ใช้ร่วมกันระหว่างโปรเซสเซอร์หลายตัวของคอมพิวเตอร์มัลติโปรเซสเซอร์[ 10 ]
อย่างไรก็ตาม การใช้งานอื่นๆ รวมถึงการเรียกคอนสตรัคเตอร์โดยตรง ซึ่งเป็นสิ่งที่ภาษา C++ ไม่อนุญาต[ 3 ]
ภาษา C++ อนุญาตให้โปรแกรมเรียกตัวทำลายโดยตรง และเนื่องจากไม่สามารถทำลายวัตถุโดยใช้ การแสดงออกได้ ดังนั้นจึงใช้วิธีนี้ในการทำลายวัตถุ ที่deleteสร้างขึ้นผ่านnewการแสดงออกการวางตำแหน่งตัวชี้ เช่น[ 11 ] [ 12 ]p->~X();
กรณีศึกษา
การกำหนดตำแหน่ง (Placement) newใช้เพื่อป้องกันnewไม่ให้ผู้ใช้งานจัดสรรหน่วยความจำ (เช่น ในกรณีที่หน่วยความจำถูกจัดสรรไว้ล่วงหน้าแล้ว และผู้ใช้งานต้องการวางวัตถุลงในตำแหน่งนั้น) แต่ยังคงสามารถสร้างวัตถุได้ ตัวอย่างสถานการณ์ทั่วไปที่อาจจำเป็นต้องใช้การกำหนดตำแหน่งนี้ ได้แก่:
- เพื่อสร้างอ็อบเจ็กต์ในหน่วยความจำที่ใช้ร่วมกันระหว่างสองกระบวนการที่แตกต่างกัน
- เพื่อให้แน่ใจว่าวัตถุถูกสร้างขึ้นในหน่วยความจำที่ไม่สามารถแบ่งหน้าได้
- เพื่อแยกการจัดสรรหน่วยความจำออกจากการสร้าง เช่น ในการใช้งาน
std::vector<T>(ดูreserve()วิธีการ)
คอนสตรัคเตอร์นั้นแตกต่างจากฟังก์ชันอื่นๆ ตรงที่มันจะถูกเรียกเมื่อไม่มีอ็อบเจ็กต์ (มีเพียงหน่วยความจำ) และเมื่อการทำงานเสร็จสิ้น หน่วยความจำนั้นก็จะกลายเป็นอ็อบเจ็กต์ที่ได้รับการเริ่มต้นใช้งานอย่างสมบูรณ์แล้ว ดังนั้นจึงไม่สามารถเรียกคอนสตรัคเตอร์กับอ็อบเจ็กต์ได้โดยตรง แต่ต้องเข้าถึง (และเริ่มต้นใช้งาน) สมาชิกที่ไม่ใช่แบบสแตติก ซึ่งทำให้การเรียกคอนสตรัคเตอร์โดยตรงเป็นข้อผิดพลาด วิธีแก้คือการใช้ตัวดำเนินการในรูปแบบการจัดวาง (placement form new)
ตัวดำเนินการนี้ถูกนำไปใช้ดังนี้:
void * operator new ( size_t count , void * here ) noexcept { return here ; }void * operator new []( size_t count , void * here ) noexcept { return here ; }การป้องกันข้อยกเว้น
โดยปกติ ฟังก์ชัน (ที่ไม่ใช่การจัดวาง) newจะโยนข้อยกเว้นประเภทstd::bad_allocหากพบข้อผิดพลาด เช่น หน่วยความจำที่มีอยู่หมดลง นี่ไม่ใช่ลักษณะที่ฟังก์ชันถูกกำหนดไว้ในคู่มืออ้างอิง C++ ที่มีคำอธิบายประกอบ ของ Stroustrup แต่เป็นการเปลี่ยนแปลงที่คณะกรรมการมาตรฐานทำขึ้นเมื่อภาษา C++ ได้รับการกำหนดมาตรฐาน พฤติกรรมดั้งเดิมของฟังก์ชัน ซึ่งก็คือการส่งคืนพอยเตอร์ว่างเมื่อเกิดข้อผิดพลาด สามารถเข้าถึงได้ผ่านไวยากรณ์การจัดวาง[ 3 ] [ 4 ] [ 6 ]
การใช้งานนี้ต้องใช้เฮดเดอร์<new>จากไลบรารีมาตรฐาน (หรือเทียบเท่ากับโมดูลstd ) เฮดเดอร์นี้ประกาศออบstd::nothrowเจ็กต์ส่วนกลางซึ่งมีประเภทstd::nothrow_t(ประกาศไว้ในเฮดเดอร์เช่นกัน) ซึ่งใช้ในการเรียกnewฟังก์ชันโอเวอร์โหลดที่ประกาศไว้โดยรับconst std::nothrow_t&เป็นพารามิเตอร์ตัวที่สอง ตัวอย่างเช่น: [ 9 ]
import std ;การใช้งานstd :: nothrow ;struct X { // ... };int main () { // เรียกฟังก์ชัน operator new(size_t, const nothrow_t&) และ (ถ้าสำเร็จ) สร้างอ็อบเจ็กต์X * p = new ( nothrow ) X ; if ( p ) { // พื้นที่จัดเก็บได้รับการจัดสรรและเรียกใช้คอนสตรัคเตอร์แล้วdelete p ; } else { // เกิดข้อผิดพลาด ไม่มีการจัดสรรพื้นที่จัดเก็บและไม่มีการสร้างอ็อบเจ็กต์} return 0 ; }ตัวจัดสรรแบบกำหนดเอง
ไวยากรณ์การจัดวางยังถูกนำมาใช้สำหรับตัวจัดสรร แบบกำหนดเองด้วย โดยไม่ใช้ฟังก์ชันจัดสรรและยกเลิกการจัดสรรใดๆ จากส่วนหัวของไลบรารีมาตรฐาน<new>แต่กำหนดให้โปรแกรมเมอร์ต้องเขียนฟังก์ชันจัดสรรและยกเลิกการจัดสรรของตนเอง โดยโอเวอร์โหลดสำหรับประเภทที่ผู้ใช้กำหนด ตัวอย่างเช่น สามารถกำหนด คลาสตัว จัดสรรอารีน่า ได้ ดังนี้: [ 7 ] [ 8 ]
import std ;คลาสArena { สาธารณะ: // ...void * allocate ( size_t n ) { // เมธอดการจัดสรร... }void deallocate ( void * p ) { // เมธอด deallocate ... } };โปรดทราบว่าการจัดสรรอารีน่ามีให้ในไลบรารีมาตรฐาน C++ โดยstd::pmr::monotonic_buffer_resourceคลาส[ 13 ]
ในทำนองเดียวกัน ให้กำหนดฟังก์ชันการจัดสรรและการยกเลิกการจัดสรรตำแหน่งแบบกำหนดเองดังต่อไปนี้: [ 7 ] [ 8 ]
void * operator new ( size_t size , Arena & arena ) { return arena . allocate ( size ); }void operator delete ( void * p , Arena & arena ) { arena . deallocate ( p ); }โปรแกรมจะใช้ไวยากรณ์การจัดวางเพื่อจัดสรรวัตถุโดยใช้อินสแตนซ์ที่แตกต่างกันของArenaคลาสดังต่อไปนี้: [ 7 ] [ 8 ]
สนามแข่งarena1 ; สนามแข่งarena2 ; X * p1 = X ใหม่( arena1 ) ; X * p2 = X ใหม่( arena2 ) ;การทำลายวัตถุที่มีการจัดสรรพื้นที่จัดเก็บในลักษณะดังกล่าวต้องใช้ความระมัดระวัง เนื่องจากไม่มีdeleteนิพจน์การจัดวาง จึงไม่สามารถใช้นิพจน์นั้นเพื่อเรียกใช้ตัวยกเลิกการจัดสรรแบบกำหนดเองได้ ต้องเขียนฟังก์ชันการทำลายที่เรียกใช้ตัวยกเลิกการจัดสรรแบบกำหนดเอง หรือเรียกใช้deleteฟังก์ชันการจัดวางโดยตรงในรูปแบบการเรียกใช้ฟังก์ชัน[ 11 ] [ 7 ] [ 8 ]
แบบแรกจะมีลักษณะดังนี้: [ 8 ]
void destroy ( X * p , Arena & arena ) { p ->~ X (); // เรียก destructor อย่างชัดเจนก่อนarena . deallocate ( p ); // จากนั้นเรียกฟังก์ชัน deallocator โดยตรง}ซึ่งจะถูกเรียกใช้จากโปรแกรมดังนี้:
อารีน่า อารีน่า; X * p = new ( อารีน่า) X ; // ... destroy ( p , arena );วิธีหลังจะเกี่ยวข้องกับการเขียนการเรียกใช้ตัวทำลายและการเรียกใช้ฟังก์ชันลบลงในโปรแกรมเท่านั้น: [ 7 ] [ 14 ]
Arena arena ; X * p = new ( arena ) X ; // ... p ->~ X (); // เรียกใช้ destructor อย่างชัดเจนก่อนoperator delete ( p , arena ); // จากนั้นเรียกฟังก์ชัน deallocator โดยอ้อมผ่าน operator delete(void*, Arena&).ข้อผิดพลาดทั่วไปคือการพยายามใช้deleteนิพจน์กับdeleteวัตถุ ซึ่งส่งผลให้มีoperator deleteการเรียกใช้ฟังก์ชันที่ไม่ถูกต้อง Dewhurst แนะนำกลยุทธ์สองประการเพื่อหลีกเลี่ยงข้อผิดพลาดนี้ ประการแรกคือการตรวจสอบให้แน่ใจว่าตัวจัดสรรแบบกำหนดเองใดๆ อาศัยไลบรารีมาตรฐานแบบทั่วโลกที่ไม่ใช่แบบจัดวางoperator newและดังนั้นจึงเป็นเพียงตัวห่อหุ้มง่ายๆ รอบการจัดการหน่วยความจำของไลบรารี C++ ประการที่สองคือการสร้างnewและdeleteฟังก์ชันสำหรับแต่ละคลาส และปรับแต่งการจัดการหน่วยความจำผ่านสมาชิกฟังก์ชันของคลาสแทนที่จะใช้ไวยากรณ์การจัดวาง[ 14 ]
การดีบัก
การจัดวางตำแหน่งnewยังสามารถใช้เป็นเครื่องมือดีบักแบบง่ายๆ เพื่อให้โปรแกรมสามารถพิมพ์ชื่อไฟล์และหมายเลขบรรทัดของซอร์สโค้ดที่การจัดสรรหน่วยความจำล้มเหลวได้ วิธีนี้ไม่จำเป็นต้องรวมส่วนหัวของไลบรารีมาตรฐาน<new>แต่ต้องรวมส่วนหัวที่ประกาศฟังก์ชันการจัดวางตำแหน่งสี่ฟังก์ชันและมาโครที่ใช้แทนnewคำหลักที่ใช้ในnewนิพจน์ ตัวอย่างเช่น ส่วนหัวดังกล่าวจะมี: [ 9 ] [ 15 ]
#if defined(DEBUG_NEW) void * operator new ( size_t size , const char * file , int line ); void * operator new []( size_t size , const char * file , int line ); void operator delete ( void * p , const char * file , int line ); void operator delete []( void * p , const char * file , int line ); #define New new(__FILE__, __LINE__) #else #define New new #endifสิ่งนี้จะถูกนำไปใช้ในโปรแกรมดังต่อไปนี้: [ 9 ] [ 15 ]
X * p = X ใหม่;ฟังก์ชัน การจัดวางที่เขียนขึ้นเองnewจะจัดการโดยใช้ข้อมูลไฟล์และหมายเลขบรรทัดที่ให้มาในกรณีที่เกิดข้อยกเว้น ตัวอย่างเช่น: [ 9 ] [ 15 ]
import std ;using std :: bad_alloc ; using std :: nothrow ;class FailedAllocationException : public bad_alloc { private : const char * file ; int line ; public : FailedAllocationException ( const char * file , int line ) : bad_alloc (), file { file }, line { line } {} };void * operator new ( size_t size , const char * file , int line ) { if ( void * p = :: operator new ( size , nothrow )) { return p ; } throw FailedAllocationException ( file , line ); }ตั้งแต่C++26new เป็นต้นมา สามารถใช้placement ใน บริบท constexpr ได้
import std ;template < typename T > class Box { private : alignas ( T ) unsigned char storage [ sizeof ( T )]; T * ptr = nullptr ;constexpr void destroy () { if ( active ) { std :: destroy_at ( ptr ); // เรียก destructor ด้วยตนเองptr = nullptr ; } } public : constexpr Box () = default ;constexpr ~ Box () { destroy (); }template < typename ... Args > constexpr T & emplace ( Args && ... args ) { destroy (); T * p = new ( static_cast < void *> ( storage )) T ( std :: forward < Args > ( args )...); return * ptr ; }constexpr T & get () { return * ptr ; } };ลบตำแหน่ง
ดังที่กล่าวไว้ข้างต้น ไม่มีdeleteนิพจน์การจัดวาง ไม่สามารถเรียกใช้ฟังก์ชันการจัดวางใดๆ โดยใช้ นิพจน์ได้[ 11 ] [ 16 ]operator deletedelete
ฟังก์ชัน การจัดวางdeleteจะถูกเรียกจากnewนิพจน์การจัดวาง โดยเฉพาะอย่างยิ่ง จะถูกเรียกหากตัวสร้างของวัตถุเกิดข้อยกเว้น ในกรณีเช่นนี้ เพื่อให้แน่ใจว่าโปรแกรมจะไม่เกิดการรั่วไหลของหน่วยความจำฟังก์ชันการลบการจัดวางจะถูกเรียกnewนิพจน์การจัดวางจะเรียกฟังก์ชันการจัดวางก่อนoperator newจากนั้นจึงเรียกตัวสร้างของวัตถุบนพื้นที่จัดเก็บดิบที่ส่งคืนจากฟังก์ชันการจัดสรร หากตัวสร้างเกิดข้อยกเว้น จำเป็นต้องยกเลิกการจัดสรรพื้นที่จัดเก็บนั้นก่อนที่จะส่งต่อข้อยกเว้นกลับไปยังโค้ดที่ดำเนินการnewนิพจน์การจัดวาง และนั่นคือจุดประสงค์ของdeleteฟังก์ชัน การจัดวาง [ 2 ] [ 4 ] [ 11 ] [ 16 ]
ฟังก์ชันการลบตำแหน่งที่ถูกเรียกจะตรงกับnewฟังก์ชันตำแหน่งที่ถูกเรียกใช้โดยนิพจน์ตำแหน่งnewดังนั้น ตัวอย่างเช่น หากโค้ดต่อไปนี้ถูกดำเนินการdeleteฟังก์ชันตำแหน่งที่ถูกเรียกจะเป็นoperator delete(void*, const Arena&): [ 2 ] [ 11 ] [ 16 ]
import std ;การใช้งานstd :: bad_alloc ;คลาสArena { // ... };คลาสX { public : X () { throw bad_alloc ( "การจัดสรรหน่วยความจำไม่ถูกต้อง" ); } };void * operator new ( size_t n , const Arena & a ) { std :: println ( "เรียกใช้ Placement new" ); }void operator delete ( void * p , const Arena & a ) { std :: println ( "Placement delete called." ); }int main () { Arena arena ; try { X * p = new ( arena ) X ; } catch ( const bad_alloc & e ) { std :: println ( "เกิดข้อยกเว้น: {}" , e.what ( ) ); } return 0 ; }นี่คือเหตุผลที่ ฟังก์ชัน การวางตำแหน่งตัวชี้deleteถูกกำหนดให้เป็นการดำเนินการที่ไม่ต้องทำอะไรเลยโดยไลบรารีมาตรฐาน เนื่องจากnewฟังก์ชันการวางตำแหน่งตัวชี้ไม่ได้จัดสรรพื้นที่จัดเก็บใดๆ จึงไม่มีพื้นที่จัดเก็บให้[ 17 ]ถูกยกเลิกการจัดสรรในกรณีที่ตัวสร้างวัตถุโยนข้อยกเว้น[ 11 ]
หากไม่มีฟังก์ชันลบตำแหน่งที่ตรงกันอยู่ จะไม่มีการเรียกฟังก์ชันยกเลิกการจัดสรรในกรณีที่มีการโยนข้อยกเว้นโดยคอนสตรัคเตอร์ภายในnewนิพจน์ตำแหน่ง นอกจากนี้ยังมีการใช้งาน C++ บางส่วน (รุ่นเก่า) ที่ไม่รองรับตำแหน่งเลยdelete(ซึ่งเช่นเดียวกับฟังก์ชันจัดสรรที่โยนข้อยกเว้น เป็นส่วนเพิ่มเติมที่ทำขึ้นใน C++ เมื่อมีการกำหนดมาตรฐาน) ในทั้งสองสถานการณ์ดังกล่าว การโยนข้อยกเว้นโดยคอนสตรัคเตอร์เมื่อจัดสรรโดยใช้ตัวจัดสรรแบบกำหนดเองจะส่งผลให้เกิดการรั่วไหลของหน่วยความจำ (ในกรณีของการใช้งาน C++ รุ่นเก่า การรั่วไหลของหน่วยความจำจะเกิดขึ้นกับ นิพจน์ ที่ไม่ใช่ตำแหน่ง ด้วย new) [ 4 ] [ 16 ]
ความปลอดภัย
นิพจน์ การจัดวางnewมีความเสี่ยงต่อการโจมตีด้านความปลอดภัย ในปี 2554 Kundu และ Bertino [ 17 ]ได้สาธิตการโจมตีบางอย่างเกี่ยวกับการจัดวาง การnewโจมตีบางอย่างได้แก่ การโจมตี แบบบัฟเฟอร์โอเวอร์โฟลว์, ออบเจ็กต์โอเวอร์โฟลว์, การเขียนทับสแต็กการ์ดแบบเลือก, การหลอกลวงพอยเตอร์เสมือน, การโจมตีการจัดเรียงหน่วยความจำที่ไม่ถูกต้อง ในปี 2558 GCC ได้ออกแพทช์[ 18 ]โดยอิงจากผลการค้นพบใน[ 17 ]
หมายเหตุ
- ^แมคคลัสกีย์ 2000
- ^ a b c d e f g h Lischner 2003 , หน้า 72–73, 128–129, 310, 623–625
- ^ a b c d Lippman 1997 , หน้า 386–389
- ^ a b c dไมเยอร์ส 1998
- ^สตรูสตรัป 1991
- ^ a b Loudon 2003 , หน้า 109–110
- ↑ a b c d e f g h i j Vermeir 2001 , หน้า 113–115
- ^ a b c d e f g h i j Stroustrup 1997 , หน้า 255–256, 576
- ^ a b c d e Anderson 1998a , หน้า 345–356
- ^ Stroustrup 1994 , หน้า 214
- ↑ a b c d e f Solter & Kleper 2005 , หน้า 458–461
- ^ Seed & Cooper 2001 , หน้า 435–436
- ^ cppreference.com (7 มีนาคม 2026). "std::pmr::monotonic_buffer_resource" . cppreference.com . cppreference.com.
- ^ a b Dewhurst 2003 , หน้า 173–176
- ↑ เอบีซีหย่งเว่ย 2007
- ^ a b c d Anderson 1998b , หน้า 631–632
- ^ a b c Kundu, Ashish; Bertino, Elisa (มิถุนายน 2011). "การโจมตีแบบ Buffer Overflow รูปแบบใหม่". การประชุมวิชาการนานาชาติว่าด้วยระบบคอมพิวเตอร์แบบกระจาย ครั้งที่ 31 ประจำปี 2011.หน้า 730–739 . doi : 10.1109/ICDCS.2011.63 . ISBN 978-1-61284-384-1S2CID 8583476 –ผ่านทาง IEEE
- ^ "Martin Sebor - [PING] [PATCH] c++/67942 - วินิจฉัยปัญหาบัฟเฟอร์ล้นตำแหน่งใหม่" . gcc.gnu.org . สืบค้นเมื่อ2020-06-15 .
อ่านเพิ่มเติม
- Franek, Frantisek (2004). หน่วยความจำในฐานะแนวคิดการเขียนโปรแกรมในภาษา C และ C++สำนักพิมพ์มหาวิทยาลัยเคมบริดจ์ISBN 978-0-521-52043-0.
- Cline, Marshall (25 กันยายน 2006). "11.10: "placement new" คืออะไร และทำไมฉันถึงต้องใช้มัน?" . C++ FAQ Lite . สืบค้นเมื่อ26 พฤศจิกายน 2008 .
- "ตัวดำเนินการ `new` ใน C++"คอม ไพเลอ ร์Mac OS X ของ IBM IBM 2003 สืบค้นเมื่อ27 พฤศจิกายน 2008
- "ฟังก์ชันตัวดำเนินการใหม่" . MSDN . Microsoft . สืบค้นเมื่อ27 พฤศจิกายน 2008 .
- "ตัวดำเนินการใหม่ (C++)" . MSDN . Microsoft . สืบค้นเมื่อ27 พฤศจิกายน 2008 .
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ ไวยากรณ์การจัดวาง
ไวยากรณ์การจัดวาง (Placement syntax) ใน ภาษาโปรแกรม C++ หมายถึง ไวยากรณ์ สำหรับการระบุ การจัดการหน่วยความจำ ของแต่ละอ็อบเจ็กต์อย่างชัดเจน (เช่น "ตำแหน่ง" ของอ็อบเจ็กต์ใน...
ประวัติศาสตร์
ใน C++ เวอร์ชันก่อนหน้า ไม่มีสิ่งที่เรียกว่า placement new แต่ผู้พัฒนาใช้การกำหนดค่าแบบชัดเจน this ภายในคอนสตรัคเตอร์เพื่อให้ได้ผลลัพธ์ที่คล้ายกัน [ 5 ] แนวปฏิบัตินี้ล้าสมัยและถูกลบออกไปใน C++98 และหนังสือ The C++ Programming Language ฉบับที่สามของ Stroustrup...
การแสดงออก
ไวยากรณ์ C++ สำหรับ new นิพจน์ที่ไม่ใช่การจัดวางคือ [ 2 ]
ฟังก์ชัน
ฟังก์ชัน การจัดวาง new เป็นการ โอเวอร์โหลด ของฟังก์ชันที่ไม่ใช่การจัดวาง การ new ประกาศฟังก์ชันที่ไม่ใช่การจัดวาง สำหรับ นิพจน์ new ที่ไม่ใช่อาร์เรย์และอาร์เรย์ ตามลำดับ มีดังนี้: [ 7 ] [ 8 ] new