การละเว้น“ destructors” ใน C กำลังทำให้ YAGNI อยู่ไกลเกินไปใช่ไหม


9

ฉันกำลังทำงานกับแอพพลิเคชั่นแบบฝังตัวขนาดกลางใน C โดยใช้เทคนิคแบบ OO "คลาส" ของฉันคือโมดูล. h / .c โดยใช้ data structs และ function pointers structs เพื่อเลียนแบบ encapsulation, polymorphism และการฉีดพึ่งพา

ตอนนี้ใคร ๆ ก็คาดหวังว่าmyModule_create(void)ฟังก์ชั่นจะมาพร้อมกับmyModule_destroy(pointer)คู่ แต่โครงการที่ถูกฝังอยู่นั้นทรัพยากรที่มีการสร้างอินสแตนซ์สมจริงไม่ควรปล่อยออกมา

ฉันหมายถึงถ้าฉันมีพอร์ตอนุกรม UART 4 ​​พอร์ตและฉันสร้างอินสแตนซ์ UART 4 ​​อันด้วยหมุดและการตั้งค่าที่ต้องการมีเหตุผลที่ไม่ต้องการทำลาย UART # 2 ในบางจุดในระหว่างรันไทม์

ดังนั้นตามหลักการ YAGNI (คุณไม่ต้องการมัน) ฉันควรละเว้น destructors หรือไม่ มันดูแปลกมากสำหรับฉัน แต่ฉันไม่สามารถนึกถึงการใช้งานได้สำหรับพวกเขา ทรัพยากรจะถูกปลดปล่อยเมื่ออุปกรณ์ปิด


4
หากคุณไม่มีวิธีการกำจัดวัตถุคุณกำลังส่งข้อความที่ชัดเจนว่าพวกมันมีอายุการใช้งาน "ไม่สิ้นสุด" เมื่อสร้างขึ้น ถ้าสิ่งนี้สมเหตุสมผลกับแอปพลิเคชันของคุณฉันพูดว่า: ทำ
glampert

4
หากคุณกำลังจะไปไกลในการเชื่อมต่อประเภทกับกรณีการใช้งานเฉพาะของคุณทำไมถึงมีmyModule_create(void)ฟังก์ชั่น? คุณสามารถเขียนโค้ดอินสแตนซ์เฉพาะที่คุณคาดว่าจะใช้กับอินเทอร์เฟซที่คุณเปิดเผย
Doval

@Doval ฉันคิดเกี่ยวกับมัน ฉันเป็นนักศึกษาฝึกงานโดยใช้ชิ้นส่วนและรหัสบิตจากหัวหน้างานของฉันดังนั้นฉันจึงพยายามที่จะเล่นปาหี่กับ "ทำถูกต้อง" ทดลองสไตล์ OO ใน C เพื่อรับประสบการณ์และความสอดคล้องกับมาตรฐานของ บริษัท
Asics

2
@glampert เล็บมัน ฉันจะเพิ่มว่าคุณควรทำให้อายุการใช้งานไม่ จำกัด คาดว่าจะสะอาดในเอกสารประกอบของฟังก์ชั่นการสร้าง
Blrfl

คำตอบ:


11

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

Glampertถูกต้อง; ไม่จำเป็นต้องมี destructors ที่นี่ พวกเขาเพียงแค่สร้างโค้ดปูดและอันตรายสำหรับผู้ใช้ (ใช้วัตถุหลังจาก destructor เรียกว่าเป็นพฤติกรรมที่ไม่ได้กำหนด)

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


3

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

ดังนั้นฉันจะให้ constructors, destructors และตรรกะการปิดระบบอย่างแน่นอนแม้ในระบบฝังตัวที่ "ในทางทฤษฎี" ไม่ควรออกเพื่อความสะดวกในการตรวจจับการรั่วไหลของหน่วยความจำเพียงอย่างเดียว ในความเป็นจริงการตรวจสอบการรั่วไหลของหน่วยความจำมีความสำคัญยิ่งกว่าหากรหัสแอปพลิเคชันไม่ควรออก


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

@ รหัส: ไม่มีเหตุผลที่จะ จำกัด ตัวเอง ในขณะที่คุณอาจได้รับการออกแบบที่ยอดเยี่ยมที่จัดสรรหน่วยความจำไว้ล่วงหน้าเมื่อเริ่มต้นเมื่อคนหลังจากที่คุณมาพร้อมผู้ที่ไม่ได้เป็นส่วนตัวกับรูปแบบที่ยิ่งใหญ่นี้หรือไม่เห็นความสำคัญของมันพวกเขาจะจัดสรรหน่วยความจำ บินและมีการออกแบบของคุณ เพียงแค่ทำให้ถูกต้องและจัดสรร / ยกเลิกการจัดสรรและตรวจสอบว่าสิ่งที่คุณใช้งานได้จริง หากคุณมีอุปกรณ์ที่ จำกัด หน่วยความจำอย่างแท้จริงสิ่งที่ทำโดยทั่วไปคือการแทนที่ตัวดำเนินการใหม่ / malloc และบล็อกการจัดสรรล่วงหน้า
Dunk

3

ในการพัฒนาของฉันซึ่งใช้ชนิดข้อมูลแบบทึบเพื่อสนับสนุนแนวทางแบบ OO ฉันก็ปล้ำกับคำถามนี้ ตอนแรกฉันอยู่ในค่ายแห่งการกำจัดผู้ทำลายล้างจากมุมมองของ YAGNI รวมถึงมุมมอง "รหัสตาย" ของ MISRA (ฉันมีห้องทรัพยากรมากมายนั่นไม่ใช่การพิจารณา)

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

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

ในขณะที่ที่อยู่ YAGNI มันไม่ได้อยู่รหัสตาย สำหรับสิ่งนั้นฉันพบว่าแมโครคอมไพล์แบบมีเงื่อนไขบางอย่างเช่น #define BUILD_FOR_TESTING ทำให้ destructor ถูกกำจัดออกจากบิลด์การผลิตขั้นสุดท้าย

การทำเช่นนี้: คุณมี destructor สำหรับการทดสอบ / นำกลับมาใช้ในอนาคตและคุณตอบสนองวัตถุประสงค์การออกแบบของ YAGNI และกฎ "ไม่มีรหัสตาย"


ระมัดระวังเกี่ยวกับ # ifdef'ing รหัสทดสอบ / ผลิตภัณฑ์ของคุณ มีความปลอดภัยพอสมควรเมื่อนำไปใช้กับฟังก์ชั่นทั้งหมดตามที่คุณอธิบายเพราะหากจำเป็นต้องใช้ฟังก์ชั่นในความเป็นจริงการรวบรวมจะล้มเหลว อย่างไรก็ตามการใช้ #ifdef inline ภายในฟังก์ชั่นมีความเสี่ยงมากเนื่องจากตอนนี้คุณกำลังทดสอบ codepath ที่แตกต่างจากที่ใช้ใน prod
เควิน

0

คุณอาจมี destructor แบบไม่ต้องทำอะไรก็ได้

  void noop_destructor(void*) {};

จากนั้นตั้งค่า destructor ของUartอาจจะใช้

  #define Uart_destructor noop_destructor

(เพิ่ม cast ที่เหมาะสมหากจำเป็น)

อย่าลืมเอกสาร บางทีคุณอาจต้องการ

 #define Uart_destructor abort

อีกวิธีหนึ่งคือกรณีพิเศษในรหัสทั่วไปที่เรียก destructor ตัวพิมพ์ใหญ่เมื่อฟังก์ชันตัวชี้ destructor คือNULLการหลีกเลี่ยงการเรียก

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