จะปลอดภัยไหมที่จะเรียกตำแหน่งใหม่บน "นี่" สำหรับวัตถุที่ไม่สำคัญ?


20

ฉันรู้ว่าคำถามนี้ถูกถามหลายครั้งแล้ว แต่ฉันไม่สามารถหาคำตอบสำหรับกรณีนี้โดยเฉพาะ

สมมติว่าฉันมีชั้นเรียนที่ไม่สำคัญซึ่งไม่ได้เป็นเจ้าของทรัพยากรใด ๆ และมีตัวทำลายที่ว่างเปล่าและตัวสร้างเริ่มต้น มันมีตัวแปรสมาชิกจำนวนหนึ่งที่มีการเริ่มต้นในคลาส constไม่ได้เป็นหนึ่งในนั้นคือ

ฉันต้องการเริ่มต้นใหม่และวัตถุของชั้นเรียนดังกล่าวโดยไม่ต้องเขียนdeInitวิธีด้วยมือ ปลอดภัยไหมที่จะทำอย่างนี้?

void A::deInit()
{
  new (this)A{};
}

ฉันไม่เห็นปัญหาใด ๆ กับวัตถุ - วัตถุอยู่ในสถานะที่ถูกต้องเสมอthisชี้ไปยังที่อยู่เดียวกัน แต่มันคือ C ++ ดังนั้นฉันต้องการแน่ใจ


2
มีสมาชิกของวัตถุ const หรือไม่?
NathanOliver

2
หากสิ่งนี้ถูกต้องมันจะเทียบเท่า*this = A{};หรือไม่
เควิน

2
@Amomum *this = A{};หมายถึงthis->operator=(A{});คือสร้างวัตถุชั่วคราวและกำหนดให้*thisแทนที่ค่าของข้อมูลสมาชิกทั้งหมดด้วยค่าของชั่วคราว เนื่องจากเป็นสิ่งที่คุณต้องการและเป็น (ในความคิดของฉัน) อ่านง่ายกว่าตำแหน่งใหม่ฉันจะไปกับมันแทน
เควิน

1
@ เควินโอ้ฉันไม่ดีคุณพูดถูก กว่า - ฉันคิดว่ามันควรจะเท่ากันถ้าสำเนาถูกลบออก?
Amomum

1
แทนที่จะอธิบายชั้นเรียนด้วยคำพูดจะดีกว่ามากในการเขียนชั้นเรียนเต็มรูปแบบและไม่เพียงวิธีเดียว ดูsscce.org
BЈовић

คำตอบ:


17

เช่นเดียวกันกับที่ถูกต้องตามกฎหมายอนุญาตให้มีการdelete thisจัดวางตำแหน่งใหม่สำหรับthisเท่าที่ฉันรู้ นอกจากนี้เกี่ยวกับว่าthisหรือตัวชี้ / การอ้างอิงอื่น ๆ ที่มีอยู่แล้วสามารถนำมาใช้ได้ภายหลังมีข้อ จำกัด บางประการดังนี้:

[basic.life]

หากหลังจากอายุการใช้งานของวัตถุสิ้นสุดลงและก่อนที่หน่วยเก็บข้อมูลที่ครอบครองวัตถุนั้นจะถูกนำมาใช้ใหม่หรือปล่อยออกมาวัตถุใหม่จะถูกสร้างขึ้นในที่เก็บสินค้าซึ่งมีวัตถุดั้งเดิมครอบครองอยู่ตัวชี้ที่ชี้ไปยังวัตถุต้นฉบับ อ้างอิงถึงวัตถุดั้งเดิมหรือชื่อของวัตถุต้นฉบับจะอ้างถึงวัตถุใหม่โดยอัตโนมัติและเมื่ออายุการใช้งานของวัตถุใหม่เริ่มต้นขึ้นสามารถใช้เพื่อจัดการวัตถุใหม่ได้หาก:

  • ที่เก็บข้อมูลสำหรับวัตถุใหม่ซ้อนทับตำแหน่งที่เก็บข้อมูลที่วัตถุดั้งเดิมครอบครองและ
  • วัตถุใหม่นั้นเป็นชนิดเดียวกับวัตถุต้นฉบับ (ไม่สนใจตัวตรวจสอบ CV ระดับบนสุด) และ
  • ชนิดของวัตถุต้นฉบับนั้นไม่มีคุณสมบัติแบบ const และถ้าเป็นชนิดของคลาสจะไม่มีสมาชิกของข้อมูลที่ไม่คงที่ซึ่งเป็นชนิดที่มีคุณสมบัติของ const หรือประเภทอ้างอิงและ
  • ไม่ใช่วัตถุดั้งเดิมหรือวัตถุใหม่เป็น subobject ที่อาจทับซ้อนกัน ([intro.object])

สองคนแรกมีความพึงพอใจในตัวอย่างนี้ แต่สองคนสุดท้ายจะต้องนำมาพิจารณา

เกี่ยวกับจุดที่สามเนื่องจากฟังก์ชั่นนั้นไม่มีคุณสมบัติของ const จึงควรมีความปลอดภัยพอสมควรที่จะสมมติว่าวัตถุดั้งเดิมนั้นไม่ใช่แบบ const ความผิดปกติจะอยู่ที่ฝั่งผู้โทรหากความมั่นคงถูกทิ้งไป เกี่ยวกับ const / สมาชิกอ้างอิงฉันคิดว่าสามารถตรวจสอบได้โดยยืนยันว่าสิ่งนี้สามารถกำหนดได้:

static_assert(std::is_trivial_v<A> && std::is_copy_assignable_v<A>);

แน่นอนเนื่องจากความสามารถในการมอบหมายเป็นสิ่งจำเป็นคุณสามารถใช้*this = {};สิ่งที่ฉันคาดหวังว่าจะสร้างโปรแกรมเดียวกันขึ้นมาแทน กรณีการใช้งานอาจจะน่าสนใจมากขึ้นอาจจะนำมาใช้ใหม่ของหน่วยความจำ*thisสำหรับวัตถุชนิดอื่น (ซึ่งจะล้มเหลวความต้องการในการใช้thisอย่างน้อยโดยไม่ต้อง reinterpreting ฟอก +)

เช่นเดียวกับการdelete thisจัดวางตำแหน่งใหม่เพื่อthisแทบจะไม่สามารถอธิบายได้ว่า "ปลอดภัย"


น่าสนใจ เป็นไปได้หรือไม่ที่จะทำให้แน่ใจว่าเงื่อนไขทั้งหมดนี้พอใจกับ static_assert ในลักษณะบางอย่าง? ไม่แน่ใจว่ามีสมาชิกเกี่ยวกับสมาชิกหรือไม่ ...
Amomum

1
@ Amumum ฉันไม่คิดว่าเป็น subobject เป็นสิ่งที่สามารถทดสอบได้ สมาชิกหรือสมาชิกอ้างอิงจะทำให้ชั้นไม่สามารถกำหนดได้ซึ่งฉันไม่คิดว่าสามารถเกิดขึ้นได้เป็นอย่างอื่นสำหรับชั้นเรียนที่ไม่สำคัญ
eerorika

นี่หมายความว่าการทำสมนามที่เข้มงวดเข้ามาเกี่ยวข้องกับความเป็นเจ้าหรือไม่? คุณสามารถให้ตัวอย่างที่สิ่งนี้อาจเข้ามาเล่น?
darune

1
@darune สร้างวัตถุ const วางตำแหน่งใหม่ลงบนมันใช้ชื่อเดิมของวัตถุ (หรือตัวชี้หรือการอ้างอิงที่มีอยู่แล้ว) และพฤติกรรมจะไม่ได้กำหนดไว้ ค่อนข้างมีผลเหมือนกันกับการเพิ่มประสิทธิภาพนามแฝงที่เข้มงวดหากไม่เหมือนกันโดยสิ้นเชิง
eerorika

1
ผกผันของมีdelete ptr new T()ผกผันของมีnew(ptr)T{} stackoverflow.com/a/8918942/845092ptr->~T();
Mooing Duck

7

กฎที่ครอบคลุมสิ่งนี้อยู่ใน[basic.life] / 5

โปรแกรมอาจสิ้นสุดอายุการใช้งานของวัตถุใด ๆ โดยการนำหน่วยเก็บข้อมูลที่วัตถุนั้นครอบครองหรือโดยการเรียก destructor อย่างชัดเจนสำหรับวัตถุประเภทคลาส สำหรับออบเจ็กต์ประเภทคลาสโปรแกรมไม่จำเป็นต้องเรียกใช้ destructor อย่างชัดเจนก่อนที่หน่วยเก็บข้อมูลที่มีการใช้งานวัตถุนั้นจะถูกนำมาใช้ซ้ำหรือปล่อยออกมา อย่างไรก็ตามถ้าไม่มีการเรียกไปยัง destructor อย่างชัดเจนหรือหากไม่ใช้การลบนิพจน์เพื่อปล่อยหน่วยเก็บข้อมูล destructor จะไม่ถูกเรียกโดยปริยายและโปรแกรมใด ๆ ที่ขึ้นอยู่กับผลข้างเคียงที่ผลิตโดย destructor นั้นมีพฤติกรรมที่ไม่ได้กำหนดไว้

และ[basic.life] / 8

หากหลังจากอายุการใช้งานของวัตถุสิ้นสุดลงและก่อนที่หน่วยเก็บข้อมูลที่ครอบครองวัตถุนั้นจะถูกนำมาใช้ใหม่หรือปล่อยออกมาวัตถุใหม่จะถูกสร้างขึ้นในที่เก็บสินค้าซึ่งมีวัตถุดั้งเดิมครอบครองอยู่ตัวชี้ที่ชี้ไปยังวัตถุต้นฉบับ อ้างอิงถึงวัตถุดั้งเดิมหรือชื่อของวัตถุต้นฉบับจะอ้างถึงวัตถุใหม่โดยอัตโนมัติและเมื่ออายุการใช้งานของวัตถุใหม่เริ่มต้นขึ้นสามารถใช้เพื่อจัดการวัตถุใหม่ได้หาก:

  • ที่เก็บข้อมูลสำหรับวัตถุใหม่ซ้อนทับตำแหน่งที่เก็บข้อมูลที่วัตถุดั้งเดิมครอบครองและ

  • วัตถุใหม่นั้นเป็นชนิดเดียวกับวัตถุต้นฉบับ (ไม่สนใจตัวตรวจสอบ CV ระดับบนสุด) และ

  • ชนิดของวัตถุต้นฉบับนั้นไม่มีคุณสมบัติแบบ const และถ้าเป็นชนิดของคลาสจะไม่มีสมาชิกของข้อมูลที่ไม่คงที่ซึ่งเป็นชนิดที่มีคุณสมบัติของ const หรือประเภทอ้างอิงและ

  • ไม่ใช่วัตถุดั้งเดิมหรือวัตถุใหม่เป็น subobject ที่อาจทับซ้อนกัน ([intro.object])

เนื่องจากวัตถุของคุณไม่สำคัญคุณไม่ต้องกังวลเกี่ยวกับ [basic.life] / 5 และตราบใดที่คุณพอใจกับสัญลักษณ์แสดงหัวข้อย่อยจาก [basic.life] / 8 ดังนั้นมันจึงปลอดภัย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.