มีคำตอบที่ยอดเยี่ยมอยู่แล้วดังนั้นฉันจึงเพิ่มบางสิ่งที่ลืมไป
0. RAII เป็นเรื่องเกี่ยวกับขอบเขต
RAII เกี่ยวกับทั้งสองอย่าง:
- การรับทรัพยากร (ไม่ว่าทรัพยากรใดก็ตาม) ในตัวสร้างและยกเลิกการรับทรัพยากรในตัวทำลาย
- มีตัวสร้างดำเนินการเมื่อมีการประกาศตัวแปรและตัวทำลายจะดำเนินการโดยอัตโนมัติเมื่อตัวแปรอยู่นอกขอบเขต
คนอื่นตอบไปแล้วดังนั้นฉันจะไม่อธิบายอย่างละเอียด
1. เมื่อเขียนโค้ดใน Java หรือ C # แสดงว่าคุณใช้ RAII อยู่แล้ว ...
MONSIEUR JOURDAIN: อะไรนะ! เมื่อฉันพูดว่า "นิโคลนำรองเท้าแตะมาให้ฉันและให้หมวกคลุมผมด้วย" นั่นเป็นร้อยแก้วใช่ไหม
ปรมาจารย์ปรัชญา: ครับท่าน
MONSIEUR JOURDAIN: เป็นเวลากว่าสี่สิบปีแล้วที่ฉันพูดเป็นร้อยแก้วโดยไม่รู้อะไรเกี่ยวกับเรื่องนี้และฉันมีหน้าที่ต้องสอนคุณอย่างมาก
- โมเลียร์: สุภาพบุรุษชนชั้นกลาง, องก์ที่ 2, ฉากที่ 4
อย่างที่ Monsieur Jourdain ทำกับร้อยแก้ว C # และแม้แต่คน Java ก็ใช้ RAII อยู่แล้ว แต่ในทางที่ซ่อนเร้น ตัวอย่างเช่นโค้ด Java ต่อไปนี้ (ซึ่งเขียนในลักษณะเดียวกันใน C # โดยแทนที่synchronized
ด้วยlock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... กำลังใช้ RAII อยู่แล้ว: การได้มาของ mutex เสร็จสิ้นในคีย์เวิร์ด ( synchronized
หรือlock
) และการยกเลิกการได้มาจะเสร็จสิ้นเมื่อออกจากขอบเขต
มันเป็นธรรมชาติมากในสัญกรณ์มันแทบจะไม่ต้องมีคำอธิบายแม้แต่คนที่ไม่เคยได้ยินเกี่ยวกับ RAII
ข้อได้เปรียบที่ C ++ มีเหนือ Java และ C # ที่นี่คือทุกอย่างสามารถทำได้โดยใช้ RAII ตัวอย่างเช่นไม่มี build-in ที่เทียบเท่าsynchronized
หรือlock
ใน C ++ โดยตรง แต่เรายังสามารถมีได้
ใน C ++ จะเขียนว่า:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
ซึ่งสามารถเขียนได้อย่างง่ายดาย Java / C # way (โดยใช้มาโคร C ++):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII มีการใช้งานอื่น
WHITE RABBIT: [ร้องเพลง] ฉันมาสาย / ฉันมาสาย / สำหรับเดทที่สำคัญมาก / ไม่มีเวลาพูด "สวัสดี" / ลาก่อน. / ฉันมาสายฉันมาสายฉันมาสาย
- Alice in Wonderland (เวอร์ชั่นดิสนีย์ปี 2494)
คุณรู้ว่าตัวสร้างจะถูกเรียกเมื่อใด (ที่การประกาศอ็อบเจ็กต์) และคุณรู้ว่าเมื่อไหร่ที่ตัวทำลายที่เกี่ยวข้องจะถูกเรียก (ที่ทางออกของขอบเขต) ดังนั้นคุณจึงสามารถเขียนโค้ดเวทย์มนตร์ได้เกือบโดยใช้บรรทัด ยินดีต้อนรับสู่ดินแดนมหัศจรรย์ C ++ (อย่างน้อยจากมุมมองของนักพัฒนา C ++)
ตัวอย่างเช่นคุณสามารถเขียนวัตถุตอบโต้ (ฉันปล่อยให้เป็นแบบฝึกหัด) และใช้เพียงแค่ประกาศตัวแปรเช่นเดียวกับที่ใช้วัตถุล็อคด้านบน:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
ซึ่งแน่นอนว่าสามารถเขียนอีกครั้งวิธี Java / C # โดยใช้มาโคร:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. ทำไม C ++ จึงขาดfinally
?
[ตะโกน] นับถอยหลังครั้งสุดท้าย !
- ยุโรป: การนับถอยหลังครั้งสุดท้าย (ขออภัยฉันไม่มีคำพูดที่นี่ ... :-)
finally
ข้อถูกนำมาใช้ใน C # / Java เพื่อกำจัดทรัพยากรที่จับในกรณีของขอบเขตออก (ทั้งผ่านreturn
หรือยกเว้นโยน)
ผู้อ่านข้อกำหนด Astute จะสังเกตเห็นว่า C ++ ไม่มีประโยคในที่สุด และนี่ไม่ใช่ข้อผิดพลาดเนื่องจากไม่จำเป็นต้องใช้ C ++ เนื่องจาก RAII จัดการการกำจัดทรัพยากรแล้ว (และเชื่อฉันเถอะว่าการเขียนตัวทำลาย C ++ นั้นง่ายกว่าการเขียน Java ที่ถูกต้องในที่สุดหรือแม้แต่วิธี Dispose ที่ถูกต้องของ C #)
ถึงกระนั้นบางครั้งfinally
ประโยคก็ดูดี เราสามารถทำได้ใน C ++ หรือไม่? ใช่เราทำได้! และอีกครั้งด้วยการใช้ RAII แบบอื่น
สรุป: RAII เป็นมากกว่าปรัชญาใน C ++ นั่นคือ C ++
ไรงี้? นี่คือ C ++ !!!
- ความคิดเห็นที่ขุ่นเคืองของนักพัฒนา C ++ คัดลอกโดยกษัตริย์ Sparta ผู้ปิดบังและเพื่อน 300 คนของเขาอย่างไร้ยางอาย
เมื่อคุณมาถึงระดับของประสบการณ์ใน C ++ บางคุณเริ่มคิดในแง่ของRAIIในแง่ของการดำเนินการ construtors และ destructors อัตโนมัติ
คุณเริ่มคิดในแง่ของขอบเขตและ{
และ}
ตัวอักษรกลายเป็นคนที่สำคัญที่สุดในรหัสของคุณ
และเกือบทุกอย่างเหมาะสมในแง่ของ RAII: ยกเว้นความปลอดภัย, mutexes, การเชื่อมต่อฐานข้อมูล, การร้องขอฐานข้อมูล, การเชื่อมต่อเซิร์ฟเวอร์, นาฬิกา, ที่จับระบบปฏิบัติการ ฯลฯ และสุดท้าย แต่ไม่ท้ายสุดคือหน่วยความจำ
ส่วนฐานข้อมูลนั้นไม่สำคัญเลยเช่นถ้าคุณยอมจ่ายราคาคุณสามารถเขียนในรูปแบบ " การเขียนโปรแกรมธุรกรรม " เรียกใช้บรรทัดและบรรทัดของโค้ดจนกว่าจะตัดสินใจในท้ายที่สุดหากคุณต้องการที่จะทำการเปลี่ยนแปลงทั้งหมด หรือถ้าเป็นไปไม่ได้ให้เปลี่ยนกลับการเปลี่ยนแปลงทั้งหมดกลับไป (ตราบเท่าที่แต่ละบรรทัดเป็นไปตามการรับประกันข้อยกเว้นที่เข้มงวดเป็นอย่างน้อย) (ดูส่วนที่สองของบทความHerb's Sutterสำหรับการเขียนโปรแกรมธุรกรรม)
และเหมือนปริศนาทุกอย่างลงตัว
RAII เป็นส่วนหนึ่งของ C ++ มาก C ++ ไม่สามารถเป็น C ++ ได้หากไม่มีมัน
สิ่งนี้อธิบายว่าเหตุใดนักพัฒนา C ++ ที่มีประสบการณ์จึงหลงใหล RAII และเหตุใด RAII จึงเป็นสิ่งแรกที่พวกเขาค้นหาเมื่อลองใช้ภาษาอื่น
และอธิบายได้ว่าทำไม Garbage Collector ในขณะที่เป็นเทคโนโลยีที่ยอดเยี่ยมในตัวมันจึงไม่น่าประทับใจนักจากมุมมองของนักพัฒนา C ++:
- RAII จัดการกรณีส่วนใหญ่ที่จัดการโดย GC อยู่แล้ว
- GC จัดการได้ดีกว่า RAII ด้วยการอ้างอิงแบบวงกลมบนอ็อบเจ็กต์ที่มีการจัดการที่บริสุทธิ์ (ลดลงโดยการใช้จุดอ่อนอย่างชาญฉลาด)
- GC ยังคงถูก จำกัด ไว้ที่หน่วยความจำในขณะที่ RAII สามารถจัดการทรัพยากรประเภทใดก็ได้
- ตามที่อธิบายไว้ข้างต้น RAII สามารถทำอะไรได้อีกมากมาย ...