พอดีหาเวลาทดลองคุณสมบัติใหม่ๆ ของ C++11 ดูหลายอย่างน่าใช้งานมาก เพราะทำให้ชีวิตการเขียนโปรแกรม C++ ง่ายขึ้น อ่านไปอ่านมาไปสะดุดกับ std::shared_ptr ซึ่งอยู่ภายได้ header ชื่อ memory ว่าดีกว่าการใช้ new กับ delete เลยคิดว่าหากเราเอามาใช้กับ design pattern ละจะดีแค่ใหน เลยลองหาโค้ดตัวอย่างของ design pattern ดูก็พบ C++ Programming/Code/Design Patterns จึงเอาส่วนของ Abstract Factory มาลองแก้ไขดู หน้าตาเลยเป็นแบบนี้
คอมไพล์และรัน
$ 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 กัน
ทดลองใช้งานดู
คอมไพล์และรันดู
$ 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
// 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
$ ./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
shared_ptr มี reference count น่ะสิครับ object ถึงไม่ถูก destroy ไปพร้อมกับตัวแปร pizza ใน pizza_information()
ตอบลบ