จำเป็นต้องใช้ฟังก์ชันเสมือนทั้งหมดในคลาสที่ได้รับมาหรือไม่?


94

นี่อาจดูเหมือนคำถามธรรมดา ๆ แต่ฉันหาคำตอบไม่ได้จากที่อื่น

สมมติว่าฉันมีสิ่งต่อไปนี้:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

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

คำตอบ:


84

คลาสที่ได้รับมาไม่จำเป็นต้องใช้ฟังก์ชันเสมือนทั้งหมดด้วยตนเอง พวกเขาจำเป็นต้องใช้สิ่งที่บริสุทธิ์เท่านั้น 1นั่นหมายความว่าDerivedชั้นเรียนในคำถามนั้นถูกต้อง มันสืบทอดการดำเนินงานจากชั้นบรรพบุรุษของตนbar Abstract(สมมติว่าAbstract::barมีการนำไปใช้ที่ใดที่หนึ่งโค้ดในคำถามจะประกาศวิธีการ แต่ไม่ได้กำหนดวิธีนี้คุณสามารถกำหนดแบบอินไลน์ตามที่คำตอบของ Trenkiแสดงหรือคุณสามารถกำหนดแยกต่างหากก็ได้)


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


3
และแม้กระทั่งสิ่งนี้ (การนำฟังก์ชันเสมือนจริงมาใช้) ก็ต่อเมื่อมีวัตถุประสงค์เพื่อสร้างอินสแตนซ์ (ตรงกันข้ามกับการเป็นคลาสพื้นฐานที่เป็นนามธรรม)
Christian Rau

1
นั่นคือสิ่งที่ฉันคิด. แต่กำลังทำสิ่งนี้ในโครงการของฉันและฉันได้รับข้อผิดพลาดในการเชื่อมโยงว่ามี "สัญลักษณ์ภายนอกที่ไม่ได้รับการแก้ไข" สำหรับ Derived :: bar (); แต่ฉันไม่เคยประกาศ bar ใน Derived ดังนั้นเหตุใดตัวเชื่อมโยงจึงมองหาเนื้อความของฟังก์ชัน
mikestaub

1
@pixelpusher แน่นอนว่าDerived::barมีร่างกายที่ทำงานAbstract::barได้ ดูเหมือนว่าหน่วยการแปลที่กำหนดไว้ (กำหนดไว้ที่ใดหรือไม่) ไม่ได้เชื่อมโยงกับหน่วยการแปลที่เรียก
Christian Rau

2
@Rob: They only need to implement the pure ones.มันทำให้เข้าใจผิด เรียนมาไม่จำเป็นต้องที่จะใช้บริสุทธิ์หน้าที่เสมือนอย่างใดอย่างหนึ่ง
Nawaz

ฉันขอบคุณความช่วยเหลือ แต่ @trenki โดนตะปูที่หัว แม้ว่าคุณจะถูกต้อง Christian Rau เหมือนกัน แต่ก็ไม่ได้กำหนดไว้
mikestaub

47

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

ดังนั้นเพียงแค่ใส่{}หลังวิธีการเสมือนที่เป็นทางเลือกของคุณจะทำให้คุณมีการใช้งานเริ่มต้นที่ว่างเปล่า:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

การใช้งานเริ่มต้นที่เกี่ยวข้องมากขึ้นจะไปอยู่ในซอร์สไฟล์แยกต่างหาก


7

มาตรฐาน ISO C ++ ระบุว่าต้องกำหนดเมธอดเสมือนทั้งหมดของคลาสที่ไม่ใช่เพียว - เวอร์ชวล

เพียงแค่ใส่กฎคือ:
หากคลาสที่ได้รับของคุณทับเมธอดเสมือนคลาสฐานแล้วก็ควรให้คำจำกัดความด้วยเช่นกันถ้าไม่เช่นนั้นคลาสฐานควรให้คำจำกัดความของเมธอดนั้น

ตามกฎข้างต้นในตัวอย่างโค้ดของคุณvirtual void bar();ต้องการคำจำกัดความในคลาสฐาน

อ้างอิง:

C ++ 03 Standard: 10.3 ฟังก์ชันเสมือน [class.virtual]

ฟังก์ชันเสมือนที่ประกาศในคลาสจะต้องถูกกำหนดหรือประกาศว่าบริสุทธิ์ (10.4) ในคลาสนั้นหรือทั้งสองอย่าง แต่ไม่จำเป็นต้องมีการวินิจฉัย (3.2)

ดังนั้นคุณควรทำให้ฟังก์ชันเสมือนจริงบริสุทธิ์หรือให้คำจำกัดความสำหรับมัน

GCC คำถามที่พบบ่อย doccuments มันเช่นกัน:

มาตรฐาน ISO C ++ ระบุว่าต้องกำหนดเมธอดเสมือนทั้งหมดของคลาสที่ไม่ใช่เพียว - เวอร์ช่วล แต่ไม่ต้องการการวินิจฉัยใด ๆ สำหรับการละเมิดกฎ[class.virtual]/8นี้ จากสมมติฐานนี้ GCC จะปล่อยเฉพาะตัวสร้างที่กำหนดโดยนัยตัวดำเนินการกำหนดตัวทำลายและตารางเสมือนของคลาสในหน่วยการแปลที่กำหนดวิธีการที่ไม่ใช่อินไลน์เป็นครั้งแรก

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

วิธีแก้ปัญหาคือเพื่อให้แน่ใจว่ามีการกำหนดวิธีการเสมือนทั้งหมดที่ไม่บริสุทธิ์ โปรดทราบว่าต้องกำหนด destructor แม้ว่าจะประกาศว่าเป็น pure-virtual [class.dtor]/7ก็ตาม


3

ใช่ไม่เป็นไร ... คุณต้องใช้ฟังก์ชันเสมือนจริงใด ๆ เพื่อสร้างอินสแตนซ์คลาสที่ได้มาจากคลาสฐานนามธรรม


1

ใช่มันถูกต้องที่คลาส Derived ต้องโอเวอร์ไรด์ฟังก์ชันซึ่งเป็น Pure Virtual ในคลาสแม่ คลาสพาเรนต์ที่มี Pure Virtual Function เรียกว่า Abstract Class เท่านั้นเนื่องจากคลาส Child ต้องให้ Pure Virtual Function เป็นของตัวเอง

สำหรับฟังก์ชันเสมือนปกติ: - ไม่จำเป็นต้องลบล้างเพิ่มเติมเนื่องจากคลาสย่อยบางคลาสอาจมีฟังก์ชันนั้นบางฟังก์ชันอาจไม่มี

วัตถุประสงค์หลักของกลไก Virtual Function คือ Run Time Polymorphism ไม่ว่าจุดประสงค์หลักของ Pure Virtual Function (Abstract Class) คือการกำหนดให้มีชื่อ Function เหมือนกันกับตัวของมันเอง

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