pImpl สำนวนที่ใช้จริงในการปฏิบัติจริง


165

ฉันกำลังอ่านหนังสือ "เจ๋ง C ++" โดย Herb Sutter และในหนังสือเล่มนั้นฉันได้เรียนรู้เกี่ยวกับสำนวน pImpl โดยพื้นฐานแล้วความคิดคือการสร้างโครงสร้างสำหรับprivateวัตถุของclassและจัดสรรแบบไดนามิกเพื่อลดเวลาในการรวบรวม (และซ่อนการใช้งานส่วนตัวในลักษณะที่ดีกว่า)

ตัวอย่างเช่น:

class X
{
private:
  C c;
  D d;  
} ;

สามารถเปลี่ยนเป็น:

class X
{
private:
  struct XImpl;
  XImpl* pImpl;       
};

และใน CPP คำจำกัดความ:

struct X::XImpl
{
  C c;
  D d;
};

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

ฉันควรใช้ทุกที่หรือด้วยความระมัดระวัง? และแนะนำให้ใช้เทคนิคนี้ในระบบฝังตัว (ซึ่งประสิทธิภาพมีความสำคัญมาก)?


นี่เป็นสิ่งเดียวกับการตัดสินใจว่า X เป็นอินเทอร์เฟซ (นามธรรม) และ Ximpl เป็นการใช้งานหรือไม่ struct XImpl : public X. นั่นทำให้ฉันรู้สึกเป็นธรรมชาติมากขึ้น มีปัญหาอื่นที่ฉันพลาดไปไหม
Aaron McDaid

@AaronMcDaid: มันคล้ายกัน แต่มีข้อดีที่ (a) ฟังก์ชั่นสมาชิกไม่จำเป็นต้องเป็นเสมือนจริงและ (b) คุณไม่จำเป็นต้องมีโรงงานหรือคำจำกัดความของคลาสการใช้งานเพื่อสร้างอินสแตนซ์
Mike Seymour

2
@AaronMcDaid pimpl idiom หลีกเลี่ยงการเรียกใช้ฟังก์ชันเสมือน นอกจากนี้ยังมีอีกเล็กน้อย C ++ - ish (สำหรับความคิดของ C ++ - ish); คุณเรียกใช้ตัวสร้างมากกว่าฟังก์ชั่นจากโรงงาน ฉันใช้ทั้งสองขึ้นอยู่กับสิ่งที่อยู่ในฐานรหัสที่มีอยู่ --- pimpl idiom (เดิมเรียกว่า Cheshire cat idiom และ predating คำอธิบายของ Herb ภายในเวลาอย่างน้อย 5 ปี) ดูเหมือนว่าจะมีประวัติที่ยาวนานกว่าและมากกว่า ใช้กันอย่างแพร่หลายใน C ++ แต่อย่างอื่นทั้งสองทำงาน
James Kanze

30
ใน C ++, pimpl ควรจะดำเนินการกับมากกว่าconst unique_ptr<XImpl> XImpl*
Neil G

1
"ไม่เคยเห็นวิธีการแบบนี้มาก่อนทั้งใน บริษัท ที่ฉันเคยทำงานหรือในโครงการโอเพ่นซอร์ส" Qt แทบจะไม่เคยใช้มันเลย
ManuelSchneid3r

คำตอบ:


132

ดังนั้นฉันสงสัยว่าเทคนิคนี้ใช้ในทางปฏิบัติจริง ๆ หรือไม่ ฉันควรใช้ทุกที่หรือด้วยความระมัดระวัง?

แน่นอนว่ามันถูกใช้ ฉันใช้มันในโครงการของฉันในเกือบทุกชั้น


เหตุผลในการใช้ PIMPL สำนวน:

ความเข้ากันได้ไบนารี

เมื่อคุณพัฒนาไลบรารีคุณสามารถเพิ่ม / ปรับเปลี่ยนฟิลด์เป็นXImplโดยไม่ทำลายความเข้ากันได้ของไบนารีกับไคลเอนต์ของคุณ (ซึ่งอาจหมายถึงล่ม!) เนื่องจากรูปแบบไบนารีของXคลาสไม่เปลี่ยนแปลงเมื่อคุณเพิ่มเขตข้อมูลใหม่ลงในXimplคลาสจึงปลอดภัยที่จะเพิ่มฟังก์ชันการทำงานใหม่ให้กับไลบรารีในการปรับปรุงรุ่นรอง

แน่นอนคุณยังสามารถเพิ่มวิธีการที่ไม่ใช่แบบเสมือน / สาธารณะใหม่ให้กับX/ XImplโดยไม่ทำลายความเข้ากันได้ของไบนารี

การซ่อนข้อมูล

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

เวลารวบรวม

เวลาในการรวบรวมลดลงเนื่องจากมีเพียงไฟล์ซอร์ส (การนำไปใช้) Xที่ต้องสร้างใหม่เมื่อคุณเพิ่ม / ลบฟิลด์และ / หรือวิธีการไปยังXImplคลาส (ซึ่งแม็พกับการเพิ่มฟิลด์ / เมธอดส่วนตัวในเทคนิคมาตรฐาน) ในทางปฏิบัติมันเป็นเรื่องธรรมดา

ด้วยเทคนิคส่วนหัว / การนำไปใช้งานมาตรฐาน (โดยไม่มี PIMPL) เมื่อคุณเพิ่มฟิลด์ใหม่ลงXในไคลเอนต์ที่เคยจัดสรรX(ทั้งบนสแต็กหรือบนกอง) จะต้องทำการคอมไพล์ใหม่เนื่องจากต้องปรับขนาดของการจัดสรร ทีนี้ลูกค้าทุกคนที่ไม่ได้จัดสรร X ก็จำเป็นต้องทำการคอมไพล์ใหม่ แต่มันเป็นแค่ค่าใช้จ่าย (โค้ดผลลัพธ์ที่ฝั่งไคลเอ็นต์จะเหมือนกัน)

ยิ่งไปกว่านั้นด้วยการแยกส่วนหัว / การใช้งานมาตรฐานXClient1.cppจะต้องมีการคอมไพล์ใหม่แม้ว่าจะมีการX::foo()เพิ่มXและX.hเปลี่ยนวิธีการส่วนตัวแม้ว่าXClient1.cppจะไม่สามารถเรียกวิธีการนี้ได้ด้วยเหตุผลของการห่อหุ้ม! ดังกล่าวข้างต้นเป็นค่าใช้จ่ายที่บริสุทธิ์และเกี่ยวข้องกับการทำงานของระบบสร้าง C ++ ในชีวิตจริง

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


แนะนำให้ใช้เทคนิคนี้ในระบบฝังตัวหรือไม่ (ในกรณีที่ประสิทธิภาพมีความสำคัญมาก)

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


16
+1 เพราะใช้กันอย่างแพร่หลายใน บริษัท ที่ฉันทำงานด้วยและด้วยเหตุผลเดียวกัน
เบอนัวต์

9
ยังเข้ากันได้ไบนารี
Ambroz Bizjak

9
ในไลบรารี Qt วิธีนี้ยังใช้ในสถานการณ์ตัวชี้อัจฉริยะ ดังนั้น QString จะเก็บเนื้อหาไว้ในคลาสที่ไม่เปลี่ยนรูปแบบภายใน เมื่อคลาสสาธารณะถูก "คัดลอก" ตัวชี้ของสมาชิกส่วนตัวจะถูกคัดลอกแทนคลาสส่วนตัวทั้งหมด คลาสส่วนตัวเหล่านี้ยังใช้พอยน์เตอร์อัจฉริยะดังนั้นโดยพื้นฐานแล้วคุณจะได้รับการเก็บขยะด้วยคลาสส่วนใหญ่นอกเหนือจากการปรับปรุงประสิทธิภาพอย่างมากเนื่องจากการคัดลอกตัวชี้แทนที่จะคัดลอกคลาสเต็ม
Timothy Baldridge

8
ยิ่งไปกว่านั้นด้วย pimpl idiom Qt สามารถรักษาความเข้ากันได้ของไบนารีทั้งข้างหน้าและข้างหลังภายในเวอร์ชันหลักเดียว (ในกรณีส่วนใหญ่) IMO นี่คือเหตุผลที่สำคัญที่สุดในการใช้งาน
whitequark

1
นอกจากนี้ยังมีประโยชน์สำหรับการนำรหัสเฉพาะแพลตฟอร์มไปใช้เนื่องจากคุณสามารถใช้ API เดิมได้
doc

49

ดูเหมือนว่ามีไลบรารีจำนวนมากในนั้นใช้เพื่อให้มีความเสถียรใน API อย่างน้อยสำหรับบางเวอร์ชัน

แต่สำหรับทุกสิ่งคุณไม่ควรใช้ทุกสิ่งโดยไม่ระมัดระวัง คิดก่อนใช้เสมอ ประเมินข้อดีที่จะได้รับจากสิ่งที่คุณได้รับและหากพวกเขาคุ้มค่ากับราคาที่คุณจ่าย

ข้อดีที่คุณอาจได้รับ:

  • ช่วยในการรักษาความเข้ากันได้ไบนารีของห้องสมุดสาธารณะ
  • ซ่อนรายละเอียดภายในบางอย่าง
  • ลดรอบการคอมไพล์ซ้ำ

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

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

  • เพิ่มการใช้หน่วยความจำเนื่องจากมีการจัดสรรมากกว่าตัวแปรไร้เดียงสา
  • เพิ่มความพยายามในการบำรุงรักษา (คุณต้องเขียนฟังก์ชันการส่งต่ออย่างน้อย)
  • การสูญเสียประสิทธิภาพ (คอมไพเลอร์อาจไม่สามารถอินไลน์เนื้อหาเหมือนกับที่ใช้งานคลาสของคุณได้อย่างไร้เดียงสา)

ดังนั้นให้ทุกอย่างให้คุณค่าและประเมินด้วยตัวคุณเอง สำหรับฉันมันเกือบจะกลายเป็นว่าการใช้สำนวน pimpl นั้นไม่คุ้มค่ากับความพยายาม มีเพียงกรณีเดียวที่ฉันใช้มันเอง (หรืออย่างน้อยคล้ายกัน):

wrapper C ++ ของฉันสำหรับการstatโทรlinux นี่คือโครงสร้างจากส่วนหัว C อาจแตกต่างกันขึ้นอยู่กับสิ่งที่#definesตั้งไว้ และเนื่องจากส่วนหัวของ wrapper ของฉันไม่สามารถควบคุมทั้งหมดได้ฉันจึงใส่เฉพาะไฟล์#include <sys/stat.h>ของฉัน.cxxและหลีกเลี่ยงปัญหาเหล่านี้


2
มันควรจะใช้เกือบทุกครั้งสำหรับส่วนต่อประสานของระบบเพื่อให้ระบบรหัสส่วนต่อประสานเป็นอิสระ Fileคลาสของฉัน(ซึ่งเปิดเผยข้อมูลส่วนใหญ่statจะกลับมาภายใต้ Unix) ใช้อินเทอร์เฟซเดียวกันภายใต้ทั้ง Windows และ Unix
James Kanze

5
@JamesKanze: ถึงแม้ฉันจะนั่งเป็นส่วนตัวก่อนและคิดว่ามันอาจจะไม่เพียงพอที่#ifdefจะทำให้เสื้อคลุมบาง ๆ เล็กที่สุดเท่าที่จะทำได้ แต่ทุกคนมีเป้าหมายที่แตกต่างกันสิ่งสำคัญคือการใช้เวลาคิดเกี่ยวกับมันแทนที่จะทำอะไรบางอย่างสุ่มสี่สุ่มห้า
PlasmaHH

31

เห็นด้วยกับคนอื่น ๆ ทั้งหมดเกี่ยวกับสินค้า แต่ขอให้ฉันเอาหลักฐานมา จำกัด : ไม่ทำงานกับแม่แบบได้ดีได้ดี

เหตุผลก็คือการสร้างอินสแตนซ์ของเท็มเพลตต้องมีการประกาศแบบเต็มซึ่งมีการเริ่มอินสแตนซ์ (และนี่คือเหตุผลหลักที่คุณไม่เห็นวิธีเทมเพลตที่กำหนดไว้ในไฟล์ CPP)

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

เป็นกระบวนทัศน์ที่ดีสำหรับ OOP คลาสสิก (การสืบทอด) แต่ไม่ใช่สำหรับการเขียนโปรแกรมทั่วไป (อิงตามความเชี่ยวชาญ)


4
คุณต้องแม่นยำยิ่งขึ้น: ไม่มีปัญหาใด ๆ เมื่อใช้คลาส PIMPL เป็นอาร์กิวเมนต์ประเภทเทมเพลต เฉพาะในกรณีที่คลาสการติดตั้งใช้งานต้องได้รับการ parametrized บนอาร์กิวเมนต์เท็มเพลตของคลาสภายนอกจะไม่สามารถซ่อนจากส่วนหัวของอินเตอร์เฟสได้อีกต่อไปแม้ว่าจะยังเป็นคลาสส่วนตัวก็ตาม หากคุณสามารถลบอาร์กิวเมนต์เทมเพลตคุณสามารถทำ PIMPL "เหมาะสม" ได้อย่างแน่นอน ด้วยการลบชนิดคุณสามารถทำ PIMPL ในคลาสที่ไม่ใช่เท็มเพลตพื้นฐานและจากนั้นให้คลาสเทมเพลตได้รับมา
Reinstate Monica

22

คนอื่น ๆ ได้ให้เทคนิคขึ้น / ลง แต่ฉันคิดว่าต่อไปนี้เป็นที่น่าสังเกต:

ก่อนอื่นไม่ต้องเชื่อฟัง ถ้า pImpl ใช้งานได้กับสถานการณ์ของคุณให้ใช้ - อย่าใช้เพียงเพราะ "ดีกว่า OO เพราะซ่อนการใช้งานจริง ๆ " เป็นต้นการอ้างอิงคำถาม C ++:

การห่อหุ้มมีไว้สำหรับรหัสไม่ใช่คน ( ซอร์ส )

เพียงเพื่อให้คุณตัวอย่างของซอฟต์แวร์ที่มาเปิดที่มีการใช้และทำไม: OpenThreads ห้องสมุดเกลียวที่ใช้โดยOpenSceneGraph แนวคิดหลักคือการลบออกจากส่วนหัว (เช่น<Thread.h>) รหัสเฉพาะแพลตฟอร์มทั้งหมดเนื่องจากตัวแปรสถานะภายใน (เช่นตัวจัดการเธรด) แตกต่างจากแพลตฟอร์มหนึ่งไปอีกแพลตฟอร์ม วิธีนี้สามารถคอมไพล์โค้ดกับไลบรารีของคุณโดยไม่ต้องมีความรู้เกี่ยวกับนิสัยแปลก ๆ ของแพลตฟอร์มอื่น ๆ เพราะทุกอย่างถูกซ่อนอยู่


12

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

สำหรับการใช้ PIMPL สำหรับทุกชั้นฉันจะพิจารณาอย่างรอบคอบเพราะผลประโยชน์เหล่านั้นมาในราคา: จำเป็นต้องมีการอ้อมระดับพิเศษเพื่อเข้าถึงวิธีการใช้งาน


"ต้องมีการอ้อมระดับพิเศษเพื่อเข้าถึงวิธีการใช้งาน" มันคือ?
xaxxon

@xaxxon ใช่มันเป็น pimpl ช้าลงหากวิธีการอยู่ในระดับต่ำ อย่าใช้เพื่อสิ่งที่มีชีวิตอยู่ในวงแคบเช่น
Erik Aronesty

@xaxxon ฉันจะพูดในกรณีทั่วไปต้องมีระดับพิเศษ หากดำเนินการ inline กว่าไม่ แต่ inlinning จะไม่เป็นตัวเลือกในรหัสที่รวบรวมใน dll ที่แตกต่างกัน
Ghita

5

ฉันคิดว่านี่เป็นหนึ่งในเครื่องมือพื้นฐานที่สุดสำหรับการแยกชิ้นส่วน

ฉันใช้ pimpl (และสำนวนอื่น ๆ อีกมากมายจากเจ๋ง C ++) ในโครงการฝังตัว (SetTopBox)

วัตถุประสงค์เฉพาะของ idoim นี้ในโครงการของเราคือการซ่อนประเภท XImpl class ที่ใช้ โดยเฉพาะเราใช้มันเพื่อซ่อนรายละเอียดของการใช้งานสำหรับฮาร์ดแวร์ที่แตกต่างกันซึ่งส่วนหัวที่แตกต่างกันจะถูกดึงเข้าเรามีการใช้งานที่แตกต่างกันของคลาส XImpl สำหรับแพลตฟอร์มหนึ่งและแตกต่างกันสำหรับอื่น ๆ เลย์เอาต์ของคลาส X ยังคงเหมือนเดิมโดยไม่คำนึงถึง platfrom


4

ฉันเคยใช้เทคนิคนี้มากในอดีต แต่ก็พบว่าตัวเองขยับออกห่างจากมัน

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

ข้อดีของ pImpl คือ:

  1. สมมติว่ามีเพียงหนึ่งการใช้งานของอินเทอร์เฟซนี้จะชัดเจนโดยไม่ใช้นามธรรมระดับ / คอนกรีตใช้งาน

  2. หากคุณมีชุดของคลาส (โมดูล) ซึ่งหลายคลาสเข้าถึง "impl" เดียวกัน แต่ผู้ใช้ของโมดูลจะใช้คลาส "ที่เปิดเผย" เท่านั้น

  3. ไม่มี v-table หากนี่ถือว่าเป็นสิ่งที่ไม่ดี

ข้อเสียที่ฉันพบของ pImpl (ที่ส่วนต่อประสานนามธรรมทำงานได้ดีกว่า)

  1. ในขณะที่คุณอาจใช้งาน "การผลิต" เพียงครั้งเดียวโดยใช้อินเทอร์เฟซแบบนามธรรมคุณยังสามารถสร้างการใช้งาน "จำลอง" ที่ทำงานในการทดสอบหน่วย

  2. (ปัญหาที่ใหญ่ที่สุด) ก่อนถึงวันของ unique_ptr และการย้ายคุณมีตัวเลือกที่ จำกัด ว่าจะเก็บ pImpl อย่างไร ตัวชี้แบบดิบและคุณมีปัญหาเกี่ยวกับชั้นเรียนของคุณไม่สามารถคัดลอกได้ auto_ptr เก่าจะไม่ทำงานกับคลาสที่ประกาศล่วงหน้า (ไม่ใช่คอมไพเลอร์ทั้งหมด) ดังนั้นผู้คนเริ่มใช้ shared_ptr ซึ่งดีในการทำให้คลาสของคุณสามารถคัดลอกได้ แต่แน่นอนว่าทั้งสองสำเนานั้นมี shared_ptr พื้นฐานที่เหมือนกันซึ่งคุณอาจไม่ได้คาดหวังไว้ (แก้ไขอย่างใดอย่างหนึ่ง ดังนั้นวิธีการแก้ปัญหามักจะใช้ตัวชี้แบบดิบสำหรับด้านในและทำให้คลาสไม่สามารถคัดลอกได้และส่งคืน shared_ptr ไปที่นั้นแทน ดังนั้นสองสายใหม่ (อันที่จริงให้ 3 อันเก่า shared_ptr ให้อีกอันที่สอง)

  3. ในทางเทคนิคแล้วการแก้ไขไม่ถูกต้องเนื่องจากความไม่ได้แพร่กระจายไปยังตัวชี้ของสมาชิก

โดยทั่วไปแล้วฉันได้ย้ายไปหลายปีจาก pImpl และเป็นการใช้อินเทอร์เฟซแบบนามธรรมแทน (และวิธีการจากโรงงานเพื่อสร้างอินสแตนซ์)


3

ดังที่หลายคนกล่าวว่า Pimpl idiom ช่วยให้สามารถซ่อนข้อมูลได้อย่างสมบูรณ์และรวบรวมความเป็นอิสระ แต่น่าเสียดายที่ต้นทุนการสูญเสียประสิทธิภาพ (การชี้ทางชี้เพิ่มเติม) และความต้องการหน่วยความจำเพิ่มเติม (ตัวชี้สมาชิก) ค่าใช้จ่ายเพิ่มเติมอาจมีความสำคัญในการพัฒนาซอฟต์แวร์ฝังตัวโดยเฉพาะอย่างยิ่งในสถานการณ์ที่หน่วยความจำจะต้องประหยัดมากที่สุด การใช้คลาสนามธรรม C ++ เป็นอินเตอร์เฟสจะนำไปสู่ผลประโยชน์ที่เหมือนกันในราคาเดียวกัน สิ่งนี้แสดงให้เห็นถึงการขาด C ++ อย่างใหญ่หลวงโดยที่ไม่ต้องเกิดขึ้นกับอินเทอร์เฟซ C-like (วิธีการทั่วโลกที่มีตัวชี้ทึบแสงเป็นพารามิเตอร์) มันเป็นไปไม่ได้ที่จะซ่อนข้อมูลจริงและรวบรวมความเป็นอิสระ ประกาศคลาสซึ่งต้องรวมโดยผู้ใช้


3

นี่เป็นสถานการณ์จริงที่ฉันพบซึ่งสำนวนนี้ช่วยได้มาก ฉันเพิ่งตัดสินใจที่จะสนับสนุน DirectX 11 เช่นเดียวกับการสนับสนุน DirectX 9 ที่มีอยู่ในเอ็นจิ้นเกม เอ็นจิ้นห่อคุณสมบัติ DX ส่วนใหญ่ไปแล้วดังนั้นจึงไม่มีการใช้อินเตอร์เฟส DX โดยตรง พวกเขาถูกกำหนดไว้ในส่วนหัวในฐานะสมาชิกส่วนตัว เอ็นจิ้นใช้ DLLs เป็นส่วนขยายเพิ่มการสนับสนุนคีย์บอร์ดเม้าส์จอยสติกและการสนับสนุนการเขียนสคริปต์ ในขณะที่ DLLs ส่วนใหญ่ไม่ได้ใช้ DX โดยตรงพวกเขาต้องการความรู้และการเชื่อมโยงไปยัง DX เพียงเพราะพวกเขาดึงส่วนหัวที่สัมผัส DX ในการเพิ่ม DX 11 ความซับซ้อนนี้เพิ่มขึ้นอย่างมาก แต่ไม่จำเป็น การย้ายสมาชิก DX ไปยัง Pimpl ที่กำหนดไว้เฉพาะในแหล่งที่มากำจัดการจัดวาง นอกจากการลดการพึ่งพาห้องสมุดแล้ว


2

มันถูกใช้ในทางปฏิบัติในหลายโครงการ มันมีประโยชน์มากขึ้นอยู่กับประเภทของโครงการ อีกหนึ่งโครงการที่มีความโดดเด่นมากกว่านี้คือQtโดยที่ความคิดพื้นฐานคือการซ่อนการใช้งานหรือโค้ดเฉพาะของแพลตฟอร์มจากผู้ใช้ (นักพัฒนาอื่น ๆ ที่ใช้ Qt)

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

ดังนั้นในการตัดสินใจออกแบบเกือบทั้งหมดจึงมีข้อดีข้อเสีย


9
มันโง่ แต่ถูกพิมพ์ ... ทำไมคุณไม่สามารถติดตามรหัสในโปรแกรมดีบั๊กได้?
UncleZeiv

2
โดยทั่วไปหากต้องการแก้ไขข้อบกพร่องในรหัส Qt คุณต้องสร้าง Qt ด้วยตนเอง เมื่อคุณทำแล้วจะไม่มีปัญหาในการเข้าสู่วิธี PIMPL และตรวจสอบเนื้อหาของข้อมูล PIMPL
Reinstate Monica

0

ประโยชน์อย่างหนึ่งที่ฉันเห็นคือมันช่วยให้โปรแกรมเมอร์สามารถดำเนินการบางอย่างได้อย่างรวดเร็ว:

X( X && move_semantics_are_cool ) : pImpl(NULL) {
    this->swap(move_semantics_are_cool);
}
X& swap( X& rhs ) {
    std::swap( pImpl, rhs.pImpl );
    return *this;
}
X& operator=( X && move_semantics_are_cool ) {
    return this->swap(move_semantics_are_cool);
}
X& operator=( const X& rhs ) {
    X temporary_copy(rhs);
    return this->swap(temporary_copy);
}

PS: ฉันหวังว่าฉันไม่ได้เข้าใจผิดความหมายย้าย

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