ข้ามไปที่เนื้อหาหลัก

C++11 Smart Pointers ทดลองใช้ std::shared_ptr

พอดีหาเวลาทดลองคุณสมบัติใหม่ๆ ของ C++11 ดูหลายอย่างน่าใช้งานมาก เพราะทำให้ชีวิตการเขียนโปรแกรม C++ ง่ายขึ้น อ่านไปอ่านมาไปสะดุดกับ std::shared_ptr ซึ่งอยู่ภายได้ header ชื่อ memory ว่าดีกว่าการใช้ new กับ delete เลยคิดว่าหากเราเอามาใช้กับ design pattern ละจะดีแค่ใหน เลยลองหาโค้ดตัวอย่างของ design pattern ดูก็พบ C++ Programming/Code/Design Patterns จึงเอาส่วนของ Abstract Factory มาลองแก้ไขดู หน้าตาเลยเป็นแบบนี้


// pizza.cpp
// This original code from http://en.wikibooks.org/wiki/C++_Programming/Code/Design_Patterns#Abstract_Factory on 2012-08-01
// add constructor to Pizza for monitor behavior

#include <iostream>
#include <string>

class Pizza {
public:
    Pizza(std::string name):name(name){
        std::cout << "Constuct Pizza name: " << name << std::endl;
    }

    virtual ~Pizza(){
        std::cout << "Destroy Pizza name: " << name << std::endl;
    }

    virtual int getPrice() const = 0;

private:
    std::string name;

};

class HamAndMushroomPizza : public Pizza {
public:
    HamAndMushroomPizza():Pizza("HamAndMushroomPizza"){}
    virtual int getPrice() const { return 850; }
};

class DeluxePizza : public Pizza {
public:
    DeluxePizza():Pizza("DeluxePizza"){}
    virtual int getPrice() const { return 1050; }
};

class HawaiianPizza : public Pizza {
public:
    HawaiianPizza():Pizza("HawaiianPizza"){}
    virtual int getPrice() const { return 1150; }
};

class PizzaFactory {
public:
    enum PizzaType {
         HamMushroom,
         Deluxe,
         Hawaiian
    };

    static Pizza* createPizza(PizzaType pizzaType) {
        switch (pizzaType) {
            case HamMushroom:
                return new HamAndMushroomPizza();
            case Deluxe:
                return new DeluxePizza();
            case Hawaiian:
                return new HawaiianPizza();
        }

        throw "invalid pizza type.";
    }
};

/*
* Create all available pizzas and print their prices
*/

void pizza_information( PizzaFactory::PizzaType pizzatype )
{
        Pizza* pizza = PizzaFactory::createPizza(pizzatype);
        std::cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
        // delete pizza;
}

int main ()
{
        std::cout << "Start get pizza information" << std::endl;
        pizza_information( PizzaFactory::HamMushroom );
        pizza_information( PizzaFactory::Deluxe );
        pizza_information( PizzaFactory::Hawaiian );
        std::cout << "End get pizza information" << std::endl;

        return 0;
}

คอมไพล์และรัน

$ g++ pizza.cpp
$ ./a.out
Start get pizza information
Constuct Pizza name: HamAndMushroomPizza
Price of 0 is 850
Constuct Pizza name: DeluxePizza
Price of 1 is 1050
Constuct Pizza name: HawaiianPizza
Price of 2 is 1150
End get pizza information

สังเกตดูว่า Destructor ของ Pizza จะไม่ถูกเรียกใช้งาน เนื่องจากเรามี new แต่ไม่มี delete นั้นเอง หากเอา คอมเมนท์ในฟังก์ชัน void pizza_information( PizzaFactory::PizzaType pizzatype ) ตรงบรรทัด // delete pizza; ออก เหลือ delete pizza; อย่างเดียวดู

$ g++ pizza.cpp
$ ./a.out
Start get pizza information
Constuct Pizza name: HamAndMushroomPizza
Price of 0 is 850
Destroy Pizza name: HamAndMushroomPizza
Constuct Pizza name: DeluxePizza
Price of 1 is 1050
Destroy Pizza name: DeluxePizza
Constuct Pizza name: HawaiianPizza
Price of 2 is 1150
Destroy Pizza name: HawaiianPizza
End get pizza information

แน่นอนคราวนี้ Destructor จะถูกเรียกใช้งาน ปัญหาหนึ่งของการทำ design pattern ใน C++ คือ แล้วเราจะ delete ตรงใหน หากโปรแกรมเราใหญ่มากๆ ยิ่งมี interrupted หรือ Thread ด้วยแล้ว ยิ่งแล้วไปใหญ่ ทางออกประการหนึ่งคือ Smart Pointers จริงๆ แล้ว Smart Pointers มีหลาย class ให้เลือกใช้ ในที่นี่จะทดลองใช้ shared_ptr กัน

// pizza_shared_ptr.cpp
// pizza_shared_ptr.cpp
// This original code from http://en.wikibooks.org/wiki/C++_Programming/Code/Design_Patterns#Abstract_Factory on 2012-08-01
// add constructor to Pizza for monitor behavior

#include <iostream>
#include <string>
#include <memory>

class Pizza {
public:
    Pizza(std::string name):name(name){
        std::cout << "Constuct Pizza name: " << name << std::endl;
    }

    virtual ~Pizza(){
        std::cout << "Destroy Pizza name: " << name << std::endl;
    }

    virtual int getPrice() const = 0;

private:
    std::string name;
};

class HamAndMushroomPizza : public Pizza {
public:
    HamAndMushroomPizza():Pizza("HamAndMushroomPizza"){}
    virtual int getPrice() const { return 850; }
};

class DeluxePizza : public Pizza {
public:
    DeluxePizza():Pizza("DeluxePizza"){}
    virtual int getPrice() const { return 1050; }
};

class HawaiianPizza : public Pizza {
public:
    HawaiianPizza():Pizza("HawaiianPizza"){}
    virtual int getPrice() const { return 1150; }
};

class PizzaFactory {
public:
    enum PizzaType {
         HamMushroom,
         Deluxe,
         Hawaiian
    };
    static std::shared_ptr<Pizza> createPizza(PizzaType pizzaType) {
        switch (pizzaType) {
            case HamMushroom:
                return std::make_shared<HamAndMushroomPizza>();
            case Deluxe:
                return std::make_shared<DeluxePizza>();
            case Hawaiian:
                return std::make_shared<HawaiianPizza>();
        }
        throw "invalid pizza type.";
    }
};

/*
* Create all available pizzas and print their prices
*/

void pizza_information( PizzaFactory::PizzaType pizzatype )

{
        auto pizza = PizzaFactory::createPizza(pizzatype);
        std::cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
}

int main ()
{
        std::cout << "Start get pizza information" << std::endl;
        pizza_information( PizzaFactory::HamMushroom );
        pizza_information( PizzaFactory::Deluxe );
        pizza_information( PizzaFactory::Hawaiian );
        std::cout << "End get pizza information" << std::endl;
        return 0;
}

ทดลองใช้งานดู

$ g++ pizza_shared_ptr.cpp -std=c++11
$ ./a.out
Start get pizza information
Constuct Pizza name: HamAndMushroomPizza
Price of 0 is 850
Destroy Pizza name: HamAndMushroomPizza
Constuct Pizza name: DeluxePizza
Price of 1 is 1050 Destroy Pizza name: DeluxePizza
Constuct Pizza name: HawaiianPizza
Price of 2 is 1150
Destroy Pizza name: HawaiianPizza
End get pizza information 

หากสังเกตดูจะพบว่า เมื่อ Constructor ของ Pizza ทำงาน แต่พอหมดขอบเขตของฟังก์ชัน void pizza_information( PizzaFactory::PizzaType pizzatype ) object ชื่อ pizza จะโดนทำลาย โดยเรียก Destructor ทันที ส่วนการเรียกใช้งาน method ต่างๆ ก็เหมือนกับเรียกใช้งาน Pointer ปรกติ ทดลองอีกนิดเพื่อให้มั่นใจว่าตัวแปรจะไม่ถูกทำลายจนกว่าหมดขอบเขตจริงๆ (หมดตัวอ้างอิง)
 
std::shared_ptr<Pizza> std::shared_ptr<Pizza> pizza_information( PizzaFactory::PizzaType pizzatype )
{
        auto pizza = PizzaFactory::createPizza(pizzatype);
        std::cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
return pizza;
}

int main ()
{
        std::cout << "Start get pizza information" << std::endl;
        auto pizza1 = pizza_information( PizzaFactory::HamMushroom );
        auto pizza2 = pizza_information( PizzaFactory::Deluxe );
        auto pizza3 = pizza_information( PizzaFactory::Hawaiian );
        std::cout << "End get pizza information" << std::endl;

        return 0;
}

คอมไพล์และรันดู

 $ g++ pizza_shared_ptr_test.cpp -std=c++11
$ ./a.out  Start get pizza information
Constuct Pizza name: HamAndMushroomPizza
Price of 0 is 850
Constuct Pizza name: DeluxePizza
Price of 1 is 1050
 Constuct Pizza name: HawaiianPizza
Price of 2 is 1150
End get pizza information
Destroy Pizza name: HawaiianPizza
Destroy Pizza name: DeluxePizza
Destroy Pizza name: HamAndMushroomPizza 

share_ptr จะทำลาย object ครั้งเดียวเมื่อจบ main เราสามารถใช้งาน share_ptr แทน Pointer ได้โดยไม่ต้องคำนึงว่าจะ delete เมื่อไหร่ รู้สึกว่าชีวิตดีขึ้นจริงๆ :D

ความคิดเห็น

  1. shared_ptr มี reference count น่ะสิครับ object ถึงไม่ถูก destroy ไปพร้อมกับตัวแปร pizza ใน pizza_information()

    ตอบลบ

แสดงความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

ตัวเอ๋ยตัวผม

กลอนนี้ใช้เวลาประมาณ 20 นาทีเขียนขึ้นมาในห้องเรียนวิชาสัมนา 1 เพราะอาจารย์อยากให้แนะนำตัวเองเป็นกลอน ไม่รู้จะแต่งว่าไงเลยแต่งออกมาเป็นดอกสร้อย เห็นว่าพอใช้ได้เลยเอามาลงไว้เป็นอนุสร ๏ ตัวเอ๋ยตัวผม นิยมในพระพุทธศาสนา ตั้งจิตตั้งใจตั้งหน้า ใฝ่หาความรู้สู่ตน ตั้งใจศึกษาให้เชี่ยวชาญ ชำนาญในศาสตร์ที่ฝึกฝน ฝึกจิตฝึกสันดานให้เป็นคน เป็นชนในชาติที่ดีเอย ๚ะ๛

บันทึกการจัดงานศพ: พิธีฌาปนกิจศพ

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

ด้วยระลึกถึงคุณย่า บันทึกจากความทรงจำ

บันทึกนี้เขียนขึ้นเพื่อบันทึกความทรงจำของผมที่มีต่อคุณย่าที่ล่วงลับไปแล้วอย่างไม่มีวันหวนคืน คุณย่าเปรียบเหมือนฟางเส้นสุดท้ายที่ร้อยครอบครัวใหญ่ของเราเอาไว้ไม่ให้แตกแยก หลังจากที่เสียคุณปู่ไปเมื่อ 23 ปีก่อน เนื่องจากบริเวณจังหวัดกระบี่ ตรัง พังงา ภูเก็ต มีชาวจีนอาศัยอยู่มาก แต่มักจะเป็นชาวจีนที่อพยพมาไทยนานแล้ว จากการการสังเกตของผม ชาวจีนแถบนี้โดยมากน่าจะเป็นชาว เปอรานากัน หรือชาวจีนที่อพยพมาจากจีนแล้วตั้งถิ่นฐานอยู่ในแหลมลายูหรืออินโดนีเซีย แล้วหลังจากนั้นจึงอพยพมาอาศัยต่อที่ประเทศไทย จากการบอกเล่าของคุณแม่ ก๋งเคยเล่าให้ฟังว่าตอนยังเด็กเคยแจวเรือจ้างอยู่ที่ปีนัง คุณย่าเคยเล่าว่าเป็นชาวฮกเกี้ยน อีกทั้วจากรูปวาดคุณย่าทวดที่มีการเกล้ามวยผม สวมเสื้อคอลึก ส่วนทางบ้านมีการใช้คำเรียกจีนผสมไทยถิ่นใต้อยู่มาก ผู้หญิงทุกคนนิยมสวมผ้าปาเต๊ะ เสื้อลูกไม้ (เสื้อฉลุลายดอกไม้) อาหารการกินเป็นแบบชาวไทยถิ่นใต้ทุกประการ (กินน้ำพริก แกงส้มเก่งกันทุกคน ยกเว้นก๋ง :D) อีกทั้งก๋งเกิดที่ดินแดนแถบนี้ไม่ได้เดินทางมาจากเมืองจีน (บางทีเรียก เตี่ยต่อเตี่ย คือ ทวดมาจากจีน ส่วนสถานที่เกิดไม่แน่ใจว่าเป็นปีนังหรือไทย)