27.คลาสและออบเจ็ค

C++ classes

ในการเขียนโปรแกรมเชิงวัตถุ คลาสเป็น program-code-template สำหรับสร้างออบเจ็ค
ในภาษา C++ ให้เราสามารถสร้างคลาสที่มีแนวคิดขยายมากจากโครงสร้างข้อมูล คลาสสามารถถูกกำหนดโดยการใช้คำสั่ง class และมีรูปแบบดังนี้:
class class_name
{
access_specifier_1:
    member1;
access_specifier_2:
    member2;
    ...
} object_names;
การสร้างคลาสคล้ายกับ structure ความแตกต่างคือคลาสสามารถมีฟังก์ชันเป็นสมาชิกของมันได้ และคลาสสามารถมีตัวกำหนดการเข้าถึง (Access specifier) เพื่อควบคุมการเข้าถึงสมากชิกในออบเจ็คได้
ในภาษา C++ นี่เป็นกำหนดการเข้าถึง
  • private: สมาชิกประเภทนี้สามารถเข้าถึงได้โดยคลาสและออบเจ็คเดียวกัน และนี่เป็นค่าเริ่มต้นเมื่อเราปล่อยให้ว่าง
  • protected: สมาชิกประเภทนี้สามารถเข้าถึงได้ในคลาสเดียวกัน (friends) และ extended classes
  • public: สมาชิกประเภทนี้สามารถเข้าถึงได้ทุกส่วนของโปรแกรม
เพื่อทำให้คุณเข้าใจดีขึ้น มาดูตัวอย่างเกี่ยวกับการสร้างโดยการใช้คลาส
class Circle
{
private:
    int r;
public:
    void setRadius(int n)
    {
        r = n;
    };
    float area(void)
    {
        return 3.14 * r * r;
    };
    float girth(void)
    {
        return 3.14 * 2 * r;
    };
} cir1;
ในตัวอย่างนี้ เราได้สร้างคลาสชื่อว่า Circle ซึ่งมันมีสมาชิกที่เป็นตัวแปรหนึ่งตัว และสมาชิกที่เป็นฟังก์ชัน 3 ตัว สมาชิกหรือตัวแปร r มีตัวกำหนดการเข้าถึงเป็น private นั้นหมายความว่าตัวแปรนี้ไม่สามารถเข้าถึงได้จากภายนอกคลาส สมาชิกสามตัวต่อมาเป็นเมธอด (หรือฟังก์ชัน) ซึ่งเป็นเหมือนกับฟังก์ชันในภาษา C++ แต่ภายในคลาสเราเรียกว่าเมธอด เมธอดเหล่านี้มีตัวควบคุมการเข้าถึงเป็นแบบ public นั่นหมายความว่ามันสามารถเข้าถึงได้จากนอกคลาส
มาดูตัวอย่างของโปรแกรมที่สร้างจากคลาสนี้
#include <iostream>

using namespace std;

class Circle
{
private:
    int r;
public:
    void setRadius(int n)
    {
        r = n;
    };
    float area(void)
    {
        return 3.14 * r * r;
    };
    float girth(void)
    {
        return 3.14 * 2 * r;
    };
} cir1;

int main ()
{
    int n;
    cout << "Enter circle radius: ";
    cin >> n;
    cir1.setRadius(n);
    cout << "Area " << cir1.area() << endl;
    cout << "Girth " << cir1.girth() << endl;
    return 0;
}
ในตัวอย่าง เราได้สร้างออบเจ็คชื่อว่า cir1 จากคลาส Circle ออบเจ็คนี้จะมีสมากชิกทุกอย่างเหมือนกับคลาสที่มันสร้างในตัวมันทั้งตัวแปรและฟังก์ชัน นั่นหมายความว่าคุณสามารถที่จะสร้างกราออบเจ็คก้ได้โดยใช้คลาสนี้ เมื่อออบเจ็คถูกสร้างแล้ว เราสามารถเข้าถึงข้อมูลภายในได้ตามของเหมาะสมตาม Access specifier ของมัน มาดูตัวอย่างเมื่อเรารันโปรแกรมนี้
Enter circle radius: 8
Area 200.96
Girth 50.24
อีกตัวอย่างหนึ่งจะแนะนำให้คุณรู้จักกับตัวดำเนินการขอบเขต (::) ซึ่งเป็นเครื่องหมายเซมิโคลอนสองอัน ตัวดำเนินการนี้จะอนุญาติให้เราสามารถสร้างฟังก์ชันข้างนอกคลาสได้ เพราะว่าในคลาสนั้นเราสามารถสร้างฟังก์ชันได้เพียงหนึ่งบรรทัด เหมือนที่คุณเห็นในคลาส Circle
#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
    string first_name, last_name;
public:
    string getFullname();
    string getCountry(int);
};

string Person::getFullname()
{
    return first_name + " " + last_name;
}

string Person::getCountry(int n)
{
    if (n == 1)
        return "US";
    else if (n == 2)
        return "Canada";
    else if (n == 3)
        return "Ukraine";
    else
        return "Unknown country";
}

int main()
{
    Person p1;
    p1.first_name = "Mateo";
    p1.last_name = "Marcus";

    Person p2;
    p2.first_name = "Thomas";
    p2.last_name = "Monte";

    Person p3;
    p3.first_name = "Dennis";
    p3.last_name = "Ritchie";

    cout << p1.getFullname() << " is in " << p1.getCountry(3) << endl;
    cout << p2.getFullname() << " is in " << p2.getCountry(2) << endl;
    cout << p3.getFullname() << " is in " << p3.getCountry(5) << endl;

    return 0;
}
ในตัวอย่างนี้ เรามีคลาส Person ซึ่งมีสมาชิกแบบตัวแปรสองตัวคือ first_name และ last_name และมีสมาชิกที่เป็นฟังก์ชันสองฟังก์ชันคือ getFullname และ getCountry สังเกตุว่าเราไม่ได้สร้างฟังก์ชันไว้ในคลาส แต่เราย้ายมันออกมาข้างนอกคลาส เพราะว่ามันจะทำให้ง่ายกว่าในการสร้างภายในคลาสของมันเอง เพื่ออ้างถึงสมากใดๆ ข้างนอกคลาส Person เราจะใช้ตัวดำเนินการขอบเขต Persion::getFullname และ Persion::getCountry เพื่อบอกให้คอมไพเลอร์รู้ และโปรแกรมจะให้ผลลัพธ์ดังต่อไปนี้
Mateo Marcus is in Ukraine
Thomas Monte is in Canada
Dennis Ritchie is in Unknown country

สรุปเรื่อง class และ object



สรุป class และ object ด้วยตัวอย่าง Bank Account


*********************************************************************************************************************

เริ่มต้นกับการเขียนโปรแกรมเชิงวัตถุ


 ภาษา C มีแนวคิดในการเขียนโปรแกรมแบบโครงสร้างที่เรียกว่า Structure programming หรือแบบกระบวนการ (Procedure programming) โดยมีจุดประสงค์หลักในการเขียนโปรแกรมให้เครื่องคอมพิวเตอร์สามารถประมวลผลและวิเคราะห์ข้อมูล ซึ่งชุดคำสั่งของโปรแกรมที่เขียนขึ้นมาเพื่อใช้ในการแก้ปัญหาเฉพาะด้าน โดยจะทำการแยกปัญหาใหญ่ ๆ ออกเป็นส่วนย่อย ๆ เรียกว่าโมดูล (Module) ซึ่งอาจประกอบไปด้วยเฮดเดอร์ไฟล์ (Hrader file) หลาย ๆ ไฟล์ (*.h) และ ซอร์สไฟล์ (Source file) หลาย ๆ ไฟล์ (*.c) แล้วค่อยแก้ไขไปทีละส่วนจนกว่าจะได้ผลลัพธ์ที่ต้องการ ซึ่งประยุกต์มาจากวิธีคิดของมนุษย์นั่นเอง

ข้อเสียของการเขียนโปรแกรมเชิงโครงสร้าง

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

การเขียนโปรแกรมเชิงวัตถุ

      จากข้อเสียดังกล่าวของภาษา C ทำให้มีการพัฒนาแก้ไขจุดบกพร่องดังกล่าวและเรียกว่าภาษาใหม่ที่สร้างขึ้นมาว่า C++ เป็นแนวคิดรูปแบบใหม่ที่นำมาใช้กันในปัจจุบัน ถึงแม้รูปแบบการเขียนจะค่อนข้างยากและมีความซับซ้อน แต่จะส่งผลดีต่อการแก้ไขโปรแกรมในระยะยาว ซึ่งแนวคิดนี้จะแยกปัญหา หรือแยกระบบงานออกเป็นส่วนย่อยเช่นกัน เพื่อลดความซับซ้อนให้น้อยลง ส่วนย่อยหรือโปรแกรมย่อยดังกล่าวจะเรียกว่าคลาส (Class) ภายในคลาสจะประกอบไปด้วยข้อมูลและฟังก์ชันการทำงาน ในการใช้งานโปรแกรมเมอร์จะสร้างตัวแทนของคลาสขึ้นมาทำงานเรียกว่าออบเจ็กต์ (Object) หากพูดให้เข้าใจได้ง่ายขึ้นออบเจ็กต์ คือ ตัวแปรของคลาสนั่นเองแต่แตกต่างจากตัวแปรปรกติตรงที่สามารถเก็บค่าได้มากกว่าหนึ่งค่าและสามารถเรียกฟังก์ชันการทำงานผ่านตัวแปรตัวนี้ได้เช่นเดียวกับที่เคยกล่าวถึงออบเจ็กต์ในเรื่องโครงสร้างข้อมูลนั่นเอง การเขียนโปรแกรมเชิงวัตถุจะพยามหาวัตถุจากโปรแกรมที่เคยสร้างมาแล้วมาประกอบในการสร้างโปรแกรมใหม่ทำให้ประหยัดเวลาในการสร้างโปรแกรมใหม่ และสามารถแก้ไขได้ง่ายเพราะวัตถุแต่ละชิ้นมีความเป็นอิสระต่อกันและวัตถุที่สร้างขึ้นมาแล้วสามารถนำกลับมาใช้ใหม่ได้อีก ซึ่งสอดคล้องกับหลักการในการนำมาใช้ใหม่ (Reusable)

วัตถุหรือออบเจ็กต์ (Object)

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

คลาส (Class)

      สิ่งต่าง ๆ ในโลกความเป็นจริงเราสามารถอธิบายได้ด้วย 2 องค์ประกอบหลัก คือ ข้อมูลและพฤติกรรมการทำงานตัวอย่าง เช่น พนักงาน (employees) เราสามารถอธิบายถึงตัวพนักงานในโลกความเป็นจริงได้ดังนี้
      1. เรามีพนักงานที่แตกต่างกันหลายประเภทดังนี้
  • พนักงานทั่วไป (employee)
  • ผู้จัดการ (manager)
  • ผู้ดูแล (supervisor)
      ประเภทหรือลักษณะต่าง ๆ ที่เป็นองค์รวมในโลกความเป็นจริงสามารถแปลงออกมาเป็นโครงสร้างข้อมูลหรือคลาสในมุมมองของการเขียนโปรแกรมได้
      2. สำหรับพนักงานจะประกอบไปด้วยข้อมูลดังนี้
  • ชื่อ (name)
  • อัตราเงินเดือน (pay rate)
      ข้อมูลในโลกความเป็นจริงซึ่งจะถูกเปลี่ยนเป็นตัวแปรสมาชิกของคลาส
      3. นอกจากนี้เรายังต้องการการทำงานบางอย่างเพิ่มเติมเช่น 
  • การจ้างพนักงาน
  • การดึงข้อมูลต่าง ๆ ของพนักงาน เช่น ชื่อ อัตราเงินเดือนฯ เป็นต้น
  • การคำนวณอัตราเงินเดือน
      สิ่งที่กล่าวมาข้างต้นถือเป็นพฤติกรรมหรือหน้าที่การทำงานในโลกความเป็นจริงซึ่งหมายถึงการทำงานหรือฟังก์ชัน (function) ในคลาส
      จากแนวคิดดังกล่าวการเขียนโปรแกรมเชิงวัตถุจึงเป็นการเขียนโปรแกรมที่ประกอบไปด้วย ข้อมูลและฟังก์ชันที่เกี่ยวข้องกันมารวมไว้ภายในคลาสเดียวกันทำให้สามารถนำเอาออบเจ็กต์ของคลาสมาอธิบายถึงการทำงานเหล่านี้ได้ คลาสจะมีลักษณะในการประกาศสำหรับ C++ ได้หลายลักษณะซึ่งมีโครงสร้างพื้นฐานที่สุดดังต่อไปนี้
โครงสร้างพื้นฐานของคลาส
รูปที่ 1. โครงสร้างพื้นฐานของคลาส
      รูปด้านบนเป็นตัวอย่างของการประกาศคลาสพื้นฐานที่ง่ายที่สุดซึ่งจะเริ่มต้นจากคำสำคัญ class แล้วตามด้วยชื่อของคลาสที่ต้องการ ภายในขอบเขตของการประกาศคลาสจะประกอบไปด้วยการประกาศตัวแปรและฟังก์ชันสมาชิกของคลาส และสิ้นสุดด้วยสัญลักษณ์ “;” เป็นการบอกถึงจุดสิ้นสุดของการประกาศคลาส การสร้างฟังก์ชั่นการทำงานของคลาสใน C++ สามารถทำได้สองลักษณะคือ
  1. เขียนการทำงานของฟังก์ชั่นไว้ในส่วนประกาศของคลาส (class header)
  2. เขียนการทำงานของฟังก์ชั่นแยกออกจากส่วนประกาศของคลาส

การเขียนการทำงานของฟังก์ชั่นไว้ในส่วนประกาศของคลาส

      วิธีนี้สามารถทำงานได้ดีกับคลาสที่มีการทำงานไม่ซับซ้อนมากเกินไป หากเป็นคลาสที่มีขนาดใหญ่จะทำให้ไล่การทำงานได้ลำบาก
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CGameUnit{
  int hp;
  int shield;
  int damage;
 
  void Attack()
  {
     cout << "Attack" << endl;
  }
 
  void Defense()
  {
     cout << "Defense" << endl;
  }
};
      ในตัวอย่างด้านบนเป็นการสร้างคลาส CGameUnit สำหรับตัวละครในเกมโดยประกอบไปด้วยตัวแปรสมาชิกหรือคุณสมบัติ (Attribute) ได้แก่ hp, damage, shield และมีฟังก์ชันการทำงานได้แก่ CGameUnit(), Attack(), และ Defense()

การเขียนการทำงานของฟังก์ชั่นแยกออกจากส่วนประกาศของคลาส

      วิธีนี้จะทำการเขียนฟังก์ชั่นการทำงานแยกออกจากส่วน header ของคลาสทำให้ส่วนของการประกาศคลาสดูเข้าใจได้ง่ายกว่าแบบแรกดังลักษณะต่อไปนี้
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CGameUnit{
  int hp;
  int shield;
  int damage;
  void Attack();
  void Defense();
};
 
void CGameUnit::Attack()
{
  cout << "Attack" << endl;
}
 
void CGameUnit::Defense()
{
  cout << "Defense" << endl;
}
      โค้ดที่ต้องเขียนจะมีขนาดใหญ่ขึ้นกว่าเดิมเล็กน้อยแต่สะดวกในการไล่การทำงานของโปรแกรม สำหรับการเขียนการทำงานของฟังก์ชันแยกออกจากส่วนประกาศคลาส จะเขียนเช่นเดียวกับฟังก์ชันทั่วไปใน C++ เพียงแต่ต้องระบุให้คอมไพเลอร์รู้ว่าฟังก์ชันที่เขียนขึ้นนี้เป็นของคลาสที่เราได้ทำการสร้างขึ้น ด้วยการเพิ่มชื่อคลาสและตามด้วยสัญลักษณ์ double colon “::” ไว้หน้าชื่อฟังก์ชัน สำหรับสัญลักษณ์ “::” จะมีชื่อเฉพาะว่า Operator of Scope ซึ่งหมายถึงความเป็นเจ้าของโดยฝั่งซ้ายของสัญลักษณ์นี้จะเป็นเจ้าของของฝั่งขวา ตัวอย่าง เช่น CGameUnit::Attack() หมายความว่า ฟังก์ชัน Attack() เป็นของคลาส CGameUnit นั่นเอง

องค์ประกอบหลักของการเขียนโปรแกรมเชิงวัตถุ
      ในการเขียนโปรแกรมเชิงวัตถุจะมีการทำงานที่เกี่ยวข้องกับคลาสและออบเจ็กต์อย่างหลีกเลี่ยงไม่ได้ ซึ่งมีหลักการที่ใช้ในการออกแบบการทำงานของคลาสดังต่อไปนี้
1.      Abstraction เป็นการคัดเลือกเอาเฉพาะองค์ประกอบของข้อมูลที่จำเป็นในการแก้ปัญหามาใช้ในการออกแบบคลาส
2.      Encapsulation การซ่อนข้อมูลของคลาสโดยการกำหนดสิทธิในการเข้าถึงข้อมูลเพื่อความชัดเจนและง่ายในการใช้งานในการใช้งานของคลาส
3.      Inheritance เป็นการสืบทอดคุณสมบัติของคลาสเพื่อช่วยให้สามารถนำเอาฟังก์ชันการทำงานต่าง ๆ กลับมาใช้ใหม่รวมไปถึงการทำให้โค้ดอ่านได้ง่ายขึ้น
4.      Polymorphism เป็นการพูดถึงฟังก์ชันการทำงานในคลาสที่มีชื่อเดียวกันแต่มีพฤติกรรมการทำงานที่แตกต่างกัน
      ในตำราบางเล่มอาจเพิ่มการดักจับข้อผิดพลาด ( Exception Handling) เป็นปัจจัยที่ห้าในองค์ประกอบหลักของการเขียนโปรแกรมเชิงวัตถุเช่นกัน


Abstraction
      Abstraction เป็นขั้นตอนแรกของการเขียนโปรแกรมเชิงวัตถุ ซึ่งเป็นการทำงานในขั้นตอนของการออกแบบสำหรับคลาส โดยทำการคัดเลือกเอาเฉพาะส่วนที่มีองค์ประกอบสำคัญในการแก้ปัญหาเท่านั้นในการทำงาน ตัวอย่างเช่น หากปัญหาเป็นการเขียนโปรแกรมสำหรับคำนวณค่าเงินเดือนของพนักงานหนึ่งคน เราสามารถวิเคราะห์ได้ว่าพนักงานหนึ่งคนอาจประกอบไปด้วยข้อมูลต่อไปนี้
·         name               ชื่อของพนักงาน
·         gender             เพศของพนักงาน
·         age                   อายุของพนักงาน
·         height              ความสูงของพนักงาน
·         weight              น้ำหนักของพนักงาน
·         hour                 ชั่วโมงการทำงาน
·         payrate            รายได้ต่อชั่วโมง
      และพนักงานแต่ละคนจะมีการทำงานหรือพฤติกรรมดังต่อไปนี้
·         Eating()             กินข้าว
·         Walking()          การเดิน
·         Driving()            ขับรถ
·         CalculateSaraly()           การคิดเงินเดือน
      นี่คือส่วนหนึ่งของข้อมูลที่สามารถมีได้ในตัวพนักงานคนหนึ่ง แต่ในความเป็นจริงแล้วปัญหาที่วิเคราะห์คือการเขียนโปรแกรมเพื่อวิเคราะห์หาค่าเงินเดือนของพนักงานซึ่งมีข้อมูลที่คัดเลือกเพื่อนำมาใช้ในการแก้ปัญหาจะได้แก่ พนักงานเป็นใคร มีชั่วโมงการทำงานเท่าไหร่ รายได้ต่อชั่วโมงเป็นเท่าใด และรวมไปถึงพฤติกรรมในการทำงานได้แก่การคำนวณหาเงินเดือน ดังนั้นการคัดเลือกเฉพาะส่วนที่เกี่ยวข้องกับปัญหาทำให้ได้คลาสที่ใช้ในการแก้ปัญหาดังนี้
1
2
3
4
5
6
class CEmployee{
  char name[32];
  float workhour;
  int payrate;
  float CalculateSaraly();
};
      ดังนั้น Abstraction จึงหมายถึงการกำจัดข้อมูลที่ไม่เกี่ยวข้องทิ้งไปให้เหลือเฉพาะข้อมูลที่จำเป็นต่อการแก้ปัญหาเท่านั้น เนื่องจากเป็นไปไม่ได้และไม่จำเป็นที่จำนำเอาคุณสมบัติทุกข้อที่มีจริงของวัตถุนั้นมาเขียนโปรแกรมดังตัวอย่างของคลาสพนักงานข้างต้น
Encapsulation
      การซ่อนข้อมูล หรือ Encapsulation คือ ความสามารถในการจัดการกับระดับการเข้าถึงข้อมูลภายในของคลาสแบ่งออกได้เป็นสามระดับคือ private, protected และ public คำสำคัญหรือ keyword เหล่านี้ใช้ได้กับคลาส และโครงสร้างข้อมูล ซึ่งใช้ระบุระดับการเข้าถึงหรือระดับในการมองเห็นจากคลาสอื่น ๆ
      สาเหตุที่ต้องมีการซ่อนข้อมูลเนื่องจากในปัจจุบันการพัฒนาโปรแกรมมักจะทำงานกันเป็นทีมและแยกส่วนกันพัฒนา ดังนั้นจึงมีการใช้การซ่อนข้อมูลที่จำเป็นบางส่วนเพื่อป้องกันไม่ให้คนอื่นในทีมเปลี่ยนแปลงค่าของสมาชิกภายในคลาส ผ่านทางการทำงานต่าง ๆ ที่เป็นสมาชิกภายในของคลาสที่สร้างขึ้นจนทำให้เกิดข้อผิดพลาดในการทำงานได้ ตัวอย่าง เช่น หากเราทำการสร้างคลาสสำหรับเครื่องยนต์ดังนี้
1
2
3
4
5
6
7
8
9
class CEngine{
public:
  float s;
  float t;
  float GetSpeed()
  {
     return s/t;
  }
};
      จากโค้ดตัวอย่างภายในคลาส CEngine มีฟังก์ชันสมาชิก คือ GetSpeed() ซึ่งทำหน้าที่ในการหาความเร็วของเครื่องยนต์ จะเกิดอะไรขึ้นหากสมาชิกอื่นในทีมมีการเรียกใช้งานดังโค้ดต่อไปนี้
1
2
3
4
5
6
7
void main(void)
{
  CEngine engine;
  engine.s = 100;
  engine.t = 0;
  cout << "Speed = " << engine.GetSpeed() << endl;
}
      หากสังเกตจะเห็นได้ว่ามีการกำหนดค่า t เป็น 0 จากนั้นเรียกใช้งานฟังก์ชัน GetSpeed() ผ่านทางออบเจ็กต์ engine ของคลาส CEngine ภายในฟังก์ชันนี้จะทำการหาค่าความเร็วของเครื่องยนต์ด้วยการนำระยะทาง (s) หารด้วยเวลา (t) แต่เนื่องจากตัวหารเป็น 0 ทำให้เกิดข้อผิดพลาดในโปรแกรมได้ ซึ่งข้อผิดพลาดนี้เกิดจาก การกำหนดค่า t ให้เป็น 0 อาจเกิดจากความเผอเรอ หรือไม่เข้าใจการทำงานภายในของฟังก์ชัน GetSpeed() เพื่อแก้ปัญหานี้เราจึงต้องอาศัยการกำหนดระดับในการเข้าถึงข้อมูลเพื่อป้องกันปัญหาดังกล่าว
      ดังนั้นจึงกล่าวได้ว่าคำสำคัญ private, protected และ public เป็นการกำหนดระดับการซ่อนข้อมูลของ ตัวแปร ฟังก์ชั่น ที่เป็นสมาชิกของคลาสออกจากฟังก์ชันภายนอกอื่น ๆ หรือจากออบเจ็กต์ของตัวคลาสเองซึ่งมีรายละเอียดของการเข้าถึงแต่ละระดับดังต่อไปนี้
private
      ตัวแปรและฟังก์ชั่นสมาชิกต่าง ๆ ภายในคลาสสามารถเรียกใช้งานได้จากฟังก์ชั่นของตัวคลาสที่เป็นเจ้าของเท่านั้น เราจะใช้คำสงวน private กับข้อมูลที่มั่นใจแล้วว่าไม่ยุ่งเกี่ยวกับการทำงานภายนอกอื่น ๆ ในโปรแกรมจะมีการใช้งานส่วนข้อมูลที่อยู่ใน private เฉพาะคลาสนี้เท่านั้น ตัวอย่าง เช่น หากเราจะสร้างคลาสของมนุษย์ขึ้นมา ส่วนที่ควรจะอยู่ระดับการเข้าถึงแบบ private จะได้แก่ ลายนิ้วมือ ลายม่านตา สปีชีส์ เป็นต้น จะเห็นว่าข้อมูลเหล่านี้เป็นข้อมูลที่แสดงถึงอัตลักษณ์ของมนุษย์และมีคุณสมบัติเฉพาะเป็นของตัวเอง ซึ่งสามารถแสดงตัวอย่างของคลาสได้ดังต่อไปนี้
1
2
3
4
5
6
7
class CHuman{
private:
  int finger_print;
  int iris;
  int species;
  void SetFingerPrint(int fingerprint);
};
      ภายใต้ส่วนประกาศ private จะเห็นได้ว่าเราสามารถประกาศได้ทั้งตัวแปรสมาชิกและฟังก์ชันสมาชิก ซึ่งส่วนประกาศที่ตามหลังการประกาศในการเข้าถึงข้อมูลทั้งหมดจะถือว่าอยู่ในระดับการเข้าถึงดังกล่าวด้วย ทั้งนี้ภายในคลาสหนึ่ง ๆ เราสามารถประกาศระดับการเข้าถึงวางไว้ในตำแหน่งใดก็ได้ภายในส่วนประกาศคลาส และสามารถประกาศซ้ำได้มากกว่าหนึ่งครั้ง
      จะเห็นได้ว่าคุณสมบัติเหล่านี้เป็นคุณสมบัติที่ไม่สามารถเปลี่ยนแปลงได้ หากจะทำการเปลี่ยนแปลงต้องอาศัยลักษณะที่ไม่เป็นไปตามธรรมชาติ หรือเป็นการฝืนกฎของธรรมชาตินั่นเอง ซึ่งจะกล่าวถึงในเรื่องของคำสำคัญ “friend”
Private by Default
      โดยปรกติหากไม่กำหนดระดับการเข้าถึงไว้ ทุก ๆ สมาชิกของคลาสจะมีสถานะเป็น private by Default โดยอัตโนมัติ
protected
      ตัวแปรและฟังก์ชั่นต่าง ๆ ที่อยู่ใน protected สามารถเรียกใช้งานได้จากฟังก์ชั่นของตัวคลาสที่เป็นเจ้าของและฟังก์ชันของคลาสที่สืบถอดจากคลาสนี้เท่านั้น ซึ่งจะเข้าใจในส่วนนี้ได้ต้องรู้จักการสืบทอดคุณสมบัติ หรือ Inheritance ของคลาสก่อน ในการเขียนโปรแกรมเชิงวัตถุนั้นเราสามารถสร้างการสืบทอดคุณสมบัติได้ เช่นเดียวกับการถ่ายทอดพันธุกรรมของสิ่งมีชีวิตที่เป็นการถ่ายทอดคุณสมบัติจากบรรพบุรุษไปยังลูกหลาน คลาสที่เป็นต้นทางของคุณสมบัติจะเรียกว่าคลาสแม่ (Parent class หรือ Super class) และคลาสที่ได้รับการถ่ายทอดคุณสมบัตินั้นจะเรียกว่าคลาสลูก (Child class หรือ Sub class) ตัวอย่าง เช่น กรุ๊ปเลือด เพศ ชื่อ นามสกุล จะเห็นได้ว่าคุณสมบัติเหล่านี้แม้จะมีในคลาสแม่แต่จะถูกส่งต่อไปให้ลูกด้วยเช่นกัน เพียงแต่จะมีรายละเอียดที่แตกต่างกัน เช่น แม่มีเพศเป็นผู้หญิง แต่ลูกอาจจะมีเพศเป็นชายเป็นต้น
1
2
3
4
5
6
7
8
9
10
11
12
class CHuman{
private:
  int finger_print;
  int iris;
  int species;
  void SetFingerPrint(int fingerprint);
protected:
  char blood_type;
  char gender;
  char name[32];
  char surname[32];
};
      ลักษณะข้อมูลที่ควรอยู่ในระดับการเข้าถึงแบบ Protected ควรเป็นข้อมูลที่สามารถเปลี่ยนแปลงได้แต่ไม่ได้หมายความว่าจะเปลี่ยนแปลงได้ง่ายนัก หากจะทำการเปลี่ยนแปลงจะเป็นการเปลี่ยนแปลงจากตัวเจ้าของเป็นคุณสมบัติเหล่านั้นเป็นคนตัดสินใจเองเท่านั้นโดยทั่วไปเราจะทำการสร้างฟังก์ชันขึ้นมารองรับการเปลี่ยนแปลงค่าเหล่านี้โดยประกาศไว้ในส่วนของ public จะไม่นิยมให้เข้าถึงข้อมูลผ่านทางตัวแปรสมาชิกในคลาสโดยตรง
public
      ตัวแปรและฟังก์ชั่นต่าง ๆ ที่อยู่ใน public สามารถเรียกใช้งานได้จากออบเจกต์ของคลาส ตัวแปรและฟังก์ชันสมาชิกที่อยู่ภายในคลาสนี้เป็นข้อมูลที่สามารถเปลี่ยนแปลงได้จากภายนอกผ่านทางออบเจ็กต์ของคลาส ตัวอย่าง เช่น สีผม เสื้อผ้า รองเท้า
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CHuman{
private:
  int finger_print;
  int iris;
  int species;
  void SetFingerPrint(int fingerprint);
protected:
  char blood_type;
  char gender;
  char name[32];
  char surname[32];
public:
  char hair_color;
  char shirt;
  char shoes;
  CHuman();
};
      จะเห็นได้ว่าข้อมูลที่ยกตัวอย่างมาเป็นข้อมูลที่มีการเปลี่ยนแปลงได้บ่อยและไม่มีผลกับภายในของตัวบุคคลที่เป็นเจ้าของคุณสมบัติเหล่านี้มากนัก ในการใช้งานหมายความว่าฟังก์ชันต่าง ๆ จากภายนอกคลาสสามารถกำหนดคุณสมบัติหรือตัวแปรสมาชิกของคลาสเหล่านี้ได้ผ่านทางออบเจ็กต์ของคลาสโดยตรง
ออบเจ็กต์ของคลาสจะเข้าถึงได้เฉพาะส่วนที่เป็น public ของคลาสเท่านั้น
Accessor และ Mutator Function
      ในบางครั้งมีความจำเป็นต้องเข้าถึงข้อมูลที่อยู่ในส่วนของ private และ protected ผ่านทางออบเจ็กต์ของคลาส ซึ่งโดยทั่วไปแล้วจะทำการสร้างฟังก์ชันสมาชิกของคลาสไว้ในส่วน public เพื่อจัดการกับข้อมูลดังกล่าว โดยแบ่งออกเป็นฟังก์ชัน Accessor และ Mutator ซึ่งเป็นชื่อที่ใช้เรียกฟังก์ชันที่มีลักษณะการทำงานดังต่อไปนี้
Accessor
      Accessor คือฟังก์ชั่นของคลาสโดยเรียกผ่านการทำงานของฟังก์ชันจากออบเจ็กต์ของคลาส ซึ่งอาจจะเป็นฟังก์ชันที่มีการคืนค่าออกมาหรือการแสดงผล แต่จะไม่มีการเปลี่ยนแปลงค่าของตัวแปรสมาชิกภายในของคลาส ตัวอย่าง เช่น
1
2
3
4
5
6
7
class A{
private:
  int x;
public:
  int GetX(){ return x;}
  void DisplayX(){cout << “X is”<<x;}
};
      ฟังก์ชัน GetX() ของคลาส A มีการคืนค่าของตัวแปรของคลาสจึงเป็น Accessor function ในขณะเดียวกันฟังก์ชัน DisplayX() มีการแสดงค่าของตัวแปรของคลาสออกมาสู่ภายนอกจึงเป็น Accessor function
Mutator
      Mutator คือฟังก์ชั่นที่มีการเปลี่ยนแปลงค่าภายในของตัวแปรภายในของคลาสผ่านทางออบเจ็กต์ ดังตัวอย่างต่อไปนี้
1
2
3
4
5
6
7
class A{
private:
  int x;
public:
  void SetX(int x){this->x=x;}
  void SetX(){x=5;}
};
      ตัวอย่างฟังก์ชันทั้งสองถือว่าเป็น Mutator function เนืองจากมีการเปลี่ยนแปลงค่าตัวแปรสมาชิกของคลาส
สรุป Encapsulation
      ในการทำงานจริงบางครั้งการกำหนดระดับการเข้าถึงอาจเป็นไปได้ยาก เนื่องจากลักษณะและมุมมองต่อปัญหาของแต่ละคนย่อมไม่เหมือนกัน ขึ้นอยู่กับว่ามองประเด็นของปัญหาในมุมมองใดเป็นสำคัญ อาจมีบางคนคิดว่าให้ใส่เป็น private กับสมาชิกภายในคลาสทุกตัวเพราะ Encapsulation เน้นความปลอดภัยของข้อมูลดังนั้นน่าจะดีกว่าถ้าให้ข้อมูลทุกตัวปลอดภัย ซึ่งความคิดนี้ไม่ถือว่าถูกและผิดแต่อย่างใด แต่ขอให้ลองนึกถึงการที่ต้องสร้างฟังก์ชัน Accessor และ Mutator สำหรับทุกตัวแปรสมาชิกในคลาสว่าเป็นงานที่เยอะเพียงใด และจำเป็นหรือไม่ ดังนั้นในที่นี้จึงแนะนำหลักการคร่าว ๆ สำหรับการพิจารณาระดับการเข้าถึงภายในคลาสดังนี้
1.      หากรู้ว่าตัวแปรและฟังก์ชันสมาชิกภายในคลาส ถูกเปลี่ยนค่าจะทำให้เกิดความเสียหายกับการทำงานของโปรแกรมแน่นอนให้ใส่ไว้ในส่วนของ private เพื่อป้องกันการเปลี่ยนแปลงค่าเหล่านั้น
2.      หากคลาสที่สร้างขึ้นมีการส่งต่อไปยังคลาสลูกแน่นอนให้กำหนดไว้ในส่วนของ protected
3.      หากไม่แน่ใจว่าข้อมูลที่ต้องการอยู่ในส่วนใดให้กำหนดไว้ในส่วนของ private จนเมื่อเห็นภาพการทำงานของโปรแกรมชัดเจนขึ้น ค่อยกำหนดระดับการเข้าถึงกับข้อมูลนั้นใหม่
 ****************************************************************************************

คอนสตรัคเตอร์

คอนสตรัคเตอร์เป็นฟังก์ชันพิเศษที่จะถูกเรียกใช้เมื่อออบเจ็คถูกสร้าง เป้าหมายของมันเพื่อสำหรับกำหนดค่าเริ่มต้นให้กับออบเจ็ค คอนสตรัคเตอร์ใช้ชื่อเดียวกับคลาส มาดูตัวอย่างคลาสที่มีคอนสตรัคเตอร์
#include <iostream>

using namespace std;

class Circle
{
public:
    int r;
public:
    Circle(int);
    float area (void);
};

Circle::Circle(int a)
{
    r = a;
}

float Circle::area()
{
    return 3.14 * r * r;
};

int main()
{
    Circle c1(2);
    Circle c2(5);
    cout << "Circle 1's area = " << c1.area() << endl;
    cout << "Circle 2's area = " << c2.area() << endl;
    return 0;
}
ในตัวอย่าง เราได้เปลี่ยนแปลงโค้ดบางส่วนของคลาส Circle จากตัวอย่างก่อนหน้า ในตัวอย่างนี้เราได้เพิ่มคอนสตรัคเตอร์เข้ามา คอนสตรัคเตอร์เป็นฟังก์ชันพิเศษที่มีชื่อเหมือนคลาสของมัน มันสามารถมีพารามิเตอร์จำนวนเท่าไหร่ก็ได้เหมือนฟังก์ชันทั่วไป ในตัวอย่างจะเห็นว่าเรามี 1 พารามิเตอร์คือรัศมีของวงกลมที่จะถูกกำหนดเมื่อออบเจ็คถูกสร้าง และนี่เป็นผลลัพธ์ของโปรแกรม
Circle 1's area = 12.56p
Circle 2's area = 78.5

Overloading constructors

Overloading constructors เป็นคอนสตรัคเตอร์ที่มีชื่อเดียวกันแต่มีพารามิเตอร์หรือประเภทข้อมูลที่แตกต่างกัน คอมไพเลอร์จะตัดสินใจอัตโนมัติที่จะเลือกใช้ที่มีพารามิเตอร์และประเภทข้อมูลตรงกันเมื่อออบเจ็คถูกสร้าง มาดูตัวอย่าง
Circle::Circle()
{
}
Circle::Circle(float a)
{
    r = (int) a;
}
Circle::Circle(int a)
{
    r = a;
}
Circle::Circle(int a, int b)
{
    r = a + b;
}

สอน C++: การทำ function overloading เบื้องต้น

ในตัวอย่างเรามี 4 Overloading constructors แต่ละคอนสตรัคเตอร์จะมีจำนวนพารามิเตอร์และประเภทข้อมูลที่ต่างกัน
ในบทนี้ เราได้ครอบคลุมถึงพื้นฐานเกี่ยวกับคลาสในภาษา C++ อย่างไรก็ตาม คลาสยังมีเนื้อหามากกว่านี้ ที่ยังไม่ได้กล่าวถึง
************************************************************************************
https://medium.com/@iBookky/%E0%B8%A3%E0%B8%B9%E0%B9%89%E0%B8%88%E0%B8%B1%E0%B8%81-class-object-%E0%B9%80%E0%B8%9A%E0%B8%B7%E0%B9%89%E0%B8%AD%E0%B8%87%E0%B8%95%E0%B9%89%E0%B8%99-250044991f48
https://www.cp.eng.chula.ac.th/~chate/2110210/objectAndClasses.pdf