เหตุใด C ++ STL จึงขึ้นอยู่กับเทมเพลตเป็นอย่างมาก (และไม่ได้อยู่ใน * อินเตอร์เฟส *)


211

ฉันหมายถึงนอกเหนือจากชื่อหน้าที่ (ไลบรารีเทมเพลตมาตรฐาน) ...

C ++ เริ่มแรกมีจุดประสงค์เพื่อนำเสนอแนวคิดของ OOP ใน C นั่นคือ: คุณสามารถบอกได้ว่าเอนทิตีที่เฉพาะเจาะจงสามารถทำได้และไม่สามารถทำได้ องค์ประกอบของความสามารถบางอย่างยากที่จะอธิบายในลักษณะนี้เนื่องจากปัญหาของการถ่ายทอดทางพันธุกรรมที่หลากหลายและความจริงที่ว่า C ++ รองรับแนวคิดของอินเตอร์เฟสในทางที่ค่อนข้างงุ่มง่าม (เทียบกับ java และอื่น ๆ ) แต่มันมี (และอาจเป็น การปรับปรุง)

จากนั้นเทมเพลตก็เข้ามาเล่นพร้อมกับ STL STL ดูเหมือนจะใช้แนวคิด OOP แบบดั้งเดิมและล้างลงในท่อระบายน้ำโดยใช้แม่แบบแทน

ควรมีความแตกต่างระหว่างกรณีเมื่อแม่แบบที่ใช้ในการพูดคุยประเภทที่ชุดรูปแบบประเภทที่ไม่เกี่ยวข้องสำหรับการดำเนินงานของแม่แบบ (ภาชนะบรรจุตัวอย่างเช่น) มีvector<int>ความรู้สึกที่สมบูรณ์แบบ

อย่างไรก็ตามในกรณีอื่น ๆ (ตัววนซ้ำและอัลกอริทึม) ประเภทของเทมเพลตควรเป็นไปตาม "แนวคิด" (อินพุต Iterator, Forward Iterator เป็นต้น ... ) ซึ่งรายละเอียดที่แท้จริงของแนวคิดถูกกำหนดโดยการนำเทมเพลตมาใช้ทั้งหมด ฟังก์ชั่น / ชั้นเรียนและไม่ตามระดับของประเภทที่ใช้กับแม่แบบซึ่งค่อนข้างต่อต้านการใช้งานของ OOP

ตัวอย่างเช่นคุณสามารถบอกฟังก์ชั่น:

void MyFunc(ForwardIterator<...> *I);

อัปเดต:เนื่องจากยังไม่ชัดเจนในคำถามต้นฉบับ ForwardIterator ก็โอเคที่จะ templated ตัวเองเพื่ออนุญาต ForwardIterator ทุกประเภท ตรงกันข้ามมี ForwardIterator เป็นแนวคิด

คาดว่าจะส่งต่อ Iterator โดยดูที่คำจำกัดความของมันซึ่งคุณจะต้องดูที่การใช้งานหรือเอกสารประกอบสำหรับ:

template <typename Type> void MyFunc(Type *I);

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

อย่างไรก็ตามฉันกำลังมองหาเหตุผลที่ลึกซึ้งยิ่งขึ้นว่าทำไมการละทิ้ง OOP แบบคลาสสิกเพื่อสร้างเท็มเพลตสำหรับ STL (สมมติว่าคุณอ่านจนถึงตอนนี้: P)


4
คุณอาจจะตรวจสอบstackoverflow.com/questions/31693/... คำตอบที่ได้รับการยอมรับเป็นคำอธิบายที่ยอดเยี่ยมเกี่ยวกับสิ่งที่แม่แบบให้คุณมากกว่ายาสามัญ
James McMahon

6
@ Jonas: นั่นไม่สมเหตุสมผล ข้อ จำกัด ของแคชจะทำให้วงจรสัญญาณนาฬิกามีค่าซึ่งเป็นสาเหตุที่สำคัญ ในตอนท้ายของวันมันเป็นวงจรนาฬิกาไม่ใช่แคชที่กำหนดประสิทธิภาพ หน่วยความจำและแคชมีความสำคัญในขณะที่มันมีผลต่อวงจรนาฬิกาที่ใช้ไป นอกจากนี้การทดสอบสามารถทำได้อย่างง่ายดาย เปรียบเทียบพูด std :: for_Each เรียกว่าด้วยอาร์กิวเมนต์ functor ด้วยวิธีการเทียบเท่า OOP / vtable ความแตกต่างในประสิทธิภาพการทำงานจะส่าย เหตุใดจึงใช้เทมเพลตเวอร์ชัน
jalf

7
และไม่มีเหตุผลว่าทำไมรหัสซ้ำซ้อนจะเติม icache ให้เต็ม หากฉันสร้างอินสแตนซ์ vector <char> และ vector <int> ในโปรแกรมของฉันเหตุใดรหัสเวกเตอร์ <char> จึงควรโหลดลงใน icache ในขณะที่ฉันกำลังประมวลผล vector <int> ความจริงแล้วโค้ดสำหรับ vector <int> นั้นถูกตัดออกเนื่องจากไม่จำเป็นต้องมีรหัสสำหรับการแคสต์ vtables และการอ้อม
jalf

3
Alex Stepanov อธิบายว่าทำไมมรดกและความเท่าเทียมกันเล่นได้ไม่ดี
fredoverflow

6
@BerndJendrissek: เอ่อปิด แต่ไม่มีตัวเอง ใช่รหัสมากขึ้นค่าใช้จ่ายในแง่ของแบนด์วิดธ์หน่วยความจำและการใช้งานแคชถ้ามันจะเคยใช้จริง แต่ไม่มีเหตุผลใดที่จะคาดหวังvector<int>และvector<char>ใช้งานในเวลาเดียวกัน พวกเขาอาจจะแน่ใจ แต่คุณอาจใช้ใด ๆสองชิ้นของรหัสในเวลาเดียวกัน ไม่เกี่ยวข้องกับเทมเพลต C ++ หรือ STL ไม่มีสิ่งใดในการสร้างอินสแตนซ์vector<int>ซึ่งต้องการให้vector<char>โค้ดโหลดหรือเรียกใช้งาน
jalf

คำตอบ:


607

คำตอบสั้น ๆ คือ "เพราะ C ++ ได้ย้าย" ใช่ย้อนกลับไปในช่วงปลายยุค 70 Stroustrup ตั้งใจสร้าง C ที่อัพเกรดด้วยความสามารถของ OOP แต่นั่นเป็นเวลานานแล้ว เมื่อถึงเวลาที่ภาษาได้มาตรฐานในปี 1998 มันไม่ใช่ภาษา OOP อีกต่อไป มันเป็นภาษาแบบหลายกระบวนทัศน์ แน่นอนว่ามันมีการสนับสนุนโค้ด OOP แต่ก็มีภาษาเทมเพลตที่สมบูรณ์แบบซึ่งซ้อนทับกันมันอนุญาต metaprogramming เวลารวบรวมและผู้คนค้นพบโปรแกรมทั่วไป ทันใดนั้น OOP ก็ไม่ได้ดูเหมือนว่าสำคัญทั้งหมด ไม่ใช่เมื่อเราสามารถเขียนโค้ดที่ง่ายกว่ากระชับและมีประสิทธิภาพยิ่งขึ้นโดยใช้เทคนิคที่มีให้ผ่านเทมเพลตและการเขียนโปรแกรมทั่วไป

OOP ไม่ใช่จอกศักดิ์สิทธิ์ มันเป็นความคิดที่น่ารักและมันก็ค่อนข้างได้รับการพัฒนามากกว่าภาษาขั้นตอนในยุค 70 เมื่อมันถูกประดิษฐ์ขึ้นมา แต่จริงๆแล้วไม่ใช่ทั้งหมดที่มันจะแตกหัก ในหลายกรณีมันเป็นเงอะงะและ verbose และมันก็ไม่ได้ส่งเสริมการใช้ซ้ำรหัสหรือ modularity

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

ลองวาดกราฟการพึ่งพาของ STL "OOP-ified" สมมุติฐาน มีกี่คลาสที่ต้องรู้เกี่ยวกับกันและกัน? จะมีจำนวนมากการอ้างอิง คุณจะสามารถรวมเฉพาะvectorส่วนหัวโดยไม่ดึงiteratorหรือiostreamดึงเข้าด้วยหรือไม่ STL ทำให้ง่ายขึ้น เวกเตอร์รู้เกี่ยวกับประเภทตัววนซ้ำที่กำหนดและนั่นคือทั้งหมด อัลกอริทึม STL ไม่รู้อะไรเลย พวกเขาไม่จำเป็นต้องรวมส่วนหัวตัววนซ้ำแม้ว่าพวกเขาทั้งหมดจะยอมรับตัววนซ้ำเป็นพารามิเตอร์ก็ตาม โมดูลแบบไหนมากกว่ากัน?

STL อาจไม่ปฏิบัติตามกฎของ OOP เนื่องจาก Java กำหนดไว้ แต่ไม่สามารถบรรลุเป้าหมายของ OOP ได้หรือไม่ มันไม่สามารถใช้ซ้ำได้การเชื่อมต่อต่ำแบบแยกส่วนและการห่อหุ้มหรือไม่?

และมันจะไม่บรรลุเป้าหมายเหล่านี้ดีกว่า OOP-ified หรือไม่?

สำหรับสาเหตุที่ STL ถูกนำไปใช้กับภาษามีหลายสิ่งที่เกิดขึ้นซึ่งนำไปสู่ ​​STL

ประการแรกแม่แบบถูกเพิ่มใน C ++ พวกเขาถูกเพิ่มเข้ามาด้วยเหตุผลเดียวกับที่เพิ่ม generics เข้าไปใน. NET มันเป็นความคิดที่ดีที่จะสามารถเขียนสิ่งต่าง ๆ เช่น "ภาชนะบรรจุประเภท T" โดยไม่ทิ้งความปลอดภัยประเภท แน่นอนว่าการติดตั้งใช้งานนั้นค่อนข้างซับซ้อนและทรงพลังมากขึ้น

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

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

มันไม่ใช่ตัวเลือกเชิงอุดมการณ์ไม่ใช่ตัวเลือกทางการเมืองของ "เราต้องการเป็น OOP หรือไม่" แต่เป็นทางเลือกที่จริงจัง พวกเขาประเมินห้องสมุดและเห็นว่ามันทำงานได้ดีมาก

ไม่ว่าในกรณีใดเหตุผลที่คุณพูดถึงความนิยม STL นั้นเป็นสิ่งจำเป็นอย่างยิ่ง

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

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

คำถามของคุณมีสมมติฐานที่ดีว่า OOP นั้น "ดีที่สุด" ฉันอยากรู้ว่าทำไม คุณถามว่าทำไมพวกเขา "ละทิ้ง OOP คลาสสิก" ฉันสงสัยว่าทำไมพวกเขาควรติดอยู่กับมัน มันจะมีข้อดีอะไรบ้าง


22
เป็นการเขียนที่ดี แต่ฉันต้องการเน้นรายละเอียดหนึ่งอย่าง STL ไม่ใช่ "ผลิตภัณฑ์" ของ C ++ ในความเป็นจริง STL เป็นแนวคิดมีอยู่ก่อน C ++ และ C ++ เกิดขึ้นเป็นภาษาที่มีประสิทธิภาพมี (เกือบ) พลังเพียงพอสำหรับการเขียนโปรแกรมทั่วไปดังนั้น STL จึงเขียนใน C ++
Igor Krivokon

17
เนื่องจากความคิดเห็นยังคงนำมาใช้ต่อไปฉันจึงทราบว่าชื่อ STL นั้นคลุมเครือ แต่ฉันไม่คิดว่าชื่อที่ดีกว่าสำหรับ "ส่วนของไลบรารีมาตรฐาน C ++ ที่เป็นแบบจำลองใน STL" ชื่อจริงสำหรับส่วนนั้นของไลบรารีมาตรฐานนั้นเป็นเพียง "the STL" แม้ว่าจะไม่ถูกต้องอย่างเคร่งครัด :) ตราบใดที่ผู้คนไม่ใช้ STL เป็นชื่อสำหรับไลบรารีมาตรฐานทั้งหมด (รวมถึง IOStreams และส่วนหัว C stdlib) ฉันมีความสุข :)
jalf

5
@einpoklum และสิ่งที่คุณจะได้รับจากชั้นฐานนามธรรม? ใช้std::setเป็นตัวอย่าง มันไม่ได้สืบทอดจากคลาสฐานที่เป็นนามธรรม คุณ จำกัด การใช้งานของstd::setคุณอย่างไร มีอะไรที่คุณไม่สามารถทำได้std::setเพราะมันไม่ได้สืบทอดมาจากคลาสฐานที่เป็นนามธรรมหรือไม่?
fredoverflow

22
@ einpoklum โปรดดูภาษา Smalltalk ซึ่ง Alan Kay ได้รับการออกแบบให้เป็นภาษา OOP เมื่อเขาคิดค้น OOP มันไม่มีอินเตอร์เฟส OOP ไม่เกี่ยวกับอินเทอร์เฟซหรือคลาสพื้นฐานที่เป็นนามธรรม คุณจะบอกว่า "Java ซึ่งไม่เหมือนสิ่งที่นักประดิษฐ์ของคำว่า OOP มีอยู่ในใจคือ OOP มากกว่า C ++ ซึ่งก็ไม่เหมือนสิ่งที่ผู้ประดิษฐ์ของคำว่า OOP มีอยู่ในใจ"? สิ่งที่คุณหมายถึงคือ "C ++ ไม่เหมือน Java สำหรับรสนิยมของฉัน" มันยุติธรรม แต่ไม่มีอะไรเกี่ยวข้องกับ OOP
jalf

8
@MasonWheeler ถ้าคำตอบนี้เป็นเรื่องไร้สาระที่เห็นได้ชัดคุณจะไม่เห็นนักพัฒนาหลายร้อยคนทั่วโลกโหวต +1 เรื่องนี้โดยมีเพียงสามคนเท่านั้นที่ทำได้
แพนด้า -34

88

คำตอบที่ตรงที่สุดกับสิ่งที่ฉันคิดว่าคุณถาม / บ่นคือ: ข้อสันนิษฐานว่า C ++ เป็นภาษา OOP เป็นข้อสันนิษฐานที่ผิด

C ++ เป็นภาษาแบบหลายกระบวนทัศน์ สามารถตั้งโปรแกรมโดยใช้หลักการ OOP สามารถตั้งโปรแกรมขั้นตอนสามารถตั้งโปรแกรมโดยทั่วไป (เทมเพลต) และด้วย C ++ 11 (เดิมชื่อ C ++ 0x) บางสิ่งสามารถตั้งโปรแกรมได้ตามหน้าที่

ผู้ออกแบบ C ++ เห็นว่านี่เป็นข้อได้เปรียบดังนั้นพวกเขาจึงโต้แย้งว่าการบังคับให้ C ++ ทำตัวเหมือนภาษา OOP ล้วนๆเมื่อการเขียนโปรแกรมทั่วไปแก้ปัญหาได้ดีกว่าและดีกว่าโดยทั่วไปแล้ว


4
"และด้วย C ++ 0x บางสิ่งก็สามารถตั้งโปรแกรมได้ตามหน้าที่" - สามารถตั้งโปรแกรมให้ทำงานได้โดยไม่ต้องใช้คุณสมบัติเหล่านั้น
Jonas Kölker

3
@Tyler แน่นอนถ้าคุณบังคับ C ++ ให้เป็น OOP บริสุทธิ์คุณจะต้องถูกทิ้งไว้กับ Objective-C
Justicle

@TylerMcHenry: เมื่อถามถึงสิ่งนี้ฉันพบว่าฉันเพิ่งตอบคำตอบเดียวกับคุณ! แค่จุดเดียว ฉันหวังว่าคุณจะเพิ่มความจริงที่ว่า Standard Library ไม่สามารถใช้ในการเขียนโค้ดเชิงวัตถุได้
einpoklum

74

ความเข้าใจของฉันคือ Stroustrup ชอบการออกแบบคอนเทนเนอร์แบบ "OOP-style" และในความเป็นจริงไม่เห็นวิธีอื่นที่จะทำ Alexander Stepanov เป็นผู้รับผิดชอบ STL และเป้าหมายของเขาไม่รวมถึง "ทำให้เป็นวัตถุที่มุ่งเน้น" :

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

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

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

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

เมื่อ Stepanov นำเสนอห้องสมุดของเขาไปที่ Stroustrup Stroustrup และคนอื่น ๆ ก็ต้องพยายามอย่างมากเพื่อให้ได้มาตรฐาน ISO C ++ (การสัมภาษณ์เดียวกัน):

การสนับสนุนของ Bjarne Stroustrup นั้นสำคัญมาก Bjarne ต้องการ STL ตามมาตรฐานจริง ๆ และถ้า Bjarne ต้องการบางสิ่งเขาก็จะได้รับมัน ... เขายังบังคับให้ฉันทำการเปลี่ยนแปลงใน STL ที่ฉันจะไม่ทำเพื่อคนอื่น ... เขาเป็นคนที่มีความคิดเดียวที่สุดที่ฉันรู้จัก เขาทำสิ่งต่าง ๆ เสร็จแล้ว เขาใช้เวลาสักครู่เพื่อทำความเข้าใจว่า STL เกี่ยวกับอะไร แต่เมื่อเขาทำเขาก็พร้อมที่จะผลักดันผ่าน นอกจากนี้เขายังสนับสนุนให้ STL โดยยืนขึ้นสำหรับมุมมองที่มากกว่าหนึ่งวิธีของการเขียนโปรแกรมที่ถูกต้อง - กับไม่มีจุดสิ้นสุดสะเก็ดระเบิดและ hype มานานกว่าทศวรรษและติดตามการรวมกันของความยืดหยุ่นประสิทธิภาพการบรรทุกเกินพิกัดและความปลอดภัยในประเภท แม่แบบที่ทำให้ STL เป็นไปได้ ฉันต้องการระบุอย่างชัดเจนว่า Bjarne เป็นผู้ออกแบบภาษาที่โดดเด่นในรุ่นของฉัน


2
บทสัมภาษณ์ที่น่าสนใจ ค่อนข้างแน่ใจว่าฉันได้อ่านมันมาก่อนหน้านี้แล้ว แต่ก็คุ้มค่าที่จะต้องผ่านอีกครั้ง :)
2552

3
หนึ่งในบทสัมภาษณ์ที่น่าสนใจที่สุดเกี่ยวกับการเขียนโปรแกรมที่ฉันเคยอ่าน แม้ว่ามันใบฉันกระหายเพื่อดูรายละเอียดเพิ่มเติม ...
Felixyz

ข้อร้องเรียนจำนวนมากที่เขาทำเกี่ยวกับภาษาเช่น Java ("คุณไม่สามารถเขียน max ทั่วไป () ใน Java ที่ใช้สองข้อโต้แย้งบางประเภทและมีค่าส่งคืนประเภทเดียวกัน") มีความเกี่ยวข้องกับรุ่นแรก ๆ เท่านั้น ของภาษาก่อนที่จะเพิ่ม generics แม้จากจุดเริ่มต้นเป็นที่รู้กันว่าในที่สุดจะมีการเพิ่ม generics แม้ว่า (ครั้งหนึ่งเคยเป็นไวยากรณ์ / ความหมายถูกปฏิบัติได้คิดออก) ดังนั้นการวิพากษ์วิจารณ์ของเขาส่วนใหญ่ไม่มีมูลความจริง ใช่ข้อมูลทั่วไปในบางรูปแบบมีความจำเป็นเพื่อรักษาความปลอดภัยของประเภทในภาษาที่พิมพ์แบบคงที่ แต่ไม่ว่าจะไม่ทำให้ OO ไร้ค่า
ผู้ชายบางคน

1
@SomeGuy พวกเขาไม่ได้ร้องเรียนเกี่ยวกับ Java ต่อ se เขากำลังพูดถึงการเขียนโปรแกรม OO "มาตรฐาน" ของ SmallTalk หรือพูด Java " การสัมภาษณ์มาจากช่วงปลายยุค 90 (เขากล่าวถึงการทำงานที่ SGI ซึ่งเขาทิ้งไว้ในปี 2000 เพื่อทำงานที่ AT&T) Generics ถูกเพิ่มเข้ามากับ Java ในปี 2004 ในรุ่น 1.5 เท่านั้นและเป็นส่วนเบี่ยงเบนจากโมเดล OO "มาตรฐาน"
melpomene

24

คำตอบอยู่ในการสัมภาษณ์ครั้งนี้กับ Stepanov ผู้เขียน STL:

ใช่. STL ไม่ได้มุ่งเน้นวัตถุ ฉันคิดว่าการวางวัตถุนั้นเป็นเรื่องหลอกลวงมากพอ ๆ กับปัญญาประดิษฐ์ ฉันยังไม่เห็นโค้ดที่น่าสนใจที่มาจากคน OO เหล่านี้


อัญมณีที่ดี; คุณรู้หรือไม่ว่าเป็นปีอะไร
Kos

2
@Kos ตามweb.archive.org/web/20000607205939/http://www.stlport.org/…รุ่นแรกของหน้าที่เชื่อมโยงมาจาก 7 มิถุนายน 2544 หน้านั้นอยู่ด้านล่างสุดพูดว่าลิขสิทธิ์ 2001- 2008
alfC

@Kos Stepanov กล่าวถึงการทำงานที่ SGI ในคำตอบแรก เขาออกจาก SGI ในเดือนพฤษภาคม 2543 ดังนั้นการสัมภาษณ์นั้นเก่ากว่านั้น
melpomene

18

ทำไมการออกแบบ OOP บริสุทธิ์ให้กับ Data Structure & Algorithms Library จะดีกว่า! OOP ไม่ใช่ทางออกสำหรับทุกสิ่ง

IMHO, STL เป็นห้องสมุดที่สวยที่สุดที่ฉันเคยเห็น :)

สำหรับคำถามของคุณ

คุณไม่ต้องการ polymorphism แบบรันไทม์มันเป็นข้อดีสำหรับ STL จริง ๆ แล้วในการใช้งาน Library โดยใช้ static polymorphism ซึ่งหมายถึงประสิทธิภาพ ลองเขียน Sort หรือ Distance แบบทั่วไปหรืออัลกอริธึมที่ใช้กับคอนเทนเนอร์ทั้งหมด! Sort ใน Java ของคุณจะเรียกใช้ฟังก์ชันที่ไดนามิกผ่าน n-levels ที่จะดำเนินการ!

คุณต้องการสิ่งที่โง่เช่น Boxing และ Unboxing เพื่อซ่อนสมมติฐานที่น่ารังเกียจของภาษา Pure OOP ที่เรียกว่า

ปัญหาเดียวที่ฉันเห็นกับ STL และแม่แบบโดยทั่วไปคือข้อความแสดงข้อผิดพลาดอันยิ่งใหญ่ ซึ่งจะแก้ไขได้โดยใช้ Concepts ใน C ++ 0X

การเปรียบเทียบ STL กับชุดสะสมใน Java เปรียบเสมือนการเปรียบเทียบทัชมาฮาลกับบ้านของฉัน :)


12
อะไรทัชมาฮาลมีขนาดเล็กและสง่างามและบ้านของคุณมีขนาดเท่าภูเขาและเป็นระเบียบ ;)
2552

แนวคิดไม่ใช่ส่วนหนึ่งของ c ++ 0x อีกต่อไป ข้อความแสดงข้อผิดพลาดบางอย่างอาจถูกจองไว้ล่วงหน้าโดยใช้static_assertบางที
KitsuneYMG

GCC 4.6 ได้รับการปรับปรุงข้อความแสดงข้อผิดพลาดของแม่แบบและฉันเชื่อว่า 4.7+ นั้นดีขึ้นกว่าเดิม
David Stone

แนวคิดนั้นเป็น "อินเทอร์เฟซ" ที่ OP ต้องการ ความแตกต่างเพียงอย่างเดียวคือ "การสืบทอด" จากแนวคิดนั้นเป็นนัย (ถ้าคลาสมีฟังก์ชันสมาชิกที่ถูกต้องทั้งหมดมันจะเป็นประเภทย่อยของแนวคิดโดยอัตโนมัติ) แทนที่จะชัดเจน (คลาส Java ต้องประกาศอย่างชัดเจนว่าใช้อินเทอร์เฟซ) . อย่างไรก็ตามการพิมพ์ย่อยทั้งโดยนัยและชัดเจนเป็น OO ที่ถูกต้องและภาษา OO บางภาษามีการสืบทอดโดยนัยซึ่งทำงานเช่นเดียวกับแนวคิด ดังนั้นสิ่งที่ถูกกล่าวถึงในที่นี้คือ "OO sucks: ใช้เท็มเพลต แต่เท็มเพลตมีปัญหาดังนั้นใช้ Concepts (ซึ่งเป็น OO)"
ผู้ชายบางคน

11

ประเภทเทมเพลตควรเป็นไปตาม "แนวคิด" (อินพุต Iterator, Forward Iterator, ฯลฯ ... ) ซึ่งรายละเอียดที่แท้จริงของแนวคิดจะถูกกำหนดโดยการใช้งานฟังก์ชั่นเทมเพลต / คลาสและไม่ใช่คลาสของประเภท ใช้กับเทมเพลตซึ่งค่อนข้างต่อต้านการใช้งานของ OOP

ฉันคิดว่าคุณเข้าใจผิดเกี่ยวกับการใช้แนวคิดโดยเทมเพลต ยกตัวอย่างเช่น Iterator ไปข้างหน้าเป็นแนวคิดที่ชัดเจนมาก ในการค้นหานิพจน์ที่ต้องถูกต้องเพื่อให้คลาสเป็น Forward Iterator และความหมายรวมถึงความซับซ้อนของการคำนวณคุณดูมาตรฐานหรือที่http://www.sgi.com/tech/stl/ForwardIterator.html (คุณต้องทำตามลิงก์ไปยัง Input, Output, และ Trivial Iterator เพื่อดูทั้งหมด)

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

ความแตกต่างในวิธีจัดการอินเตอร์เฟสระหว่าง STL และ Java เป็นสามเท่า:

1) STL กำหนดนิพจน์ที่ถูกต้องโดยใช้วัตถุในขณะที่ Java กำหนดวิธีการที่จะต้อง callable บนวัตถุ แน่นอนว่าการแสดงออกที่ถูกต้องอาจเป็นการเรียกใช้เมธอด (ฟังก์ชันสมาชิก) แต่ไม่จำเป็นต้องเป็น

2) ส่วนต่อประสาน Java เป็นวัตถุรันไทม์ในขณะที่แนวคิด STL ไม่สามารถมองเห็นได้ที่รันไทม์แม้จะมี RTTI

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

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

ใน C ++ เหมือนกับใน Python ทั้งซีแมนทิกส์และไวยากรณ์นั้นมีความหมายถึงแม้ว่าใน C ++ (และใน Python ถ้าคุณได้รับพรีโพรเซสเซอร์ที่พิมพ์อย่างแรง) คุณจะได้รับความช่วยเหลือจากคอมไพเลอร์ หากโปรแกรมเมอร์ต้องการการประกาศอินเทอร์เฟซที่เหมือน Java อย่างชัดเจนโดยการนำคลาสไปใช้งานวิธีการมาตรฐานคือการใช้ลักษณะของประเภท (และการสืบทอดหลายอย่างสามารถป้องกันไม่ให้สิ่งนี้เกินไป) สิ่งที่ขาดไปเมื่อเทียบกับ Java เป็นเทมเพลตเดียวที่ฉันสามารถสร้างอินสแตนซ์กับประเภทของฉันได้และจะรวบรวมถ้าหากนิพจน์ที่จำเป็นทั้งหมดนั้นถูกต้องสำหรับประเภทของฉัน สิ่งนี้จะบอกฉันว่าฉันได้ติดตั้งบิตที่จำเป็นทั้งหมดหรือไม่ "ก่อนที่ฉันจะใช้งาน" นั่นคือความสะดวกสบาย แต่มันไม่ได้เป็นแกนหลักของ OOP (และยังไม่ได้ทดสอบความหมาย

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

คุณสามารถบอกฟังก์ชั่น ... คาดว่าจะส่งต่อ Iterator เพียงโดยดูที่คำจำกัดความของที่คุณต้องการดูการใช้งานหรือเอกสารประกอบสำหรับ ...

ส่วนตัวผมคิดว่าประเภทโดยนัยเป็นจุดแข็งเมื่อใช้อย่างเหมาะสม อัลกอริธึมบอกว่ามันทำอะไรกับพารามิเตอร์เทมเพลตและตัวดำเนินการทำให้แน่ใจว่าสิ่งเหล่านั้นทำงานได้: มันเป็นตัวหารทั่วไปของสิ่งที่ "อินเตอร์เฟส" ควรทำ นอกจากนี้ด้วย STL คุณไม่น่าจะใช้พูดโดยstd::copyอิงจากการค้นหาการประกาศล่วงหน้าในไฟล์ส่วนหัว โปรแกรมเมอร์ควรทำงานตามที่ฟังก์ชั่นใช้ตามเอกสารไม่ใช่เพียงแค่ฟังก์ชั่นลายเซ็น สิ่งนี้เป็นจริงใน C ++, Python หรือ Java มีข้อ จำกัด ในสิ่งที่สามารถทำได้ด้วยการพิมพ์ในภาษาใด ๆ และพยายามใช้การพิมพ์เพื่อทำสิ่งที่ไม่ได้ทำ (ตรวจสอบความหมาย) จะเป็นข้อผิดพลาด

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

นี่คือ Bjarne ในอินเทอร์เฟซที่ประกาศอย่างชัดเจน: http://www.artima.com/cppsource/cpp0xP.html

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

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


บนอินเทอร์เฟซที่ประกาศไว้อย่างชัดเจนสองคำ: class classes (ซึ่งเป็นสิ่งที่ Stepanov หมายถึงโดย "แนวคิด".)
pyon

"หากคุณไม่สามารถตรวจสอบนิพจน์ที่ถูกต้องที่ต้องการสำหรับแนวคิด STL คุณจะได้รับข้อผิดพลาดในการรวบรวมที่ไม่ระบุเมื่อคุณสร้างอินสแตนซ์ของเทมเพลตบางประเภท" - นั่นเป็นเท็จ การส่งผ่านบางสิ่งไปยังstdไลบรารีที่ล้มเหลวในการจับคู่แนวคิดมักเป็น "รูปแบบไม่ดีไม่จำเป็นต้องวินิจฉัย"
Yakk - Adam Nevraumont

จริงฉันเล่นเร็วและหลวมด้วยคำว่า "ถูกต้อง" ฉันแค่ตั้งใจว่าคอมไพเลอร์ไม่สามารถรวบรวมหนึ่งในนิพจน์ที่ต้องการจากนั้นมันจะรายงานบางสิ่งบางอย่าง
Steve Jessop

8

"OOP สำหรับฉันหมายถึงเฉพาะการส่งข้อความการเก็บรักษาในท้องถิ่นและการป้องกันและการซ่อนกระบวนการของรัฐและการผูกมัดปลายสุดของทุกสิ่งมันสามารถทำได้ใน Smalltalk และ LISP อาจมีระบบอื่น ๆ ที่เป็นไปได้ แต่ ฉันไม่รู้จักพวกเขา " - Alan Kay ผู้สร้าง Smalltalk

C ++, Java และภาษาอื่น ๆ ส่วนใหญ่นั้นค่อนข้างห่างไกลจาก OOP แบบดั้งเดิม ที่กล่าวว่าการโต้เถียงเพื่ออุดมการณ์ไม่ได้เป็นประโยชน์มาก C ++ นั้นไม่บริสุทธิ์ในทุกแง่มุมดังนั้นมันจึงใช้ฟังก์ชั่นที่ดูเหมือนว่าใช้งานได้จริงในเวลานั้น


7

STL เริ่มต้นด้วยความตั้งใจให้เป็นห้องสมุดขนาดใหญ่ที่ครอบคลุมขั้นตอนวิธีการที่ใช้กันมากที่สุด - มีเป้าหมายของพฤติกรรม consitent และประสิทธิภาพการทำงาน เทมเพลตมาเป็นปัจจัยสำคัญในการทำให้การใช้งานและเป้าหมายเป็นไปได้

เพียงเพื่อให้การอ้างอิงอื่น:

การสัมภาษณ์ของ Al Stevens Alex Stepanov ในเดือนมีนาคม 2538 ของ DDJ:

Stepanov อธิบายประสบการณ์การทำงานและทางเลือกของเขาที่มีต่อห้องสมุดขนาดใหญ่ของอัลกอริทึมซึ่งต่อมาได้พัฒนาเป็น STL

บอกเราบางอย่างเกี่ยวกับความสนใจในระยะยาวของคุณในการเขียนโปรแกรมทั่วไป

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

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


5

ปัญหาพื้นฐานด้วย

void MyFunc(ForwardIterator *I);

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


1
ฉันก็ทำได้เช่นกัน: 1. อย่าพยายามรับมันเพราะฉันกำลังเขียนโค้ดทั่วไป หรือ 2 รับโดยใช้กลไกการสะท้อนสิ่ง C ++ เสนอวันนี้
einpoklum

2

ลองคิดว่าไลบรารีมาตรฐานเป็นฐานข้อมูลของคอลเลกชันและอัลกอริทึม

หากคุณได้ศึกษาประวัติของฐานข้อมูลคุณไม่ต้องสงสัยเลยว่าในตอนแรกฐานข้อมูลส่วนใหญ่เป็น "ลำดับชั้น" ฐานข้อมูลแบบลำดับขั้นมีการติดต่ออย่างใกล้ชิดกับ OOP แบบคลาสสิกโดยเฉพาะความหลากหลายแบบสืบทอดเดี่ยวเช่น Smalltalk ที่ใช้

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

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

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

เหตุผลส่วนใหญ่ที่ฐานข้อมูลเชิงสัมพันธ์เข้ามามีความหลากหลาย แบบจำลองเชิงสัมพันธ์นั้นทำหน้าที่เป็น superset ของโมเดลเครือข่าย (ซึ่งก็คือ superset ของโมเดลลำดับชั้น)

C ++ ส่วนใหญ่ตามเส้นทางเดียวกัน ความสอดคล้องกันระหว่างการสืบทอดเดี่ยวและโมเดลลำดับชั้นและระหว่างการสืบทอดหลายครั้งและโมเดลเครือข่ายค่อนข้างชัดเจน การติดต่อระหว่างแม่แบบ C ++ และโมเดลลำดับชั้นอาจไม่ชัดเจน แต่ก็ค่อนข้างใกล้เคียงกันอยู่แล้ว

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

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


0

คุณเปรียบเทียบกับ ForwardIterator * อย่างไร นั่นคือคุณจะตรวจสอบว่ารายการที่คุณมีคือสิ่งที่คุณกำลังมองหาหรือคุณผ่านมันได้หรือไม่

ส่วนใหญ่ฉันจะใช้สิ่งนี้:

void MyFunc(ForwardIterator<MyType>& i)

ซึ่งหมายความว่าฉันรู้ว่าฉันกำลังชี้ไปที่ประเภทของฉันและฉันรู้วิธีการเปรียบเทียบ แม้ว่ามันจะดูเหมือนเทมเพลต แต่มันก็ไม่ได้จริงๆ (ไม่มีคำหลัก "เทมเพลต")


คุณก็สามารถใช้ <,> = และผู้ประกอบการของประเภทและไม่ทราบว่าสิ่งเหล่านี้จะ (แม้ว่าอาจจะไม่เป็นสิ่งที่คุณหมาย)
lhahne

ขึ้นอยู่กับบริบทเหล่านั้นอาจไม่มีเหตุผลหรืออาจทำงานได้ดี ยากที่จะบอกโดยไม่ทราบเพิ่มเติมเกี่ยวกับ MyType ซึ่งสันนิษฐานว่าผู้ใช้ทำและเราไม่ต้องการ
Tanktalus

0

คำถามนี้มีคำตอบที่ดีมาก ควรกล่าวถึงว่าเทมเพลตรองรับการออกแบบแบบเปิด ด้วยสถานะปัจจุบันของภาษาโปรแกรมเชิงวัตถุเราต้องใช้รูปแบบผู้เยี่ยมชมเมื่อจัดการกับปัญหาดังกล่าวและ OOP ที่แท้จริงควรสนับสนุนการเชื่อมโยงแบบไดนามิกหลายรายการ ดูOpen Multi-Methods สำหรับ C ++, P. Pirkelbauer, et.al สำหรับการอ่านที่น่าสนใจมาก

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

template<class Value,class T>
Value euler_fwd(size_t N,double t_0,double t_end,Value y_0,const T& func)
    {
    auto dt=(t_end-t_0)/N;
    for(size_t k=0;k<N;++k)
        {y_0+=func(t_0 + k*dt,y_0)*dt;}
    return y_0;
    }

โปรดสังเกตว่าฟังก์ชั่นนี้จะใช้งานได้หากValueเป็นเวกเตอร์บางชนิด ( ไม่ใช่ std :: vector ซึ่งควรเรียกใช้std::dynamic_arrayเพื่อหลีกเลี่ยงความสับสน)

หากfuncมีขนาดเล็กฟังก์ชั่นนี้จะได้รับประโยชน์มากมายจากอินไลน์ ตัวอย่างการใช้งาน

auto result=euler_fwd(10000,0.0,1.0,1.0,[](double x,double y)
    {return y;});

ในกรณีนี้คุณควรรู้คำตอบที่แน่นอน (2.718 ... ) แต่มันง่ายที่จะสร้าง ODE แบบง่ายโดยไม่ต้องใช้วิธีแก้ปัญหาเบื้องต้น (คำแนะนำ: ใช้พหุนามใน y)

ตอนนี้คุณมีนิพจน์ขนาดใหญ่funcและคุณใช้ตัวแก้ปัญหา ODE ในหลาย ๆ ที่ดังนั้นไฟล์ปฏิบัติการของคุณจะปนเปื้อนด้วยแม่แบบอินสแตนซ์ทุกที่ จะทำอย่างไร? สิ่งแรกที่ควรสังเกตคือตัวชี้ฟังก์ชันปกติทำงาน จากนั้นคุณต้องการเพิ่มการแกงเพื่อให้คุณเขียนอินเทอร์เฟซและการสร้างอินสแตนซ์ที่ชัดเจน

class OdeFunction
    {
    public:
        virtual double operator()(double t,double y) const=0;
    };

template
double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction& func);

แต่การสร้างอินสแตนซ์ด้านบนใช้งานdoubleได้ทำไมไม่เขียนอินเทอร์เฟซเป็นเทมเพลต:

template<class Value=double>
class OdeFunction
    {
    public:
        virtual Value operator()(double t,const Value& y) const=0;
    };

และเชี่ยวชาญสำหรับบางประเภทค่าทั่วไป:

template double euler_fwd(size_t N,double t_0,double t_end,double y_0,const OdeFunction<double>& func);

template vec4_t<double> euler_fwd(size_t N,double t_0,double t_end,vec4_t<double> y_0,const OdeFunction< vec4_t<double> >& func); // (Native AVX vector with four components)

template vec8_t<float> euler_fwd(size_t N,double t_0,double t_end,vec8_t<float> y_0,const OdeFunction< vec8_t<float> >& func); // (Native AVX vector with 8 components)

template Vector<double> euler_fwd(size_t N,double t_0,double t_end,Vector<double> y_0,const OdeFunction< Vector<double> >& func); // (A N-dimensional real vector, *not* `std::vector`, see above)

หากฟังก์ชั่นได้รับการออกแบบรอบ ๆ อินเทอร์เฟซก่อนคุณจะต้องถูกบังคับให้สืบทอดจาก ABC นั้น ตอนนี้คุณมีตัวเลือกนี้เช่นเดียวกับตัวชี้ฟังก์ชั่นแลมบ์ดาหรือวัตถุฟังก์ชั่นอื่น ๆ กุญแจสำคัญคือเราต้องมีoperator()()และเราต้องสามารถใช้ตัวดำเนินการทางคณิตศาสตร์บางอย่างกับชนิดส่งคืนได้ ดังนั้นเทมเพลตเครื่องจักรจะแตกในกรณีนี้ถ้า C ++ ไม่มีผู้ให้บริการโหลดมากเกินไป


-1

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

จากนั้นจาวาก็มาพร้อมกับตัวรวบรวมขยะและinterfaceคำสำคัญในตัวและในทันใดมันก็เป็นจริงที่จะแยกอินเทอร์เฟซและการใช้งานจริง ก่อนที่คุณจะทราบความคิดนี้กลายเป็นส่วนหนึ่งของ OO C ++, เทมเพลตและ STL ถือกำเนิดสิ่งเหล่านี้ทั้งหมด


ยอมรับว่าอินเตอร์เฟสไม่ใช่แค่ OO แต่ความสามารถที่หลากหลายในระบบประเภทคือ (มันอยู่ใน Simula ในยุค 60) อินเทอร์เฟซของโมดูลมีอยู่ใน Modula-2 และ Ada แต่สิ่งเหล่านี้ทำงานในระบบประเภทที่แตกต่างกันฉันคิดว่า
andygavin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.