เหตุใดจึงไม่มีคลาสพื้นฐานใน C ++


84

จากมุมมองของการออกแบบเหตุใดใน C ++ จึงไม่มีคลาสพื้นฐานทั้งหมดobjectในภาษาอื่น ๆ


27
นี่เป็นคำถามเชิงปรัชญามากกว่าคำถามเกี่ยวกับการออกแบบ คำตอบสั้น ๆ คือ "เพราะ Bjarne ไม่ต้องการทำเช่นนั้น"
Jerry Coffin

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

3
@Martin แต่ลองดูแบบนี้เช่นจนถึง C ++ 0x auto คุณต้องใช้คำจำกัดความแบบยาวไมล์สำหรับตัววนซ้ำหรือครั้งtypedefเดียว ด้วยลำดับชั้นทั่วไปมากขึ้นคุณก็สามารถใช้หรือobject iterator
slezica

9
@Santiago: การใช้ระบบ unified type เกือบตลอดเวลาหมายความว่าคุณต้องใช้รหัสจำนวนมากที่อาศัยความหลากหลายการส่งแบบไดนามิกและ RTTI ซึ่งทั้งหมดนี้มีราคาค่อนข้างแพงและทั้งหมดนี้ขัดขวางการเพิ่มประสิทธิภาพที่เป็นไปได้เมื่อ ไม่ได้ใช้
James McNellis

2
@GWW - ... นอกเหนือจากข้อเท็จจริงที่ว่าจะไม่ใช้กับคอนเทนเนอร์ STL ใด ๆ
Chris Lutz

คำตอบ:


116

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

เหตุใด C ++ จึงไม่มีวัตถุคลาสสากล

  • เราไม่ต้องการอย่างใดอย่างหนึ่ง: การเขียนโปรแกรมทั่วไปมีทางเลือกที่ปลอดภัยในการพิมพ์แบบคงที่ในกรณีส่วนใหญ่ กรณีอื่น ๆ ได้รับการจัดการโดยใช้การสืบทอดหลายรายการ

  • ไม่มีคลาสสากลที่มีประโยชน์: สากลที่แท้จริงไม่มีความหมายเป็นของตัวเอง

  • คลาส "สากล" กระตุ้นการคิดเกี่ยวกับประเภทและอินเทอร์เฟซที่ไม่เป็นระเบียบและนำไปสู่การตรวจสอบรันไทม์มากเกินไป

  • การใช้คลาสฐานสากลหมายถึงต้นทุน: อ็อบเจ็กต์ต้องได้รับการจัดสรรฮีปให้เป็นโพลีมอร์ฟิก ซึ่งหมายถึงหน่วยความจำและต้นทุนการเข้าถึง วัตถุฮีปไม่สนับสนุนความหมายของการคัดลอกโดยธรรมชาติ วัตถุฮีปไม่สนับสนุนพฤติกรรมที่กำหนดขอบเขตง่ายๆ (ซึ่งทำให้การจัดการทรัพยากรยุ่งยาก) คลาสฐานสากลสนับสนุนการใช้ dynamic_cast และการตรวจสอบรันไทม์อื่น ๆ


83
เราไม่ต้องการไม่มี / วัตถุคลาสพื้นฐาน / เราไม่ต้องการไม่มี / การควบคุมความคิด ... ;)
Piskvor ออกจากอาคาร

3
@Piskvor - ฮ่า ๆ ฉันอยากเห็นมิวสิควิดีโอ!
Byron Whitlock

11
Objects must be heap-allocated to be polymorphic- ฉันไม่คิดว่าคำพูดนี้ถูกต้องโดยทั่วไป คุณสามารถสร้างอินสแตนซ์ของคลาสโพลีมอร์ฟิคบนสแต็กได้อย่างแน่นอนและส่งต่อเป็นตัวชี้ไปยังคลาสพื้นฐานตัวใดตัวหนึ่งซึ่งส่งผลให้เกิดพฤติกรรมที่หลากหลาย
Byron

5
ใน Java ความพยายามที่จะโยนอ้างอิง-to- Fooเป็นอ้างอิง-to- Barเมื่อBarไม่สืบทอดFoo, deterministicallyพ่นยกเว้น ในภาษา C ++ การพยายามร่ายพอยน์เตอร์ทู - ฟูลงในพอยน์เตอร์ทูบาร์สามารถส่งหุ่นยนต์ย้อนเวลากลับไปฆ่าซาราห์คอนเนอร์ได้
supercat

3
@supercat นั่นคือเหตุผลที่เราใช้ dynamic_cast <> เพื่อหลีกเลี่ยง SkyNet และโซฟาเชสเตอร์ฟิลด์ที่ดูลึกลับ
Captain Giraffe

44

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

  1. เพื่อรองรับการดำเนินการทั่วไปหรือคอลเลกชันที่จะทำงานกับวัตถุประเภทใดก็ได้
  2. เพื่อรวมโพรซีเดอร์ต่างๆซึ่งเป็นเรื่องปกติสำหรับอ็อบเจ็กต์ทั้งหมด (เช่นการจัดการหน่วยความจำ)
  3. ทุกอย่างเป็นวัตถุ (ไม่ใช่ของดั้งเดิม!) บางภาษา (เช่น Objective-C) ไม่มีสิ่งนี้ซึ่งทำให้สิ่งต่างๆยุ่งเหยิง

นี่คือเหตุผลที่ดีสองประการที่ภาษาของแบรนด์ Smalltalk, Ruby และ Objective-C มีคลาสพื้นฐาน (ในทางเทคนิค Objective-C ไม่มีคลาสฐาน แต่สำหรับเจตนาและวัตถุประสงค์ทั้งหมด)

สำหรับ # 1 ความต้องการคลาสพื้นฐานที่รวมอ็อบเจ็กต์ทั้งหมดไว้ในอินเทอร์เฟซเดียวนั้นถูกขัดขวางโดยการรวมเทมเพลตใน C ++ ตัวอย่างเช่น:

void somethingGeneric(Base);

Derived object;
somethingGeneric(object);

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

template <class T>
void somethingGeneric(T);

Derived object;
somethingGeneric(object);

สำหรับ # 2 ในขณะที่ Objective-C ขั้นตอนการจัดการหน่วยความจำเป็นส่วนหนึ่งของการนำไปใช้งานของคลาสและสืบทอดมาจากคลาสฐานการจัดการหน่วยความจำใน C ++ จะดำเนินการโดยใช้องค์ประกอบแทนที่จะสืบทอด ตัวอย่างเช่นคุณสามารถกำหนดสมาร์ทพอยน์เตอร์แรปเปอร์ซึ่งจะทำการอ้างอิงการนับอ็อบเจ็กต์ประเภทใดก็ได้:

template <class T>
struct refcounted
{
  refcounted(T* object) : _object(object), _count(0) {}

  T* operator->() { return _object; }
  operator T*() { return _object; }

  void retain() { ++_count; }

  void release()
  {
    if (--_count == 0) { delete _object; }
  }

  private:
    T* _object;
    int _count;
};

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

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

ดังนั้นคำตอบสั้น ๆ สำหรับคำถามของคุณ: C ++ ไม่มีคลาสพื้นฐานเนื่องจากมีความหลากหลายเชิงพาราเมตริกผ่านเทมเพลตจึงไม่จำเป็นต้องมี


ไม่เป็นไร! ดีใจที่คุณพบว่ามีประโยชน์ :)
Jonathan Sterling

2
สำหรับจุดที่ 3 ฉันตอบโต้ด้วย C # C # มีพื้นฐานที่สามารถใส่กล่องเป็นobject( System.Object) ได้ แต่ไม่จำเป็นต้องเป็น ไปยังคอมไพเลอร์intและSystem.Int32เป็นนามแฝงและสามารถใช้แทนกันได้ รันไทม์จะจัดการกับการชกมวยเมื่อจำเป็น
Cole Johnson

ใน C ++ ไม่จำเป็นต้องชกมวย ด้วยเทมเพลตคอมไพลเลอร์จะสร้างรหัสที่ถูกต้องในเวลาคอมไพล์
Rob K

2
โปรดทราบว่าในขณะที่คำตอบ (เก่า) นี้แสดงให้เห็นถึงการอ้างอิงตัวชี้อัจฉริยะที่ถูกนับ แต่ตอนนี้ C ++ 11 มีstd::shared_ptrที่ควรใช้แทน

15

กระบวนทัศน์ที่โดดเด่นสำหรับตัวแปร C ++ คือ pass-by-value ไม่ใช่ pass-by-ref การบังคับให้ทุกอย่างมาจากรูทObjectจะทำให้การส่งผ่านค่าเหล่านั้นเป็นข้อผิดพลาด ipse facto

(เนื่องจากการยอมรับ Object ด้วยค่าเป็นพารามิเตอร์โดยคำจำกัดความจะหั่นมันและลบวิญญาณของมันออก)

สิ่งนี้ไม่เป็นที่พอใจ C ++ ทำให้คุณคิดว่าคุณต้องการค่าหรือความหมายอ้างอิงให้คุณเลือก นี่เป็นเรื่องใหญ่ในการคำนวณประสิทธิภาพ


1
มันจะไม่ใช่ข้อผิดพลาดในตัวมันเอง มีลำดับชั้นของประเภทอยู่แล้วและพวกเขาใช้ pass-by-value ได้ค่อนข้างดีเมื่อเหมาะสม อย่างไรก็ตามมันจะสนับสนุนกลยุทธ์ที่อิงกับฮีปมากขึ้นซึ่งการอ้างอิงแบบพาสบายมีความเหมาะสมมากกว่า
Dennis Zickefoose

IMHO ภาษาที่ดีควรมีการสืบทอดที่แตกต่างกันสำหรับสถานการณ์ที่คลาสที่ได้รับสามารถใช้เป็นอ็อบเจ็กต์คลาสพื้นฐานได้อย่างถูกต้องโดยใช้ slice-by-reference ซึ่งสามารถเปลี่ยนเป็นภาษาเดียวได้โดยใช้ slice-by-value และผู้ที่ไม่ถูกต้องตามกฎหมาย มูลค่าของการมีประเภทฐานทั่วไปสำหรับสิ่งที่สามารถหั่นบาง ๆ ได้จะมี จำกัด แต่หลายสิ่งไม่สามารถหั่นบาง ๆ ได้และต้องจัดเก็บทางอ้อม ค่าใช้จ่ายเพิ่มเติมของการมีสิ่งเหล่านี้มาจากสิ่งที่พบบ่อยObjectจะค่อนข้างน้อย
supercat

5

ปัญหาคือมีประเภทนี้ใน C ++! มันคือvoid. :-) ตัวชี้ใด ๆ อาจถูกส่งโดยปริยายอย่างปลอดภัยvoid *รวมถึงตัวชี้ไปยังประเภทพื้นฐานคลาสที่ไม่มีตารางเสมือนและคลาสที่มีตารางเสมือน

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

ดังนั้นจึงมีประเภทดังกล่าว มันให้อินเทอร์เฟซที่เรียบง่ายมาก (ในความเป็นจริงว่างเปล่า) เนื่องจากลักษณะของภาษาระดับต่ำ :-)


ชนิดของตัวชี้และประเภทวัตถุไม่เหมือนกัน voidคุณไม่สามารถมีวัตถุของการพิมพ์
Kevin Panko

1
ในความเป็นจริงนั่นคือวิธีการสืบทอดใน C ++ โดยปกติ สิ่งที่สำคัญที่สุดอย่างหนึ่งคือความเข้ากันได้ของตัวชี้และชนิดการอ้างอิง: ในกรณีที่ต้องใช้ตัวชี้ไปยังคลาสอาจมีการกำหนดตัวชี้ไปยังคลาสที่ได้รับ และความสามารถในการคงพอยน์เตอร์แบบคงที่ระหว่างสองประเภทคือสัญญาณที่เชื่อมต่อกันในลำดับชั้น จากมุมมองนี้โมฆะเป็นคลาสพื้นฐานสากลใน C ++
Ellioh

หากคุณประกาศตัวแปรประเภทvoidมันจะไม่คอมไพล์และคุณได้รับข้อผิดพลาดนี้ ใน Java คุณสามารถมีตัวแปรประเภทObjectและมันจะทำงานได้ นั่นคือความแตกต่างระหว่างvoidและประเภท "จริง" บางทีอาจเป็นความจริงที่voidเป็นประเภทพื้นฐานสำหรับทุกสิ่ง แต่เนื่องจากไม่มีตัวสร้างวิธีการหรือฟิลด์ใด ๆ จึงไม่มีทางที่จะบอกได้ว่ามีหรือไม่ การยืนยันนี้ไม่สามารถพิสูจน์หรือพิสูจน์ไม่ได้
Kevin Panko

1
ใน Java ทุกตัวแปรเป็นการอ้างอิง นั่นคือความแตกต่าง :-) (BTW ใน C ++ ยังมีคลาสนามธรรมที่คุณไม่สามารถใช้เพื่อประกาศตัวแปรได้) และในคำตอบของฉันฉันอธิบายว่าเหตุใดจึงไม่สามารถรับ RTTI จากโมฆะได้ ดังนั้นโมฆะในทางทฤษฎียังคงเป็นคลาสพื้นฐานสำหรับทุกสิ่ง static_cast เป็นหลักฐานเนื่องจากจะทำการร่ายระหว่างประเภทที่เกี่ยวข้องเท่านั้นและสามารถใช้เป็นโมฆะได้ แต่คุณพูดถูกการไม่มีการเข้าถึง RTTI ในความว่างเปล่าทำให้แตกต่างจากประเภทพื้นฐานในภาษาระดับสูงกว่ามาก
Ellioh

1
@Ellioh หลังจากปรึกษา C ++ 11 มาตรฐาน§3.9.1p9ฉันยอมรับว่าvoidเป็นประเภท (และค้นพบการใช้งานที่? :ค่อนข้างฉลาด) แต่ก็ยังห่างไกลจากการเป็นคลาสฐานสากล ประการหนึ่งก็คือ "ประเภทที่ไม่สมบูรณ์ซึ่งไม่สามารถทำให้เสร็จสมบูรณ์ได้" และสำหรับอีกประเภทหนึ่งคือไม่มีvoid&ประเภทใด
bcrist

-2

C ++ เป็นภาษาที่พิมพ์อย่างรุนแรง แต่เป็นที่น่างงว่าไม่มีประเภทออบเจ็กต์สากลในบริบทของความเชี่ยวชาญพิเศษของเทมเพลต

ยกตัวอย่างเช่นรูปแบบ

template <class T> class Hook;
template <class ReturnType, class ... ArgTypes>
class Hook<ReturnType (ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

ซึ่งสามารถสร้างอินสแตนซ์เป็น

Hook<decltype(some_function)> ...;

ตอนนี้สมมติว่าเราต้องการเหมือนกันสำหรับฟังก์ชันเฉพาะ ชอบ

template <auto fallback> class Hook;
template <auto fallback, class ReturnType, class ... ArgTypes>
class Hook<ReturnType fallback(ArgTypes...)>
{
   ...
   ReturnType operator () (ArgTypes... args) { ... }
};

ด้วยการสร้างอินสแตนซ์พิเศษ

Hook<some_function> ...

แต่อนิจจาแม้ว่าคลาส T สามารถยืนอยู่ในประเภทใดก็ได้ (คลาสหรือไม่ก็ได้) ก่อนที่จะเชี่ยวชาญ แต่ก็ไม่มีสิ่งที่เทียบเท่ากันauto fallback(ฉันกำลังใช้ไวยากรณ์นั้นเป็นรูปแบบทั่วไปที่ไม่ใช่ประเภทที่ชัดเจนที่สุดในบริบทนี้) ที่สามารถยืนหยัดได้ อาร์กิวเมนต์แม่แบบที่ไม่ใช่ประเภทก่อนความเชี่ยวชาญ

ดังนั้นโดยทั่วไปรูปแบบนี้จะไม่ถ่ายโอนจากอาร์กิวเมนต์เทมเพลตประเภทไปยังอาร์กิวเมนต์แม่แบบที่ไม่ใช่ประเภท

เช่นเดียวกับหลาย ๆ มุมในภาษา C ++ คำตอบน่าจะเป็น "ไม่มีกรรมการคนใดคิด"


แม้ว่า (บางอย่างเช่น) งานของคุณReturnType fallback(ArgTypes...)จะเป็นการออกแบบที่ไม่ดี template <class T, auto fallback> class Hook; template <class ReturnType, class ... ArgTypes> class Hook<ReturnType (ArgTypes...), ReturnType(*fallback)(ArgTypes...)> ...ทำในสิ่งที่คุณต้องการและหมายความว่าพารามิเตอร์เทมเพลตHookเป็นชนิดที่
Caleth

-4

C ++ ในตอนแรกเรียกว่า "C พร้อมคลาส" เป็นความก้าวหน้าของภาษา C ซึ่งแตกต่างจากสิ่งที่ทันสมัยกว่าเช่น C # และคุณไม่สามารถเห็น C ++ เป็นภาษา แต่เป็นพื้นฐานของภาษา (ใช่ฉันจำหนังสือ Scott Meyers C ++ ที่มีประสิทธิภาพ)

C นั้นเป็นการผสมผสานระหว่างภาษาภาษาโปรแกรม C และตัวประมวลผลล่วงหน้า

C ++ เพิ่มส่วนผสมอื่น:

  • คลาส / วัตถุเข้าใกล้

  • เทมเพลต

  • STL

โดยส่วนตัวแล้วฉันไม่ชอบบางสิ่งที่มาจาก C ถึง C ++ โดยตรง ตัวอย่างหนึ่งคือคุณลักษณะ enum วิธีที่ C # อนุญาตให้นักพัฒนาใช้เป็นวิธีที่ดีกว่า: จำกัด enum ในขอบเขตของตัวเองมีคุณสมบัติ Count และสามารถทำซ้ำได้อย่างง่ายดาย

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


"ไม่เห็น C ++ เป็นภาษา แต่เป็นพื้นฐานของภาษา" ... สนใจที่จะอธิบายและแสดงให้เห็นว่าคำพูดที่แปลกประหลาดนั้นกำลังขับเคลื่อนอยู่ที่อะไร? กระบวนทัศน์หลายอย่างแตกต่างจาก "หลายภาษา" แม้ว่าจะเป็นเรื่องยุติธรรมที่จะบอกว่าตัวประมวลผลก่อนไม่ปะติดปะต่อจากขั้นตอนที่เหลือของคอมไพเลอร์ - จุดที่ดี Re enums - พวกเขาสามารถห่อหุ้มไว้ในขอบเขตของคลาสได้เล็กน้อยหากต้องการและภาษาทั้งหมดขาดการไตร่ตรองซึ่งเป็นประเด็นสำคัญแม้ว่าในกรณีนี้จะสามารถประมาณได้ - ดูรหัส Benum ของ Boost vault ประเด็นของคุณคือ Q เท่านั้น: C ++ ไม่ได้เริ่มต้นด้วยฐานสากลหรือไม่?
Tony Delroy

@ โทนี่: อยากรู้อยากเห็นหนังสือที่ฉันอ้างถึงสิ่งเดียวที่พวกเขาไม่ได้กล่าวถึงเป็นภาษาอื่นคือตัวประมวลผลล่วงหน้าซึ่งเป็นเพียงเล่มเดียวที่คุณพิจารณาต่างหาก ฉันเข้าใจมุมมองของคุณเนื่องจากตัวประมวลผลล่วงหน้าจะแยกวิเคราะห์อินพุตของตัวเองก่อน แต่การมีกลไกการสร้างเทมเพลตก็เหมือนกับการมีภาษาอื่นที่สร้างความเข้าใจให้กับคุณ คุณใช้ไวยากรณ์เทมเพลตและคุณจะมีฟังก์ชันสร้างคอมไพเลอร์สำหรับทุกประเภทที่คุณมี เมื่อคุณสามารถเขียนโปรแกรมด้วยภาษา C และป้อนข้อมูลให้กับคอมไพเลอร์ C ++ นี่คือสองภาษาในภาษาเดียว: C และ C พร้อมวัตถุ => C ++
sergiol

STL สามารถมองได้ว่าเป็นส่วนเสริม แต่มีคลาสและคอนเทนเนอร์ที่ใช้งานได้จริงซึ่งผ่านเทมเพลต NATIVELY ขยายพลังของ C ++ และ enums ที่ฉันพูดคือ NATIVE enums เมื่อคุณพูดถึง Boost คุณต้องอาศัยรหัสของบุคคลที่สามที่ไม่ใช่ภาษา NATIVE ใน C # ฉันไม่จำเป็นต้องรวมสิ่งที่อยู่ภายนอกเพื่อให้มี enum ที่ทำซ้ำได้และกำหนดขอบเขตด้วยตนเอง ใน C ++ เคล็ดลับที่ฉันมักใช้คือการเรียกรายการสุดท้ายของ enum - โดยไม่มีค่าที่กำหนดดังนั้นพวกเขาจึงเริ่มต้นจากศูนย์และเพิ่มขึ้นโดยอัตโนมัติเช่น NUM_ITEMS และสิ่งนี้ทำให้ฉันมีขอบเขตบน
sergiol

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

1
ฉันคิดว่าปัญหาที่ใหญ่ที่สุดในการมีประเภทฐานสากลคือประเภทดังกล่าวจะไร้ประโยชน์หากไม่มีวิธีการเสมือนจริง C ++ ไม่ต้องการเพิ่มค่าใช้จ่ายใด ๆ สำหรับประเภทโครงสร้างที่ไม่มีเมธอดเสมือนใด ๆ แต่ทุกออบเจ็กต์ทุกประเภทที่มีเมธอดเสมือนต้องมีตัวชี้ไปยังตารางการจัดส่งเป็นอย่างน้อย หากคลาสและโครงสร้างมีจักรวาลที่แตกต่างกันอาจเป็นไปได้ที่จะมีอ็อบเจ็กต์คลาสสากล แต่คลาสและโครงสร้างนั้นเป็นสิ่งเดียวกันใน C ++
supercat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.