การสืบทอดจากคลาสเทมเพลตใน c ++


108

สมมติว่าเรามีคลาสเทมเพลตAreaซึ่งมีตัวแปรสมาชิกT areaa T getArea()และvoid setArea(T)ฟังก์ชันสมาชิก

ฉันสามารถสร้างวัตถุชนิดโดยเฉพาะการพิมพ์AreaArea<int>

ตอนนี้ฉันมีคลาสRectangleที่สืบทอดAreaคลาส เนื่องจากRectangleตัวมันเองไม่ใช่เทมเพลตฉันจึงไม่สามารถพิมพ์Rectangle<int>ได้

ฉันจะเชี่ยวชาญAreaประเภทที่สืบทอดมาสำหรับRectangleวัตถุได้อย่างไร

แก้ไข: ขออภัยฉันลืมที่จะชี้แจง - คำถามของฉันคือเป็นไปได้หรือไม่ที่จะสืบทอด Area โดยไม่เชี่ยวชาญดังนั้นจึงไม่ได้รับการสืบทอดเป็น Area of ​​ints แต่เนื่องจาก Area Rectangle สามารถเชี่ยวชาญประเภทสำหรับ


4
เป็นเทมเพลตของคลาสเนื่องจากเป็นเทมเพลตจากคลาสที่สร้างขึ้น
sbi

1
@sbi ฉันไม่ได้ตั้งใจที่จะเริ่มสงครามเปลวไฟที่นี่ แต่ถ้า Bjarne Stroustrup ไม่ได้สร้างความแตกต่างระหว่างเทมเพลตคลาสและคลาสเทมเพลต (ดูภาษาการเขียนโปรแกรม C ++ , 4th ed., ส่วน 23.2.1) คุณควรจะ 't อย่างใดอย่างหนึ่ง
Michael Warner

@MichaelWarner ฉันดูเหมือนจะจำเขาได้ทำให้แตกต่าง นั่นคือในยุค 90 แม้ว่าใน Usenet บางทีเขาอาจจะยอมแพ้ตั้งแต่ (หรือบางทีเขาอาจหมายถึงเทมเพลตคลาสที่สร้างอินสแตนซ์เป็นคลาสเทมเพลต?)
sbi

คำตอบ:


247

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

โดยเฉพาะAreaไม่ใช่คลาสเทมเพลต แต่เป็นเทมเพลตคลาส นั่นคือมันเป็นเทมเพลตที่สามารถสร้างคลาสได้ Area<int>เป็นคลาสดังกล่าว ( ไม่ใช่วัตถุ แต่แน่นอนคุณสามารถสร้างวัตถุจากคลาสนั้นด้วยวิธีเดียวกับที่คุณสามารถสร้างวัตถุจากคลาสอื่น ๆ ) Area<char>อีกระดับดังกล่าวจะเป็น โปรดทราบว่าคลาสเหล่านี้เป็นคลาสที่แตกต่างกันโดยสิ้นเชิงซึ่งไม่มีอะไรเหมือนกันยกเว้นความจริงที่ว่าคลาสเหล่านี้ถูกสร้างขึ้นจากเทมเพลตคลาสเดียวกัน

เนื่องจากAreaไม่ใช่คลาสคุณจึงไม่สามารถหาคลาสRectangleจากคลาสได้ คุณสามารถได้รับคลาสจากคลาสอื่น (หรือหลายคลาส) เนื่องจากArea<int>เป็นคลาสตัวอย่างเช่นคุณสามารถได้มาRectangleจากมัน:

class Rectangle:
  public Area<int>
{
  // ...
};

เนื่องจากArea<int>และArea<char>เป็นคลาสที่แตกต่างกันคุณสามารถได้รับจากทั้งสองอย่างในเวลาเดียวกัน (อย่างไรก็ตามเมื่อเข้าถึงสมาชิกของพวกเขาคุณจะต้องจัดการกับความคลุมเครือ):

class Rectangle:
  public Area<int>,
  public Area<char>
{
  // ...
};

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

สิ่งที่ทำได้คือทำRectangleเทมเพลตด้วย ถ้าคุณเขียน

template<typename T> class Rectangle:
  public Area<T>
{
  // ...
};

คุณมีเทมเพลต Rectangleจากการที่คุณจะได้รับในชั้นเรียนRectangle<int>ที่มาจากArea<int>และระดับที่แตกต่างกันซึ่งเกิดขึ้นจากRectangle<char>Area<char>

อาจเป็นไปได้ว่าคุณต้องการมีแบบเดี่ยว Rectangleเพื่อให้คุณสามารถส่งผ่านทุกประเภทRectangleไปยังฟังก์ชันเดียวกัน (ซึ่งตัวเองไม่จำเป็นต้องทราบประเภทพื้นที่) เนื่องจากRectangle<T>คลาสที่สร้างขึ้นโดยการสร้างอินสแตนซ์เทมเพลตRectangleนั้นเป็นอิสระจากกันอย่างเป็นทางการจึงไม่ได้ผลเช่นนั้น อย่างไรก็ตามคุณสามารถใช้ประโยชน์จากการสืบทอดหลายรายการได้ที่นี่:

class Rectangle // not inheriting from any Area type
{
  // Area independent interface
};

template<typename T> class SpecificRectangle:
  public Rectangle,
  public Area<T>
{
  // Area dependent stuff
};

void foo(Rectangle&); // A function which works with generic rectangles

int main()
{
  SpecificRectangle<int> intrect;
  foo(intrect);

  SpecificRectangle<char> charrect;
  foo(charrect);
}

หากเป็นสิ่งสำคัญที่ยาสามัญของคุณRectangleมาจากยาสามัญAreaคุณสามารถทำเคล็ดลับเดียวกันได้Areaเช่นกัน:

class Area
{
  // generic Area interface
};

class Rectangle:
  public virtual Area // virtual because of "diamond inheritance"
{
  // generic rectangle interface
};

template<typename T> class SpecificArea:
  public virtual Area
{
  // specific implementation of Area for type T
};

template<typename T> class SpecificRectangle:
  public Rectangle, // maybe this should be virtual as well, in case the hierarchy is extended later
  public SpecificArea<T> // no virtual inheritance needed here
{
  // specific implementation of Rectangle for type T
};

หมายเหตุ: พารามิเตอร์ประเภทเทมเพลตเป็นส่วนหนึ่งของคลาสของอ็อบเจ็กต์ซึ่งอินสแตนซ์เทมเพลตคลาสนั้น กล่าวอีกนัยหนึ่งไม่ใช่องค์ประกอบของชั้นเรียน แต่เป็นส่วนหนึ่งของประเภท
Nikos

21

คุณแค่พยายามที่จะได้มาจากArea<int>? ในกรณีนี้ให้ทำดังนี้

class Rectangle : public Area<int>
{
    // ...
};

แก้ไข: หลังจากการชี้แจงดูเหมือนว่าคุณกำลังพยายามสร้างRectangleเทมเพลตด้วยซึ่งในกรณีต่อไปนี้ควรใช้งานได้:

template <typename T>
class Rectangle : public Area<T>
{
    // ...
};


8

ทำให้ Rectangle เป็นเทมเพลตและส่งชื่อพิมพ์ไปที่ Area:

template <typename T>
class Rectangle : public Area<T>
{

};

6

Rectangleจะต้องเป็นแม่แบบมิฉะนั้นจะเป็นเพียงประเภทเดียว ไม่สามารถเป็นเทมเพลตได้ในขณะที่ฐานของมันมีความมหัศจรรย์ (ฐานของมันอาจเป็นการสร้างอินสแตนซ์เทมเพลตแม้ว่าคุณจะต้องการรักษาการทำงานของฐานเป็นเทมเพลตก็ตาม)


3
#include<iostream>

using namespace std;

template<class t> 
class base {
protected:
    t a;
public:
    base(t aa){
        a = aa;
        cout<<"base "<<a<<endl;
    }
};

template <class t> 
class derived: public base<t>{
    public:
        derived(t a): base<t>(a) {
        }
        //Here is the method in derived class 
    void sampleMethod() {
        cout<<"In sample Method"<<endl;
    }
};

int main() {
    derived<int> q(1);
    // calling the methods
    q.sampleMethod();
}

จะเกิดอะไรขึ้นถ้าคุณมีวิธีการในคลาสที่ได้รับ? คุณจะกำหนดพวกเขาอย่างไร? และการนำเข้าส่วนใหญ่อย่างน้อยสำหรับฉันคุณจะเรียก / ใช้วิธีการเหล่านั้นในฟังก์ชัน main () ของคุณได้อย่างไร
Pototo

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