40.การสร้างฟังก์ชันใช้เอง (Programmer Defined Function)

การสร้างฟังก์ชันใช้เอง (Programmer Defined Function)
แบ่งเป็นหัวข้อย่อยดังนี้
การทำงานของโปรแกรมในภาษา C++ จะเริ่มต้นที่ฟังก์ชัน main( ) โปรแกรมหนึ่ง ๆ อาจะประกอบด้วยชุดคำสั่ง ที่ประมวลผล หรือทำงานต่างหน้าที่กัน ชุดคำสั่งชุดที่ 1 อาจรับข้อมูลเข้ามาเก็บไว้ในหน่วยความจำ ชุดคำสั่งชุดที่ 2 อาจเรียงลำดับข้อมูล
ชุดคำสั่งที่ 3 คำนวณหาค่าเฉลี่ย ชุดคำสั่งที่ 4 อาจทำหน้าที่แสดงผลออกที่จอภาพ เป็นต้น การนำชุดคำสั่งซึ่งทำงานในหน้าที่ต่าง ๆ กัน
ไปรวมกันอยู่ในฟังก์ชัน main( ) ทั้งหมด ทำให้โปรแกรมมีลักษณะซับซ้อน คำสั่งที่เรียงลดหลั่นกันมาทำให้ดูลายตา อ่านได้ยาก ขาดคุณสมบัติที่เรียกว่าเรียบง่าย สบายตา (Simplicity and readability) เราจึงนิยมนำชุดคำสั่งที่ทำหน้าที่ประมวลผลงานหนึ่ง ๆ ไปเขียนรวมกันเป็นฟังก์ชัน หรือ เรียกทั่ว ๆ ไป เป็น มอดุล (module)
ฟังก์ชันในภาษา C++ อาจนำมาจากฟังก์ชันสำเร็จรูป ที่มีอยู่ใน standard C++ Library เช่น setw (ใช้กำหนดความกว้างของฟิลด์ที่จะแสดงผลจำนวนเลข) หรือ sqrt ใช้หาค่ารากที่สองของจำนวนจริงใด ๆ ฟังก์ชันใดที่โปรแกรมเมอร์เขียนขึ้นมาเพื่อใช้ประมวลผลงานหนึ่ง ๆ เรียกว่า programmer defined function
แนวคิดเรื่องฟังก์ชัน เกิดจากแนวคิดการแก้ปัญหาแบบ Divide and conquer แบ่งปัญหาออกเป็นส่วนย่อย ๆ (module) ส่วนย่อย ๆ นี้จะทำหน้าที่แก้ปัญหาในส่วนนั้น ๆ ให้สมบูรณ์ในตัวมันเอง มีลักษณะง่ายและเข้าใจขั้นตอนการทำงานได้ไม่ยากนัก
ประโยชน์การแบ่งโปรแกรมยาว ๆ ให้เป็นฟังก์ชัน คือ ฟังก์ชันมีการทำงานตามจุดประสงค์ทีแน่นอน เราอาจเขียนโปรแกรมตรวจสอบการทำงานของฟังก์ขัน โดยแยกออกจาโปรแกรมหลัก เพราะฟังก์ชันมีขนาดเล็ก การตรวจสอบจึงทำได้ง่ายและสะดวกกว่า ฟังก์ชันใดเมื่อผ่านการตรวจสอบมาดีแล้ว เมื่อนำไปใช้รวมกับโปรแกรมหลัก ก็ไม่จำเป็นต้องตรวจสอบซ้ำตรงฟังก์ชันนี้อีก เช่น เขียนฟังก์ชันหาค่าเฉลี่ยของจำนวนเลข n ชุด เมื่อตรวจสอบแล้วพบว่าการทำงานของฟังก์ชันนี้ไม่มีข้อบกพร่อง เราสามารถนำฟังก์ชันไปใช้ในโปรแกรมอื่น ๆที่ต้องการหาค่าเฉลี่ยได้อีกด้วย วิธีการเขียนฟังก์ชันเพียงครั้งเดียวแล้วนำไปใช้กับโปรแกรมอื่น ๆ ได้โดยที่ไม่ต้องเขียนโปรแกรมซ้ำนี้ เรียกว่า การนำกลับมาใช้ใหม่ (reusability) เป็นการประหยัดเวลาในการพัฒนาโปรแกรมขนาดใหญ่ ๆ ได้เป็นอย่างมาก
การเขียนโปรแกรมในลักษณะ มอดุล หรือฟังก์ชันนี้ช่วยลดความยาวและความซับซ้อนของโปรแกรม ชุดคำสั่งบางคำสั่งที่อาจเกิดขึ้นซ้ำ ๆ ตรงส่วนต่าง ๆ ของโปรแกรม เมื่อถูกนำไปทำเป็นมอดุล หรือฟังก์ชัน ชุดคำสั่งที่ปรากฏซ้ำ ๆ นั้น จะถูกเขียนแทนด้วยคำสั่งที่เรียกใช้ฟังก์ชันนั้นเพียงบรรทัดเดียว
การแบ่งโปรแกรมขนาดใหญ่ออกเป็นมอดุล ทำให้โปรแกรมเมอร์หลาย ๆ คนสามารถทำงานคู่ขนานกันไปในโครงการเดียวกัน โดยต่างคนต่างเขียนฟังก์ชันที่ได้รับมอบหมาย จากนั้นนำฟังก์ชั่นทั้งหมดมารวมกันเป็นโครงการ ทำให้ระยะเวลาในการเขียนโปรแกรมในโครงการหนึ่ง ๆ สั้นลง
ฟังก์ชันหรือมอดุลหนึ่ง ๆ ซึ่งถูกเขียนให้ประมวลผลงานหนึ่งสำเร็จตามจุดประสงค์นั้นเป็นการสนับสนุนแนวคิดที่เรียกว่า ซ่อนรายละเอียดที่ไม่จำเป็นต้องรู้ (abstraction) ฟังก์ชันประกอบด้วยรายละเอียดของขั้นตอนในการทำงานชิ้นนั้น โปรแกรมเมอร์สามารถอ้างถึงหรือเรียกใช้ฟังก์ชันนั้นโดยไม่ต้องสนใจในรายละเอียดของฟังก์ชันนั้นว่ามันทำงานอย่างไร อาจมองฟังก์ชันหรือมอดุล เป็น “ กล่องดำ ” (black box) เพียงป้อนข้อมูลอินพุตให้ถูกต้อง ก็จะไดผลลัพธ์จากกล่องดำนั้น ใน standard C++ library มีฟังก์ชันใช้คำนวณหารค่าล็อกการิธึม เราสามารถนำฟังก์ชันนั้นมาหาค่า log ของจำนวนใด ๆ ได้โดยไม่ต้องไปรู้ในรายละเอียดว่าฟังก์ชันนั้นใช้วิธีเปิดตารางหาค่า log หรือคำนวณโดยใช้การประมาณค่าจากอนุกรม การซ่อนรายละเอียดจะช่วยลดเวลาการพัฒนาโปรแกรม และเพิ่มความมั่นใจว่าโปรแกรมไม่มีข้อผิดพลาด (increase its quality)
ฟังก์ชันหนึ่ง ๆ ควรมีความยาวเท่าใด ถึงแม้จะไม่มีการจำกัดความยาวของชุดคำสั่ง โปรแกรมเมอร์หลายคนเสนอแนะว่า ฟังก์ชันควรมีขนาดยาวพอเหมาะกับ 1 จอภาพ เพื่อที่สามารถอ่านคำสั่งในฟังก์ชันได้ตลอด ฟังก์ชันที่มีขนาดเล็กนั้นง่ายต่อทำความเข้าใจและการแก้ไข ฟังก์ชันหนึ่งควรทำงานหรือเกิดผลลัพธ์เพียง 1 งาน ถ้าเขียนฟังก์ชันใดแล้วพบว่ามันยาวเกินไป แสดงว่าควรแตกฟังก์ชันนั้นออกเป็นฟังก์ชันย่อย ๆ อีกได้แล้ว
รูปแบบของฟังก์ชัน
จะแสดงตัวอย่างการเขียนโปรแกรมที่ประกอบด้วยฟังก์ชันที่เราเขียนขึ้นใช้เองดังนี้
โปรแกรม 4.1 เมื่อป้อนค่ารัศมีของวงกลม โปรแกรมจะคำนวณหาพื้นที่ของวงกลมและเส้นรอบวง
 
[Source code: circle.cpp]
จากโปรแกรม 4.1 จะแบ่งเนื้องานออกเป็น 4 งานย่อย ๆ คือ ส่วนที่ทำหน้าที่รับข้อมูลได้แก่ฟังก์ชัน input_radius ( ) ส่วนที่ 2 เป็นส่วนที่คำนวณหาพื้นที่ของวงกลม ฟังก์ชัน find_area ส่วนที่ 3 ทำหน้าที่คำนวณความยาวเส้นรอบวงกลม compute_perimeter ( ) ส่วนที่ 4 เป็นส่วนที่แสดงผลการคำนวณ คือฟังก์ชัน display ( ) งานแต่ละส่วนถูก แยกเขียนเป็นฟังก์ชัน 4 ฟังก์ชัน จะเห็นว่าจำนวนบรรทัดในฟังก์ชัน main ( ) ซึ่งเป็นส่วนหลักของโปรแกรมจะสั้นลง ในแต่ละบรรทัดจะสื่อความหมายชัดเจนว่ากำลังทำอะไร รายละเอียดของแต่ละส่วน เช่นคำนวณหาพื้นทีอย่างไร จะถูกซ่อนไว้ในฟังก์ชัน
ฟังก์ชันที่เขียนขึ้นใช้เองจะประกอบด้วย 2 ส่วนใหญ่ คือ ส่วนหัว (header) และส่วนที่เป็นร่างของฟังก์ชัน (body)
ส่วนหัวของฟังก์ชัน มีรูปแบบการเขียนดังนี้
return_type function_name ( [ type [ parameter_name] ] ,…)
เช่น ฟังก์ชัน find_area จะมีส่วนหัวของฟังก์ชัน ในบรรทัดที่ 31
จะเห็นว่าไม่ต้องมี เซมิโคลอน ต่อท้าย
หมายเลข 1 คือ return type คือชนิดของข้อมูลที่ฟังก์ชันส่งค่ากลับคืนไปยังโปรแกรมที่เรียกใช้งาน
หมายเลข 2 คือ function name เป็นชื่อของฟังก์ชัน การตั้งชื่อฟังก์ชันมีกฎเกณฑ์เช่นเดียวกับการตั้งชื่อตัวแปร ต้องขึ้นต้นด้วยตัวอักษรเท่านั้น อักษรถัดไปจะเป็นตัวเลขหรือตัวอักษรหรือเครื่องหมาย under score ผสมปนกันก็ได้ ชื่อของฟังก์ชันจะยาวเท่าใดก็ได้ แต่คอมไพเลอร์จะเก็บชื่อฟังก์ชันไว้เพียง 32 ตัวอักษรแรกเท่านั้น ชื่อฟังก์ชันควรตั้งให้สอดคล้องและสื่อความหมายกับงานที่ฟังก์ชันทำ เพื่อทำให้การอ่านโปรแกรมแล้วสามารถเข้าใจได้ง่าย
ตัวอักษรตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก จะทำให้ฟังก์ชันกลายเป็นฟังก์ชันคนละฟังก์ชันกัน เช่น find_area และ Find_Area ถือว่าเป็นคนละฟังก์ชันกัน
หมายเลข 3 จะอยู่ในวงเล็บเรียกว่า parameter list ฟังก์ชัน find_area มีพารามิเตอร์ที่ใช้ในฟังก์ชันเพียง 1 ตัว คือ float radius ส่วนที่เป็น body ของฟังก์ชันจะนำพารามิเตอร์เหล่านี้ไปใช้ในการทำงาน
ส่วนที่จัดว่าเป็น body ของฟังก์ชันจะอยู่ระหว่างวงเล็บปีกกา ประกอบด้วยส่วนที่เป็นประกาศของตัวแปรที่ใช้งานในฟังก์ชัน ตามด้วยประโยคคำสั่งต่าง ๆ ที่ฟังก์ชันนั้นทำงาน และจะปิดท้ายด้วยคำสั่ง return ดังปรากฏในบรรทัดที่ 34
คำสั่ง return เป็นการจบการทำงานของฟังก์ชันนั้นและส่งค่าที่ฟังก์ชันนั้นทำงานได้กลับไปให้กับโปรแกรมที่เรียกใช้ฟังก์ชันนั้นใช้งาน ฟังก์ชันหนึ่ง ๆ ส่งค่าคืนกลับได้เพียงค่าเดียว ฟังก์ชันอาจทำงานโดยไม่มีการส่งค่าคืนกลับก็ได้ ดังเช่น ฟังก์ชัน display( ) ในบรรทัดที่ 41
คำสั่ง void หมายถึง ฟังก์ชันนี้ไม่มีการส่งค่าคืนกลับไปยังโปรแกรมที่เรียกใช้งาน
ย้อนกลับไปดูโปรแกรม 4.1 อีกครั้ง จะเห็นโปรแกรมที่มีการแบ่งเป็นส่วนย่อย ๆ แตกเป็นฟังก์ชันนั้น จะเกี่ยวข้องกับส่วนต่าง ๆ ดังนี้
ส่วน A เรียกว่า การกำหนดรูปแบบฟังก์ชัน (function prototype) ก่อนที่จะนำไปใช้งาน (ในภาษา C ไม่จำเป็นต้องมีส่วนนี้)
ส่วน B เรียกว่า function definition ประกอบด้วยส่วนหัวและส่วนที่เป็นร่างของฟังก์ชัน ส่วนนี้จะอยู่ก่อน main( ) หรือหลังก็ได้
ส่วน C คือส่วนที่มีการเรียกใช้ฟังก์ชัน (function call)
ฟังก์ชันที่เป็นมาตรฐานใน Library การกำหนดรูปแบบฟังก์ชัน จะมีการกำหนดไว้เรียบร้อยแล้วใน include file
ต่อไปนี้เป็นตัวอย่างการเขียน function prototype เพิ่มเติม
long calculate_rectangle (long length, long width);
คำนวณพื้นที่สี่เหลี่ยมผืนผ้าส่งค่ากลับเป็นจำนวนเต็มชนิด long มีพารามิเตอร์ 2 ตัวคือ length และ width ซึ่งเป็นตัวแปรชนิด long ด้วยกันทั้งคู่
void print_result (int number);
ฟังก์ชัน print_result ไม่มีการส่งค่ากลับ มีพารามิเตอร์เป็นข้อมูลชนิดจำนวนเต็ม 1 ตัว
int get_your_selection ( );
ส่งค่ากลับเป็นข้อมูลชนิดจำนวนเต็ม ไม่มีพารามิเตอร์
myfunction ( );
ส่งค่ากลับเป็นข้อมูลประเภท int ไม่มีพารามิเตอร์
ขอบเขตของตัวแปรและฟังก์ชัน
ตัวแปรที่ถูกประกาศไว้ภายในบล็อกหรือฟังก์ชันหนึ่ง จะถูกจำกัดการใช้งานได้เพียงภายในบล็อกนั้นหรือภายในฟังก์ชันนั้นเท่านั้น เรียกตัวแปรประเภทนี้ว่าเป็นตัวแปรท้องถิ่น (local variable) คำสั่งอื่น ๆ นอกบล็อกนั้นหรือฟังก์ชันอื่น ไม่สามารถนำตัวแปรนั้นมาใช้งานได้
ในโปรแกรม 4.1 ตัวแปรที่ประกาศไว้ในฟังก์ชัน main คือ area, circumference, r ตัวแปรเหล่านี้จะถูกใช้งานภายในฟังก์ชัน main ฟังก์ชันอื่น ๆ ได้แก่ find_area, input_radius, compute_perimeter ไม่สามารถนำค่าที่เก็บไว้ในตัวแปรมาใช้ในการทำงานได้ ในทำนองเดียวกัน ในฟังก์ชัน input_radius จะมีตัวแปร radius ชนิด float ใช้งานได้ในฟังก์ชันนี้เท่านั้น ฟังก์ชัน main หรือ ฟังก์ชันอื่น ไม่สามารถนำตัวแปรนี้ไปใช้งานได้ ในฟังก์ชัน find_area มีตัวแปรชื่อ area ซึ่งเป็นตัวแปรชนิด float มีชื่อซ้ำกับตัวแปร area ทีปรากฏในฟังก์ชัน main ถือว่าเป็นตัวแปรคนละตัวกัน ตัวแปรจะยึดครองหน่วยความจำ ขณะที่ฟังก์ชันนั้น หรือบล็อกคำสั่งนั้นทำงาน เมื่อบล็อกหรือฟังก์ชันนั้นสิ้นสุดการทำงาน ตัวแปรเหล่านั้นจะไม่ถูกลบจากหน่วยความจำทันทีทันใด ตัวแปรเหล่านั้นจะคงอยู่ในหน่วยความจำอีกถ้ามีการทำงานในบล็อกนั้นหรือในฟังก์ชันนั้นซ้ำอีกครั้ง
ตัวอย่างต่อไปนี้ จะแสดงให้เห็นถึงขอบเขตของตัวแปรในบล็อกและฟังก์ชัน
[Source code:varScope.cpp]
โปรแกรม 4.2 เริ่มต้นที่ main ( ) ภายในฟังก์ชัน main( ) มีการกำหนดตัวแปรชนิด int ชื่อ number ให้มีค่าเป็น 3
บรรทัดที่ 10 เมื่อพิมพ์ค่า number จะได้ผลลัพธ์เท่ากับ 3 บรรทัดที่ 12 เรียกใช้ฟังก์ชัน displaynumber() ในฟังก์ชันนี้มีตัวแปรท้องถิ่น ชื่อ number เหมือนกัน แต่กำหนดให้มีค่าเป็น 5 และจะแสดงผล number มีค่าเท่ากับ 5
ในการวนรอบด้วยคำสั่ง for ในบรรทัดที่ 21 มีการประกาศค่าตัวแปร ชื่อ number (เป็นตัวแปรตัวที่ 3 ที่มีชื่อ number เหมือนกัน) โดยให้วนรอบ เริ่มตั้งแต่ number = 1 วนรอบจำนวน 10 ครั้ง ในแต่ละครั้งจะแสดงค่า number บนจอภาพ จะเห็นว่าค่า number จะมีค่าตั้งแต่ 1 ถึง 10 เมื่อวนรอบจบแล้ว ให้แสดงผล number อีกครั้ง ดังปรากฏในคำสั่งบรรทัดที่ 24 ขณะนี้ การทำงานของโปรแกรมออกมานอกขอบเขตของการวนรอบของคำสั่ง for แล้ว ดังนั้นค่า number ที่ได้ในบรรทัดที่ 24 นึ้จึงเป็นค่าของตัวแปร number = 5 ที่กำหนดไว้ในฟังก์ชัน displaynumber ()
เมื่อสิ้นสุดการทำงานชองฟังก์ชันในบรรทัดที่ 13 ให้แสดงผล number อีกครั้ง บรรทัดที่ 14 จะนำค่า number = 3 แสดงผลบนจอภาพ จะเห็นว่าค่า number ที่กำหนดไว้ในฟังก์ชัน displaynumber( ) ไม่มีผลต่อตัวแปรในฟังก์ชัน main ( )
โปรแกรมนี้แสดงให้เห็นขอบเขตการทำงานของตัวแปรที่ถูกกำหนดค่าไว้คนละที่เท่านั้น ในทางปฏิบัติควรกำหนดชื่อตัวแปรทั้งสามตัว ให้มีชื่อต่างกันไปจะดีกว่า เพื่อลดความสับสน ขณะไล่ดูการทำงานของโปรแกรม
ตัวแปรแบบโกลบบอล (Global variable)
ตัวแปรที่นิยามไว้นอกฟังก์ชันใด ๆ รวมทั้ง main ด้วย เป็นตัวแปรที่ทุกฟังก์ชันมองเห็นและสามารถนำไปใช้งานได้ ในบางกรณีที่โปรแกรมเมอร์ ต้องการให้ฟังก์ชันทุกฟังก์ชันสามารถตำค่าจากตัวแปรนี้ไปใช้หรือเปลี่ยนค่าตัวแปรได้โดยไม่ต้องผ่านข้อมูลในตัวแปรี้ผ่านทางตัวพารามิเตอร์ของฟังก์ชัน โดยปกติดเราไม่นิยมใช้ตัวแปรแบบโกลบบอลในภาษา C++ เพราะค่อนข้างเสี่ยงต่ออันตราย ทั้งนี้เพราะการใช้ข้อมูลร่วมกันในตัวแปรแบบโกลบบอล ฟังก์ชันอื่น ๆ ต่างก็สามารแก้ไขข้อมูลในตัวแปร โดยมิได้ตั้งใจ อาจทำให้โปรแรกมเกิดข้อผิดพลาดและการแก้ไขทำได้ลำบาก
ตัวแปรท้องถิ่นถึงแม้จะมีชื่อเดียวกันกับตัวแปรแบบโกลบบอล ก็จะไม่กระทบกระเทือนถึงข้อมูลที่เก็บไว้ในตัวแปรแบบโกลบบอล การอ้างถึงตัวแปรในฟังก์ชันใดที่มีขื่อซ้ำกัน จะหมายถึงตัวแปรประจำถิ่นในฟังก์ชันนั้น
[Source code: GlobalVar.cpp]
จากโปรแกรม 4.3 บรรทัดที่ 7 และ 8 เป็นการประกาศตัวแปรชนิด int ชื่อ x และ y ซึ่งเป็นตัวแปรแบบโกลบบอล กำหนดให้ x = 10, y = 20 ตามลำดับ
บรรทัดที่ 12 และ 13 แสดงผลค่า x และ y ในฟังก์ชันเนื่องจากได้มีการนิยามตัวแปร x ในฟังก์ชัน main() ซ้ำกับตัวแปรโกลบบอล ผลลัพธ์ที่ได้จึงเป็นค่า x ของตัวแปรท้องถิ่น และค่า y ของตัวแปรโกลบบอล
เมื่อเรียกใช้งานฟังก์ชัน displayvar( ) ในบรรทัดที่ 16 ฟังก์ชันนี้จะมีการนิยามและประกาศค่าตัวแปร 1 ตัวคือ int x = 2 การแสดงค่า x จึงใช้ค่าตัวแปรท้องถิ่น
เมื่อฟังก์ชันทำงานจบกลับมาที่ main ( ) แสดงผลค่า x และ y อีกครั้ง จะเป็นค่า x ในตัวแปรท้องถิ่น และค่า y ที่เก็บไว้ในตัวแปรแบบโกลบบอล
ในบรรทัดที่ 21 แสดงให้เห็นว่าถ้าต้องการเข้าถึงข้อมูลที่เก็บไว้ในตัวแปรโกลบบอล x ในฟังก์ชัน main ( ) ทำได้โดยใช้เครื่องหมาย :: (scope resolution operator) กำกับหน้าตัวแปร x
cout << “Global x << ::x << endl;
เครื่องหมายนี้บอกให้คอมไพเลอร์รู้ว่าต้องการแสดงค่า x ที่เก็บไว้ในตัวแปรแบบโกลบบอล ไม่ใช่ค่าที่เก็บไว้ในตัวแปรท้องถิ่น
การผ่านค่า parameter ไปยังฟังก์ชัน (Passing parameter to function)
เมื่อมีการเรียกใช้ฟังก์ชันในโปรแกรมใด จะต้องมีการผ่านค่าพารามิเตอร์ ไปยังฟังก์ชัน ตามที่ประกาศไว้ในการกำหนดรูปแบบฟังก์ชัน (Function prototype) การผ่านค่าใน C++ มี 2 แบบดังนี้
1. การผ่านเฉพาะค่าของตัวแปรนั้น (passing by value) ฟังชันก์จะสำเนานำเฉพาะข้อมูลของตัวแปรที่ถูกส่งผ่านไปประมวลผล ฟังก์ชันจะสร้างตัวแปรชนิดเดียวกัน โดยข้อมูลที่เก็บไว้ในตัวแปรที่ถูกส่งผ่านนั้นยังคงเดิม นั่นคือฟังก์ชันจะคัดลอกเฉพาะข้อมูลจากตัวแปรที่ส่งผ่าน และสร้างตัวแปรที่มีชนิดข้อมูลแบบเดียวกันในหน่วยความจำคนละแห่ง
ในโปรแกรม 4.1 คำสั่งเรียกใช้ฟังก์ชัน find_area (float r) เป็นการผ่านค่า r (รัศมีของวงกลม) ไปให้ฟังก์ชัน find_area หาพื้นที่ของวงกลม ฟังก์ชัน find_area จะก็อปปี้เฉพาะค่า r ไปใช้ในการคำนวณเท่านั้น ไม่ทำให้ค่าที่เก็บไว้นใน r มีการเปลี่ยนแปลงแต่อย่างไร
การผ่าน r ในการเรียกใช้ฟังก์ชัน computer_preimeter ก็เช่นเดียวกัน เป็นการผ่านค่าแบบ passing by value เพราะข้อมูลในตัวแปรที่ใช้ในการผ่านค่าไม่มีการเปลี่ยนแปลง การเขียนโปรแกรมจึงต้องพึงระวัง ในกรณีที่ต้องการให้ฟังก์ชันนั้นมีการปรับปรุงข้อมูล ตัวแปรที่ใช้ผ่านค่า ดังตัวอย่างต่อไปนี้
[Source code: PassByVal.cpp]
โปรแกรมนี้ต้องการสลับค่าระหว่าง x กับ y โดยสร้างฟังก์ชันชื่อ swap ให้ทำการสลับค่า จะเห็นว่าเมื่อผ่านค่า x = 2, y = 7 ไปให้ฟังก์ชัน swap ฟังก์ชันจะรับค่าไปสลับได้อย่างถูกต้อง สังเกตจากการแสดงผลภายในฟังก์ชัน จะได้ x = 7, y= 2
เมื่อฟังก์ชัน swap ทำงานเสร็จแล้ว กระโดดกลับมายังฟังก์ชัน main เมื่อแสดงผลค่า x, y พบว่ายังคงมีค่าเท่าเดิม ค่าที่เก็บไว้ในตัวแปร x, y ไม่มีการสลับแต่อย่างใด
การผ่านค่าแบบ passing by value นอกจากสามารถใช้ตัวแปรเป็นตัวพารามิเตอร์แล้ว ยังสามารถใช้ค่าคงที่ หรือ นิพจน์ ในการผ่านค่าได้ด้วย เช่น
find_area ( 5 ) ; // คำนวณพื้นที่วงกลมรัศมี 5 หน่วย
find_area ( 3*x + 7) ; // คำนวณพื้นที่วงกลมรัศมีเกิดจาก 3x + 7
display( find_area( 5) ) // แสดงผลลัพธ์ที่ได้จากการคำนวณหาพื้นที่วงกลม เป็นการใช้ฟังก์ชันเป็นพารามิเตอร์

2. การผ่านโดยการอ้างอิงตำแหน่งหน่วยความจำของตัวแปรนั้น (passing by reference) ประโยชน์ของการผ่านค่าโดยวิธีนี้คือฟังก์ชันสามารถเข้าถึงตัวแปรใน calling program และใช้ในกรณีที่ต้องการให้ฟังก์ชันส่งค่ากลับคืนมากกว่า 1 ค่า หรือต้องการเปลี่ยนแปลงค่าตัวแปรหรือตัวแปรที่ใช้ผ่านค่าใช้เนื้อที่หน่วยความจำมาก เสียเวลาในการคัดลอก เช่นข้อมูลรูปภาพขนาด 5 Mb เป็นต้น กลไกการผ่านค่าแบบนี้จะต่างจากการผ่านค่าแบบ passing value คือ แทนที่จะสำเนาข้อมูลในตัวแปรที่ใช้เป็นตัวพารามิเตอร์ กลับถือว่าพารามิเตอร์นั้นเป็นตัวแปรเดียวกับตัวแปรพารามิเตอร์ที่ใช้ผ่านค่า เพียงแต่เป็นคนละชื่อกันเท่านั้น ไม่ได้มีการสร้างตัวแปรขึ้นมาใหม่
โปรแกรม 4.5 จะนำโปรแกรมที่ 4.4 มาปรับปรุงโดยให้การผ่านค่าไปยังฟังก์ชัน swap เป็นแบบอ้างอิง
[Source code: PassByRef.cpp]
ในการประกาศรูปแบบฟังก์ชัน swap มีสิ่งที่เพิ่มเติมจากเดิมดังนี้
void swap (int &, int & ) ;
เครื่องหมาย & ๖ reference operator ) จะอยู่ต่อท้ายชนิดของข้อมูล ในที่นี้คือ int เป็นสัญลักษณ์บอกว่าเป็นการผ่านค่าตัวแปรแบบอ้างอิง
int &a หมายถึง a จะเป็นตัวใช้อ้างอิงตัวแปรชนิด int ที่ถูกผ่านค่ามา (a is a reference to the int variable passed to it) ในทำนองเดียวกัน int & b b จะเป็นชื่อที่ใช้เป็นตัวอ้างอิงตัวแปรชนิด int ตัวที่ 2 ที่ถูกผ่านค่ามา a และ b จะมีตำแหน่งในหน่วยความจำเดียวกันกับ x, y ที่ผ่านค่ามา อาจกล่าวได้ว่า a, b คือตัวแปร x, y ที่ถูกเปลี่ยนชื่อ
สังเกตเห็นได้อย่างหนึ่งว่า เราไม่ได้ใช้เครื่องหมาย & ในการเรียกใช้ฟังก์ชัน swap (x, y) ในบรรทัดที่ 13 ดังนั้นการดูประโยคคำสั่งที่เรียกใช้ฟังก์ชัน จึงไม่สามารถตัดสินได้เลยว่าการผ่านค่าพารามิเตอร์ เป็นแบบส่งค่าหรืออ้างอิงตำแหน่ง
การผ่านค่าแบบอ้างอิงตำแหน่ง สามารถเขียนเป็น
int & x, int & y; หรือ
int &x, int &y; ก็ได้
พารามิเตอร์ที่ใช้ในการผ่านค่า ต้องเป็นตัวแปรเท่านั้น ไม่สามารถใช้ค่าคงที่หรือสมการ หรือฟังก์ชันในการผ่านค่าได้
ตัวอย่างต่อไปนี้ จะอาศัยประโยชน์จากการผ่านค่าแบบอ้างอิง โดยไม่ต้องอาศัยคำสั่ง return
[Source code: PassByRef2.cpp]

เปรียบเทียบระหว่างการผ่านค่าแบบ passing by value กับ passing by reference จะได้ดังนี้
Passing by value
passing by reference
การประกาศฟังก์ชัน
float x;
myfunction (float x);

float &x;
myfunction (float &x);
x เป็นตัวแปรท้องถิ่นx เป็นตัวแปรท้องถิ่นเช่นกัน
ฟังก์ชันที่ถูกเรียกจะคัดลอกค่าจากพารามิเตอร์ที่ใช้ผ่านค่าฟังก์ชันที่ถูกเรียกจะมีตัวแปรเป็นตัวเดียวกับพารามิเตอร์ที่ใช้ในการผ่านค่า เพียงแต่ชื่อตัวแปรต่างกัน
พารามิเตอร์ที่ใช้ในการผ่านค่าไม่มีการเปลี่ยนแปลงค่า (read only)สามารถเปลี่ยนแปลงค่าที่เก็บไว้ในพารามิเตอร์ที่ใช้ผ่านค่า (read write)
พารามิเตอร์ที่ใช้ผ่านค่าเป็นได้ทั้งค่าคงที่ ตัวแปร สมการใช้ตัวแปรเป็นพารามิเตอร์ในการผ่านค่าเท่านั้น

โปรแกรม 4.7 เป็นการนำโปรแกรม 4.1 มาปรับปรุงโดยใช้การผ่านค่าแบบอ้างอิง จำนวนบรรทัดของโปรแกรมจะสั้นลง
[Source code:Circle2.cpp]
ตัวอย่างต่อไปนี้เป็นการตรวจสอบการผ่านค่าแบบอ้างอิง ทดลองพิมพ์ ค่าก่อนส่งผ่านค่า และระหว่างที่อยู่ในฟังก์ชัน และหลังจากออกจากฟังก์ชัน
[Source code:TestRef.cpp]
การผ่านค่าแบบอ้างอิง มีความเสี่ยงตรงที่ตัวแปรที่ใช้เป็นตัวพารามิเตอร์ถูกเปลี่ยนแปลงค่าได้ ในภาษา C++ จึงมีทางเลือกที่ 3 ให้ คือ ผ่าน ค่า by constant reference เป็นการผ่านค่าเหมือนกับแบบอ้างอิงปกติทั่วไป แต่ฟังก์ชันจะมีการป้องกันไม่ให้ตัวแปรเปลี่ยนค่าระหว่างที่ฟังก์ชันทำงาน
โปรแกรม 4.9 เป็นตัวอย่างการผ่านค่าแบบ constant reference
[Source code: ConstantRef.cpp]
ถ้าทดลองเปลี่ยนค่า z โดยลบ comment ในบรรทัดที่ 22 ออก จะคอมไพล์ไม่ผ่าน มีการแจ้งข้อความผิดพลาด การผ่านค่าโดยวิธี constant reference นิยมใช้ผ่านค่า object ที่มีขนาดใหญ่ เช่น array
inline function
ฟังก์ชันช่วยให้การเขียนโปรแกรมมีขนาดกะทัดรัดและอ่านง่าย เมื่อผ่านการคอมไพล์และถูกโหลดเข้าไปในเครื่องแล้ว ไม่สิ้นเปลืองหน่วยความจำ เพราะส่วน binary code ของฟังก์ชันจะอยู่ในหน่วยความจำเพียงที่เดียว ส่วนอื่น ๆ ของโปรแกรมที่มีการเรียกใช้ฟังก์ชันนี้ ไม่ว่าจะกี่ครั้งก็ตาม คอมไพเลอร์จะกำหนดให้กระโดดไปทำงานยังตำแหน่งของหน่วยความจำที่ฟังก์ชันนั้นอยู่ทันที เมื่อทำงานเสร็จจะกระโดดกลับไปยังตำแหน่งที่อยู่หลังคำสั่งที่เรียกใช้ฟังก์ชันนี้
ขณะที่ได้ประโยชน์จากการประหยัดหน่วยความจำในการเก็บชุดคำสั่งของฟังก์ชันนี้ บางครั้งต้องสูญเสียเวลาและหน่วยความจำบางส่วน ในขณะที่มีการเรียกใช้ฟังก์ชัน ถ้าผ่านค่าแบบ by value จะต้องคัดลอกค่าของพารามิเตอร์ต่าง ๆ เก็บไว้ในตัวแปรท้องถิ่น ต้องเก็บตำแหน่งหน่วยความจำของคำสั่งที่เรียกใช้ฟังก์ชัน เพื่อที่จะได้กระโดดกลับมาได้ถูก เมื่อฟังก์ชันทำงานเสร็จแล้ว จะต้องใช้เวลาลบตัวแปรต่าง ๆ ที่สร้างไว้ และส่งค่าคืนกลับไปยังตัวแปรชองโปรแกรมหลัก (ถ้าฟังก์ชันมีการส่งค่ากลับคืน)
เพื่อลดเวลาการทำงานของโปรแกรม ถ้าฟังก์ชันนั้นมีขนาดสั้น และถูกเรียกใช้บ่อย ๆ จึงเลือกที่จะให้ฟังก์ชันนั้นฝังตัวอยู่ในโปรแกรมตรงส่วนที่มีการเรียกใช้ทันที ยอมเสียเนื้อที่หน่วยความจำเพื่อแลกกับเวลาการทำงานที่เร็วขึ้น
การบอกให้คอมไพเลอร์นำชุดคำสั่งของฟังก์ชันไปใส่แทนตรงตำแหน่งที่มีการเรียกใช้ฟังก์ชัน ทำได้โดยเพิ่มคำว่า inline ไว้ข้างหน้าฟังก์ชันนั้น
1: // Program 4.10 test inline funciton
2: // Feb16, 2004
3: #include <iostream>
4: using namespace std;
5:
6: inline float mile2km (float mile) {
7: return (8.0*mile/5.0);
8: }
9:
10:
11: int main() {
12: float m;
13: cout << "Enter Distance in mile : " ;
14: cin >> m;
15: cout << "Distance in kilometer = " << mile2km(m) ;
16: }
17:
มีข้อปลีกย่อยเกี่ยวกับการใช้ inline function บางประการ คือ คอมไพเลอร์จะต้องมองเห็นตัวฟังก์ชันทั้งหมด ก่อนที่จะมีการเรียกใช้ฟังก์ชัน (ไม่ใช่เฉพาะการประกาศฟังก์ชัน) เพื่อที่จะได้แทรกชุดคำสั่งลงในโปรแกรมได้ถูกต้องตรงตำแหน่งที่เรียกใช้ จากตัวอย่างจะเห็นว่า ฟังก์ชัน mile2km จึงถูกเขียนไว้ก่อน main () ดังนั้นการประกาศฟังก์ชันก่อนเรียกใช้จึงไม่จำเป็น
เมื่อผ่านการคอมไพล์แล้ว ภายในโปรแกรม main ( ) จะถูกแทนที่ด้วยชุดคำสั่งที่อยู่ในฟังก์ชัน mile2km ดังนี้
int main() {
float m;
cout << "Enter Distance in mile : " ;
cin >> m;
cout << "Distance in kilometer = " << 8.0*mile/5.0 ;
}
ฟังก์ชันเรียกใช้ตัวมันเอง (Recursion)
คือการที่ฟังก์ชันหนึ่ง ๆ เรียกใช้ตัวมันเอง มีอยู่ 2 แบบ คือ เรียกใช้ตัวมันเองโดยตรง (direct) และโดยอ้อม คือการที่ฟังก์ชัน A เรียกใช้ฟังก์ชัน B และ ในฟังก์ชัน B นั้นก็มีการเรียกใช้ฟังก์ชัน A
การใช้วิธีเรียกใช้ตัวมันเองแก้ปัญหาบางปัญหา ทำให้โปรแกรมนั้นดูง่ายและมีขนาดกะทัดรัด มักใช้กับปัญหาที่กระทำกับข้อมูลแต่ละตัว ในลักษณะที่มีวิธีการซ้ำ ๆ กัน การออกแบบฟังก์ชันแบบรีเคอชัน จะได้ผลลัพธ์จากการประมวลผลสมตามความต้องการ แต่ก็เสียงกับการวนรอบแบบไม่รู้จบและเกิด runtime error
เมื่อฟังก์ชันเรียกใช้ตัวมันเอง โปรแกรมจะสร้างฟังก์ชันนั้นขึ้นในหน่วยความจำอีกชุด ตัวแปรท้องถิ่นของฟังก์ชันอันหลังจะเป็นคนละตัวกับตัวแปรของฟังก์ชันอันแรก ดังนั้นค่าที่เก้บไว้ในตัวแปรแต่ละชุดของฟังก์ชัน จึงไม่กระทบกระเทือนต่อกัน
ตัวอย่างปัญหาที่ใช้วิธี recursion ในการแก้ปัญหาได้แก่เลขอนุกรมของฟิโบนัคซี (Fibonacci series) ประกอบด้วยเลขจำนวนเต็มต่อไปนี้
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …….
เลขสองตัวค่าแรกจะเป็น 0 และ 1 เลขถัดจากนี้ไปจะเกิดจากการนำผลบวกของเลขสองจำนวนที่อยู่ก่อนหน้ารวมกัน หรือเลขตัวที่ n เกิดจากผลรวมของเลขเทอมที่ n-2 และ n-1 โดยที่ n มากกว่า 2
ฟังก์ชันเรียกตัวมันเองต้องมีเงื่อนไขในการให้การเรียกตัวมันเองหยุดลง เช่นในเรื่องอนุกรมฟิโบนัคซี ฟังก์ชันจะหยุดการเรียกตัวมันเองเมื่อ n > 2
ขั้นตอนการทำงานจะเป็นดังนี้
-  ต้องการเลขฟิโบนัคซีที่ตำแหน่ง n
- เรียกใช้ฟังก์ชัน fibonacci (n)
- ฟังก์ชัน fibonacci (n) จะตรวจสอบอากิวเมนต์ n
ถ้า n < 3 จะคืนค่ากลับเท่ากับ 1
ถ้า n > 3 จะเรียกใช้ฟังก์ชัน fibonacci โดยผ่านค่า n-2 และเรียกตัวมันเองอีกครั้ง โดยผ่านค่า n-1 แล้วคืนค่าที่เป็นผลบวก
-  ถ้าเรียกใช้ฟังก์ชัน fibonacci (0) จะส่งค่า 0 คืนกลับ
ถ้าเรียกใช้ฟังก์ชัน fibonacci (1) จะส่งค่า 1 คืนกลับ
ถ้าเรียกใช้ฟังก์ชัน fibonacci (2) จะส่งค่า 1 คืนกลับ
- ถ้าเรียกใช้ฟังก์ชัน fibonacci (3) จะส่งค่า fibonacci (2) และ fibonacci (1) คืนกลับ เพราะ fibonacci (2) ส่งค่ากลับมาเป็น 1 และ fibonacci (2) ส่งค่าคืนกลับมาเป็น 1 ดังนั้น fibonacci (3) จะส่งค่ากลับคืนมาเป็น 2
- ถ้าเรียกใช้ฟังก์ชัน fibonacci (4) จะส่งค่าผลบวกจากการเรียกใช้ fibonacci (3) และ fibonacci (2) เพราะ fibonacci (3) ส่งค่าคืนกลับมาเป็น 2 โดยการเรียกใช้ fibonacci (2) และ fibonacci (1) fibonacci (2) ส่งค่าคืนกลับมาเป็น 1 ดังนั้น fibonacci (4) จะได้ผลลัพธ์เป็น 3
- ถ้าเรียกใช้ฟังก์ชัน fibonacci (5) ฟังก์ชันจะส่งค่าผลบวกระหว่าง fibonacci (4) และ fibonacci (3) เพราะ fibonacci (4) ส่งค่าคืนกลับมาเป็น 3 และ fibonacci (3) ส่งค่าคืนกลับมาเป็น 2 ผลรวมที่ fibonacci (5) ส่งกลับมาคือ 5
ขั้นตอนการหาเลขฟิโบนัคซีดังกล่าวไม่มีประสิทธิภาพนัก เพราะเมื่อเราหา fibonacci (20) จะต้องมีการเรียกใช้ตัวมันเองถึง 13,529 ครั้ง หรือ fibonacci (n) จำนวนครั้งที่เรียกใช้ตัวมันเองคือ  ในการเรียกใช้ตัวมันเองแต่ละครั้งจะต้องสูญเสียหน่วยความจำในการคัดลอกฟังก์ชัน เมื่อมี่คำสั่ง return จึงจะปลดปล่อยหน่วยความจำส่วนนั้น
[Source code: Fibbo.cpp]
เช่นต้องการหาเลขฟิโบนัคซี เทอมที่ 35 หาได้ดังนี้

การหาแฟคทอเรียลของจำนวนเต็มบวก n ใด ๆ เป็นตัวอย่างการแก้ปัญหาแบบเรียกใช้ตัวมันเองที่ดีอีกตัวอย่างหนึ่ง เขียนเป็นสัญลักษณ์ทางคณิตศาสตร์ได้เป็น n! อ่านว่า n factorial หาได้จาก
n ! = n (n -1) (n – 2) ( n – 3)… 1
โดยที่ 1 ! = 1 และ 0 ! = 1
6! = (6)(5)(4)(3)(2)(1)
เราสามารถใช้วิธีการวนรอบแบบธรรมดาคำนวณหาค่า n! ได้ดังนี้
factorial =1;
for (int i = n; i >=1; i--)
factorial = factorial * i;
return factorial;
เมื่อใช้วิธีแก้ปัญหาโดยใช้ฟังก์ชันเรียกตัวมันเอง โปรแกรมที่สมบูรณ์จะเป็นดังนี้
[Source code: factorial.cpp]
ทดลองหา 33! จะได้ผลลัพธ์ดังภาพ
ฟังก์ชัน factorial รับค่าพารามิเตอร์ชนิด unsigned long และคืนค่ากลับเป็นข้อมูลชนิด unsigned long เหมือนกัน ข้อมูลชนิด unsigned long จะเก็บข้อมูลโดยใช้หน่วยความจำ 4 byte สามารับค่าที่ได้จากการคำนวณแฟคทอเรียลได้ถึง 4,294,967,295 แต่ค่าแฟคทอเรียลที่ได้จะเพิ่มค่าอย่างรวดเร็ว ควรทดลองหาค่า n ว่ามีค่าเท่าใดจึงจะไม่เกินค่าที่เก็บได้ในหน่วยความจำนี้
การเขียนฟังก์ชันเรียกตัวมันเอง โดยไม่มีการคืนค่ากลับ หรือไม่มีคำสั่ง return จะถูกคอมไพเลอร์แจ้งเตือนขณะที่ทำการคอมไพล์ ถ้าในฟังก์ชันนั้นขาดเงื่อนไขที่จะให้ฟังก์ชันหยุดการเรียกตนเอง จะทำให้เกิดกรณีเรียกตนเองไม่สิ้นสุด สิ้นเปลืองหน่วยความจำ และให้ผลลัพธ์จากการทำงานที่มิอาจคาดเดาได้
ฟังก์ชันที่มีการตั้งค่าปริยายให้แก่พารามิเตอร์์ (Function with default parameter)
การเรียกใช้ฟังก์ชัน จะต้องผ่านค่าที่ต้องการให้กับพารามิเตอร์แก่ฟังก์ชันที่ถูกเรียก โปรแกรมเมอร์สามารถจะกำหนดค่าอากิวเมนต์ให้มีค่าตามที่ตั้งไว้ได้ เมื่อเรียกใช้ฟังก์ชันโดยไม่มีการผ่านค่าพารามิเตอร์ใด ๆ ค่าที่กำหนดไว้จะถูกนำมาใช้งานทันที
ค่าอากิวเมนต์ที่มีการตั้งค่าที่กำหนดไว้ จะต้องเป็นพารามิเตรอ์ที่อยู่ขวามือสุดของฟังก์ชันนั้น ถ้ามีหลายตัว ต้องเรียงลำดับไว้ขวามือสุด ค่าที่ถูกตั้งไว้โดยปริยายนั้นสามารถเป็นได้ทั้งตัวคงที่ ตัวแปรแบบโกลบบอลหรือฟังก์ชัน การตั้งค่า default นี้ สามารถทำได้ใน inline function ด้วย
[Source code:DefaultArg.cpp]
การส่งผ่านค่าพารามิเตอร์ต่อไปนี้ ไม่ถูกต้อง
volumeOfBox ( , 3, 2) เมื่อจะละเว้นพารามิเตอร์ตัวแรก จะต้องละเว้นตัวที่ 2 และ 3 ด้วย
volumeOfBox ( 5, , 2) เมื่อละเว้นพารามิเตอร์ตัวที่ 2 จะต้องละเว้นค่าพารามิเตอร์ทุกตัวที่ตามหลังตัวที่ 2
การประกาศฟังก์ชันที่มีค่า default ต่อไปนี้ ไม่ถูกต้อง
ตัวอย่าง void myfunc ( int a=2, int b , int c =3);
แก้ไขโดยตำพารามิเตอร์ที่มีค่าตั้งไว้ ไปไว้ทางขวามือ
void myfunc (int b , int a=2, int c =3);
ตัวอย่าง int func(int a, int b = 0, int c);
แก้ไขโดยเปลี่ยนลำดับพารามิเตอร์ใหม่ดังนี้
int func(int a, int c , int b = 0);
ตัวอย่าง void anotherFunc( int , int =2 , int = 3);
การประกาศในตัวอย่างนี้ ทำได้ถูกต้อง
Function Overloading
ในภาษา C++ ยอมให้เราเขียนฟังก์ชันที่มีชื่อเหมือนกันแต่ชนิดข้อมูลและจำนวนพารามิเตอร์แตกต่างกัน ความสามารถที่ยอมให้ฟังก์ชันมีชื่อซ้ำกันได้นี้ เรียกว่า overloading เมื่อมีการเรียกใช้ overloaded function คอมไพเลอร์จะตรวจสอบจำนวน ชนิด และลำดับของอากิวเมนต์ที่ถูกส่งผ่านในพารามิเตอร์ และจะเลือกฟังก์ชันที่มีชื่อเดียวกันนั้นให้เหมาะสมกับอากิวเมนต์นั้น ฟังก์ชันโอเวอร์โหลดดิง จะใช้ในการสร้างฟังก์ชันที่ทำหน้าที่เดียวกัน แต่มีชนิดข้อมูลต่างกัน ดังตัวอย่างต่อไปนี้
[Source code: Overloading.cpp]
การใช้ overloaded function ทำให้เราประหยัดการตั้งชื่อฟังก์ชัน ในภาษา C ถ้าชนิดข้อมูลต่างกัน ถึงแม้ฟังก์ชันจะทำงานแบบเดียวกัน ต้องตั้งชื่อต่างกัน เช่น
int imultiply ( int num1, int num2);
double dmultiply (double num1, double num2);
short smultiply (short num1, short num2);

การสร้างฟังก์ชัน overloading มีข้อพึงระวังดังนี้
•  ไม่สามารถทำ overload ฟังก์ชันที่มีชนิดและจำนวนของตัวพารามิเตอร์เท่ากัน แต่คืนค่าชนิดข้อมูลต่างกัน
int max (int a, int b);
unsigned max ( int a , int b);
ฟังก์ชัน max มีพารามิเตอร์เหมือนกันทุกประการ แต่ส่งคืนค่าข้อมูลต่างชนิดกัน C++ จะจำแนก overloading function โดยดูจาก argument list ไม่สามารถจำแนกจากชนิดข้อมูลที่คืนค่า
•  ถ้าค่า argument ที่ส่งผ่านมีชนิดข้อมูลไม่สอดคล้องกับที่ได้ประกาศไว้ จะต้อง cast ชนิดข้อมูลให้ตรงกันก่อน เช่น
float float_var =2.4;
in tint_var = 4;
int result;
result = multiply (float_var , int_var);
จะขึ้นข้อความผิดพลาดว่าสับสนชนิดข้อมูล ใน argument list ต้องแก้เป็น
result = multiply ( (int) float_var , int_var);
แต่ถ้าข้อมูลที่ส่งผ่านค่าเป็นชนิดข้อมูลที่ใช้เนื้อที่หน่วยความจำในการเก็บน้อยกว่าชนิดข้อมูลของ argument list C++ จะเปลี่ยนชนิดข้อมูลนั้น ให้เป็นชนิดข้อมูลตัวเลข (numeric type) ตามที่ปรากฎใน argument list เช่น
int multiply ( int i1, int i2);
double multiply (double d1, double d2);
float f1, f2;
cout << multiply (f1, f2);
จะเปลี่ยน ข้อมูลชนิด float ให้เป็นข้อมูลชนิด double
long L1, L2;
cout << multiply (L1, L2);
ไม่สามารถเปลี่ยนข้อมูลชนิด long ให้เป็นข้อมูลชนิด int ได้ เพราะข้อมูลชนิด long ใช้เนื้อที่หน่วยความจำมากกว่าข้อมูลชนิด int
•  Overloaded function ที่มีการตั้งค่าตัวแปรโดยปริยายไว้ อาจจะสับสนกับ overloaded function ที่ผ่านค่าตัวแปรเป็น void ได้เช่น
int multiply ( int i1=5, int i2=7);
int multiply (void) {
int i1=3 ;
int i2=4;
return i1*i2;
}
เมื่อเรียกใช้ฟังก์ชัน
int result = multiply ( );
คอมไพเลอร์จะสับสนไม่สามารถเลือกถูกว่าจะใช้ฟังก์ชันชุดใด
ฟังก์ชันต้นแบบ ( Function template)
ฟังก์ชันโอเวอร์โหลดใช้เมื่อการผ่านค่า argument ที่มีจำนวนและชนิดข้อมูลต่างกัน งานที่ประมวลผลมีลักษณะเหมือนกันหรือมีลักษณะซ้ำกันทุกประการ การใช้ template จะทำให้เขียนโค้ดของฟังก์ชันนั้นเพียงครั้งเดียว แต่ใช้กับตัวแปรที่มีชนิดข้อมูลต่างกันได้ เราสามารถสร้างฟังก์ชันต้นแบบ เพียง 1 ชุด นำไปแก้ปัญหาเดียวกันได้ทั้งหมด
ถึงแม้จะเป็นข้อมูลต่างชนิดกัน เป็นการสร้าง generic function ที่ใช้ได้กับข้อมูลครอบจักรวาล
ฟังก์ชันต้นแบบ จะนำหน้าด้วยคำว่า template ตามด้วยชนิดข้อมูล รูปแบบทั่วไปจะเป็นดังนี้
template <class Ttype>
return_type FunctionName ( parameter list) {
// body function
}
เมื่อนำฟังก์ชัน multiply เขียนเป็นฟังก์ชันต้นแบบจะได้ดังนี้
[Source code: templatedFunc.ccp]
บรรทัดที่ 5 ถึง 8 เป็นการเขียนฟังก์ชันต้นแบบ ชื่อ multiply มีการประกาศข้อมูลชนิด T ซึ่งจะถูกใช้ในฟังก์ชัน และข้อมูลที่ส่งค่าคืนกลับก็เป็นข้อมูลชนิด T เช่นเดียวกัน จะเห็นว่าการใช้ฟังก์ชันต้นแบบจะลดจำนวนบรรทัดการเขียนคำสั่งลงไปได้มาก
ตัวอย่างต่อไปนี้เป็นฟังก์ชันต้นแบบที่ใช้เปรียบเทียบข้อมูล 3 ชุด แล้วหาว่าข้อมูลชุดใดมีค่ามากที่สุด


แบบฝึกหัดที่ 4.1
1  จงคอมไพล์โปรแกรมที่ใช้งาน overloaded function ต่อไปนี้ แล้วดูผลที่ได้ ถ้ามีข้อผิดพลาดจะต้องแก้ไขอย่างไร
1: // ex 01. cpp
2: // from Borland C++ 3.1 -- Object oriented programming page 186
3: #include <iostream>
4: using namespace std;
5:
6: void display (long val) { cout << val << " in function 1 " << endl;} // ...( 1)
7: void display (double val) { cout << val << " in function 2" << endl; } //..( 2)
8: void display (float val) { cout << val <<" in function 3 " << endl; } //.....( 3)
9:
10:
11: main() {
12: display ( 123456789) ;
13: display ( 3.141592653) ;
14: display ( 23) ;
15: return 0 ;
16:
17: }
( เฉลย:เมื่อ คอมไพล์ด้วย BCC 5.1 จะแจ้งข้อผิดพลาด ในบรรทัดที่ 12 และ 14 ว่าเป็นการผ่านค่าแบบกำกวม ทำให้ไม่สามารถเลือกใช้ display ที่เป็น long หรือ float
บรรทัดที่ 13 น่าจะเรียกใช้ฟังก์ชัน 3 แต่ค่าคงที่ 3.141582653 จะถูกคอมไพเลอร์มองว่าเป็น double เพราะไม่มีการประกาศตัวแปรว่าเป็น float ดังนั้นจึงเรียกใช้ฟังก์ชันที่ 2)
2.  จงเขียนฟังก์ชันที่รับตัวเลขจำนวนเต็ม 3 ค่า ฟังก์ชันจะส่งกลับจำนวนที่มีค่ามากที่สุด
int max_int ( int i, int j, int k);
3  เขียนโปรแกรมที่ใช้ฟังก์ชันและตรวจสอบ
โดยกำหนด function prototype ดังนี้
double fx( double x);
4.  จงเขียนและตรวจสอบฟังก์ชันต่อไปนี้
โดยกำหนด function prototype ดังนี้
double gx( double x);
5.  เขียนและตรวจสอบฟังก์ชันที่หาความยาวเส้นตรงระหว่างจุด (x 1 , y 1 ) และ จุด (x 2 , y 2 )
length = 
6.  จงเขียนและตรวจสอบว่า จุด (x,y) อยู่ใน quadrant ใด
int quadrant ( double x, double y);

7.  หอคอยแห่งฮานอย เกมส์นี้ประกอบด้วยเสาหลักจำนวน 3 หลัก ให้เป็นเสา A, B และ C ตามลำดับ ที่เสา A มีแผ่นจานขนาดต่าง ๆ เรียงซ้อนกันโดยให้จานแผ่นใหญ่อยู่ล่างสุดไล่ไปตามลำดับแผ่นเล็กอยู่บนสุดจำนวน N แผ่น (จากรูป N = 3 แผ่น) กติกาการเล่นคือให้เคลื่อนย้ายแผ่นจากจากเสา A ทั้งหมดไปยังเสา C มีข้อบังคับว่า
•  การหยิบแต่ละครั้ง จะหยิบแผ่นจานได้เพียง 1 แผ่น
•  ห้ามนำจานแผ่นใหญ่วางซ้อนบนจานแผ่นเล็ก
•  สามารถใช้เสา B เป็นที่พักจานชั่วคราวได้ แต่ห้ามนำจานแผ่นใหญ่วางซ้อนจานแผ่นเล็กเช่นกัน


ถ้าจานมีเพียง 1 แผ่น (N = 1) สามารถย้ายจานจากเสา A ไปยังเสา C ได้เลยโดยไม่ต้องใช้เสา B เป็นที่พักจาน (จะใช้เป็นเงื่อนไขในการหยุดการทำงาน และส่งค่าคืนกลับของฟังก์ชัน)
ถ้ามีจานอยู่ 3 แผ่น จะย้ายจานจากเสา A มาไว้ที่เสา C ลำดับการย้ายจะเป็นดังนี้
A -> C
A -> B
C -> B
A -> C
B -> A
B -> C
A -> C
8. ชุดคำสั่งต่อไปนี้ ผิดตรงส่วนใด
void main ( ) {
discount_price ( 500.0 , 10 );
….
}
void discount_price (float & price, float & percent_discount) {
price = price - price*percent_discount/100.0;
}
[เฉลย: ผิดตรงที่ผ่านค่าคงที่ให้กับการผ่านค่า by reference ]
9. บรรทัดใดทำได้ถูกต้อง เมื่อต้องการประกาศตัวแปร 3 ตัว เป็นแบบ passing by reference
int& a, b, c;
int &a, &b, &c;
int &a, b ,c;
[ เฉลย: บรรทัดแรก ถูกเพียงบรรทัดเดียว บรรทัดที่ 2, 3 ไม่ถูกต้อง ]
10. พิจารณาคำสั่งต่อไปนี้ มีข้อผิดพลาดตรงไหนบ้าง
int minus (int i ) {
return –i;
}
long minus (long r) {
return –r;
}
int main ( ) {
int a = minus (‘A');
cout << a;
long b = minus (271.83);
}
11.  การประกาศ Overloaded function ของแต่ละบรรทัดถูกต้องหรือไม่
int add ( int i, int j);
int add ( int i, int j, int k);
[เฉลย: ถูกต้อง]
12.  การประกาศ Overloaded function ต่อไปนี้มีข้อบกพร่องหรือไม่
int calculate ( int x);
int calculate ( int x, int y = 50);
[เฉลย: ปัญหาคือ การเรียกใช้โดยผ่านค่าเพียงตัวเดียวอาจจะเป็นฟังก์ชันที่ 2 โดยละเว้นค่า default ก็ได้ ]