องค์ประกอบ C ++ พร้อมคลาสนามธรรม

สมมติว่าฉันมีคลาสนามธรรมที่มีราคาแพงในการสร้างและคัดลอก:

class AbstractBase {
public:
    AbstractBase() {
        for (int i = 0; i < 50000000; ++i) {
            values.push_back(i);
        }
    }

    virtual void doThing() = 0;

private:
    vector<int> values;
};

มันมีสองคลาสย่อย FirstDerived:

class FirstDerived : public AbstractBase {
public:
    void doThing() {
        std::cout << "I did the thing in FirstDerived!\n";
    }

};

และ SecondDerived:

class SecondDerived : public AbstractBase {
public:
    void doThing() {
        std::cout << "I did the thing in SecondDerived!\n";
    }
};

นอกจากนี้ ฉันต้องการสร้างคลาสที่ใช้ FirstDerived หรือ SecondDerived โดยใช้การเรียบเรียง (ไม่ใช่การรวมกลุ่ม) หมายความว่าฉันต้องการ ComposedOfAbstractBase เป็นเจ้าของ ไม่ว่าจะส่งผ่านชั่วคราวใดก็ตาม หากฉันไม่ได้ใช้คลาสนามธรรมในคลาสนี้จะมีลักษณะดังนี้: (ใน C ++ 11)

class ComposedOfWhicheverDerived {
public:
    ComposedOfWhicheverDerived(AbstractBase abstract_base) : abstract_base(std::move(abstract_base)) {;}
private:
    AbstractBase abstract_base;
};

อย่างไรก็ตาม สิ่งนี้ใช้ไม่ได้กับคลาสนามธรรมเนื่องจากฉันไม่สามารถสร้างอินสแตนซ์ของ AbstractBase ได้ แม้ว่าฉันจะระมัดระวังที่จะไม่ส่งผ่าน AbstractBase ชั่วคราวก็ตาม เช่น:

ComposedOfWhicheverDerived a(FirstDerived());

สำหรับคอมไพเลอร์สิ่งนี้แย่พอ ๆ กับ:

ComposedOfWhicheverDerived b(AbstractBase());

เพราะฉันยังมีอินสแตนซ์ AbstractBase ในการประกาศคลาส

วิธีแก้ปัญหาต่อไปที่ฉันคิดคือ:

class ComposedOfAbstractBase {
public:
    ComposedOfAbstractBase(AbstractBase&& abstract_base) : some_derived_instance(abstract_base) {;}

private:
    AbstractBase& some_derived_instance;
};

มันใช้งานได้อย่างสมบูรณ์แบบ (แม้ว่าฉันจะไม่เข้าใจมันก็ตาม)! ทั้งสองกรณีนี้ถูกต้องและทำงานตามที่ตั้งใจไว้:

ComposedOfAbstractBase a(FirstDerived());
ComposedOfAbstractBase b(SecondDerived());

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

class ComposedOfFirstDerived {
public:
    ComposedOfFirstDerived(FirstDerived first_derived) : first_derived(std::move(first_derived)) {;}

private:
    FirstDerived first_derived;
};

ซึ่งสามารถรับได้เฉพาะใน FirstDerived เท่านั้น ดังนั้นเราจึงสามารถใช้ std::move เพื่อลดภาระการเป็นเจ้าของชั่วคราวได้ ฉันสามารถสร้างตัวอย่างดังนี้:

ComposedOfFirstDerived c(FirstDerived()); 

สิ่งที่น่าสนใจคือคลาสนี้สร้างได้เร็วกว่า ComposedOfAbstractClass ถึง 10% อย่างสม่ำเสมอ

ไม่มีใครรู้ว่าเกิดอะไรขึ้นที่นี่? เหตุใด ComposedOfFirstDerived จึงสร้างได้เร็วกว่า ComposedOfAbstractBase มาก มีวิธีที่ดีกว่าในการจัดองค์ประกอบด้วยคลาสนามธรรมหรือฉันติดอยู่กับวิธีแก้ปัญหาที่ไม่เหมาะสมหรือไม่?

ขออภัยถ้านี่เป็นคำถามที่ปากมาก ฉันขอขอบคุณทุกคนที่สละเวลาอ่านและให้คำตอบอย่างแท้จริง เพราะฉันนิ่งงันมาก!


person donutmonger    schedule 28.08.2015    source แหล่งที่มา
comment
วิธีแก้ปัญหา ไม่เหมาะที่สุด ผิดทั้งหมด มันไม่ได้เป็นเจ้าของชั่วคราว   -  person Piotr Skotnicki    schedule 28.08.2015
comment
อะไรเป็นเจ้าของชั่วคราวแล้ว? มันเทียบเท่ากับการสร้างตัวชี้ใหม่แล้วไม่ลบเลยหรือไม่?   -  person donutmonger    schedule 28.08.2015
comment
ไม่ เทียบเท่ากับการถือตัวชี้ไปยังพื้นที่หน่วยความจำที่ไม่ถูกต้อง   -  person Piotr Skotnicki    schedule 28.08.2015


คำตอบ (1)


ComposedOfAbstractBase ไม่ใช่วิธีแก้ปัญหา คุณกำลังถือการอ้างอิงห้อยต่องแต่ง

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

class ComposedOfAbstractBasePtr {
public:
    ComposedOfAbstractBasePtr(std::unique_ptr<AbstractBase> p)
    : some_derived_instance(std::move(p))
    { }

private:
    std::unique_ptr<AbstractBase> some_derived_instance;
};

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

person Barry    schedule 28.08.2015
comment
แล้วฉันจะเริ่มต้นสิ่งนั้นได้อย่างไร? ฉันทำไม่ได้แล้ว ComposedOfAbstractBasePtr(FirstDerived()) ฉันต้องสร้าง Unique_ptr ให้กับวัตถุแล้วส่งต่อหรือไม่ อะไรขัดขวางไม่ให้ฉันลบ ptr นั้นในภายหลังเนื่องจากความประมาท - person donutmonger; 28.08.2015
comment
@donutmonger คุณจะทำ make_unique<FirstDerived>() หรือคุณสามารถสร้าง unique_ptr ก่อนหน้าและ move เข้าไปได้ ตราบใดที่คุณจัดการกับ unique_ptrs โดยเฉพาะ คุณจะไม่สามารถลบมันออกโดยไม่ระมัดระวังได้ คุณต้องพิมพ์ delete p.get(); อย่างชัดเจนซึ่งควรเป็นธงสีแดง... - person Barry; 28.08.2015
comment
@donutmonger: ComposedOfAbstractBasePtr(std::make_unique<FirstDerived>()) คุณไม่สามารถสมัคร delete กับ unique_ptr ได้ และแม้ว่าคุณจะทำได้ คุณก็ยังส่งผ่านที่ไม่มีชื่อชั่วคราวอยู่ดี สิ่งนี้ไม่อาจผิดพลาดได้เนื่องจากความประมาท - person Christian Hackl; 28.08.2015
comment
ขอบคุณมาก ตอนนี้มันสมเหตุสมผลมากขึ้นแล้ว! - person donutmonger; 28.08.2015