กลับไปหน้าบทความ

อ่าน 9 นาที

การโอเวอร์โหลดผู้ปฏิบัติงาน

ในการเขียนโปรแกรมคอมพิวเตอร์ การโอเวอร์โหลดตัวดำเนินการ ( operator overloading ) หรือบางครั้งเรียกว่า โพลีมอร์ฟิซึมเฉพาะกิจ ( operator ad hoc polymorphism ) เป็นกรณีเฉพาะของโพ...

การโอเวอร์โหลดผู้ปฏิบัติงาน

ในการเขียนโปรแกรมคอมพิวเตอร์ การโอเวอร์โหลดตัวดำเนินการ ( operator overloading ) หรือบางครั้งเรียกว่า โพลีมอร์ฟิซึมเฉพาะกิจ ( operator ad hoc polymorphism ) เป็นกรณีเฉพาะของโพ ลีมอร์ฟิ ซึม (polymorphism ) ที่ตัวดำเนินการต่างกัน มีวิธีการใช้งานที่แตกต่างกันไปขึ้นอยู่กับ อาร์กิวเมนต์การโอเวอร์โหลดตัวดำเนินการโดยทั่วไปถูกกำหนดโดยภาษาโปรแกรมโปรแกรมเมอร์หรือทั้งสองอย่าง

เหตุผล

การโอเวอร์โหลดตัวดำเนินการเป็นน้ำตาลทางไวยากรณ์และถูกใช้เพราะช่วยให้การเขียนโปรแกรมโดยใช้สัญลักษณ์ที่ใกล้เคียงกับโดเมนเป้าหมายมากขึ้น[ 1 ]และช่วยให้ประเภทที่ผู้ใช้กำหนดได้รับการสนับสนุนทางไวยากรณ์ในระดับเดียวกับประเภทที่สร้างขึ้นในภาษา เป็นเรื่องปกติ เช่น ในการคำนวณทางวิทยาศาสตร์ ซึ่งช่วยให้สามารถจัดการการแสดงผลทางคณิตศาสตร์ด้วยไวยากรณ์เดียวกันกับบนกระดาษ

การโอเวอร์โหลดตัวดำเนินการไม่ได้เปลี่ยนแปลงพลังการแสดงออกของภาษา (ด้วยฟังก์ชัน) เนื่องจากสามารถจำลองได้โดยใช้การเรียกฟังก์ชัน ตัวอย่างเช่น พิจารณาตัวแปรa, bและcที่มีประเภทที่ผู้ใช้กำหนดเอง เช่นเมทริกซ์ :

a + b * c

ในภาษาโปรแกรมที่รองรับการโอเวอร์โหลดตัวดำเนินการ และโดยทั่วไปถือว่า*ตัวดำเนินการหลักมีลำดับความสำคัญ สูง กว่า+ตัวดำเนินการรอง วิธีการเขียนแบบกระชับมีดังนี้:

Add(a, Multiply(b, c))

อย่างไรก็ตาม โครงสร้างประโยคแบบเดิมสะท้อนให้เห็นถึงการใช้งานทางคณิตศาสตร์ทั่วไป

ตัวอย่าง

ในกรณีนี้ ตัวดำเนินการบวกถูกโอเวอร์โหลดเพื่อให้สามารถบวกกับชนิดข้อมูลที่ผู้ใช้กำหนดเองTimeในC++ ได้ :

ตัวดำเนินการเวลา+ ( const Time & lhs , const Time & rhs ) { เวลาtemp = lhs ; temp . seconds += rhs . seconds ; temp . minutes += temp . seconds / 60 ; temp . seconds %= 60 ; temp . minutes += rhs . minutes ; temp . hours += temp . minutes / 60 ; temp . minutes %= 60 ; temp . hours += rhs . hours ; return temp ; }

การบวกเป็นการดำเนินการแบบไบนารีซึ่งหมายความว่ามีตัวถูกดำเนินการ สองตัว ในภาษา C++ อาร์กิวเมนต์ที่ส่งผ่านคือตัวถูกดำเนินการ และtempออบเจ็กต์คือค่าที่ส่งคืน

การดำเนินการนี้สามารถกำหนดเป็นเมธอดของคลาสได้เช่นกัน โดยแทนที่lhsด้วยthisอาร์กิวเมนต์ที่ซ่อนอยู่ อย่างไรก็ตาม วิธีนี้บังคับให้ตัวถูกดำเนินการทางซ้ายต้องเป็นประเภทTime:

// คำว่า "const" ที่อยู่หน้าวงเล็บปีกกาเปิดหมายความว่า `this` จะไม่ถูกแก้ไขTime Time :: operator + ( const Time & rhs ) const { Time temp = * this ; // `this` ไม่ควรถูกแก้ไข ดังนั้นจึงสร้างสำเนาtemp . seconds += rhs . seconds ; temp . minutes += temp . seconds / 60 ; temp . seconds %= 60 ; temp . minutes += rhs . minutes ; temp . hours += temp . minutes / 60 ; temp . minutes %= 60 ; temp . hours += rhs . hours ; return temp ; }

โปรดทราบว่า ตัวดำเนินการเอก ภาคที่กำหนดเป็นเมธอดของคลาสจะไม่ได้รับอาร์กิวเมนต์ใดๆ (ใช้งานได้เฉพาะจากthis):

bool Time :: operator ! () const { return hours == 0 && minutes == 0 && seconds == 0 ; }

ตัวดำเนินการน้อยกว่า (<) มักถูกนำไปใช้ในการเรียงลำดับโครงสร้างหรือคลาส:

คลาสIntegerPair { private : int x ; int y ; public : explicit IntegerPair ( int x = 0 , int y = 0 ) : x { x }, y { y } {}bool operator < ( const IntegerPair & p ) const { if ( x == p . x ) { return y < p . y ; } return x < p . x ; } };

เช่นเดียวกับตัวอย่างก่อนหน้านี้ ในตัวอย่างสุดท้าย การโอเวอร์โหลดตัวดำเนินการจะทำภายในคลาส ในภาษา C++ หลังจากโอเวอร์โหลดตัวดำเนินการน้อยกว่า ( operator<) แล้วฟังก์ชันการเรียงลำดับมาตรฐานสามารถใช้เพื่อเรียงลำดับคลาสบางคลาสได้

ตั้งแต่C++20 เป็นต้นมา ด้วยการแนะนำตัว ดำเนิน การเปรียบเทียบสามทาง (three-way comparison operator operator<=>) ตัวดำเนินการเรียงลำดับทั้งหมดสามารถกำหนดได้โดยการกำหนดตัวดำเนินการนั้นเพียงอย่างเดียว ตัวดำเนินการเปรียบเทียบสามทางมีอยู่ในหลายภาษา รวมถึงC++ , Python , Rust , SwiftและPHPภาษาอื่นๆ เช่นJavaและC#Comparable.compareTo() จะใช้วิธี การแทน

import std ;การใช้std :: strong_ordering ;คลาสIntegerPair { private : int x ; int y ; public : explicit IntegerPair ( int x = 0 , int y = 0 ) : x { x }, y { y } {}// สามารถสร้างอัตโนมัติได้ด้วย = default; strong_ordering operator < ( const IntegerPair & p ) const { if ( strong_ordering cmp = x <=> p . x ; cmp != strong_ordering :: equal ) { return cmp ; } return y <=> p . y ; } };

คำวิจารณ์

การโอเวอร์โหลดตัวดำเนินการถูกวิพากษ์วิจารณ์[ 2 ]เนื่องจากอนุญาตให้โปรแกรมเมอร์กำหนดความหมายของตัวดำเนินการใหม่โดยขึ้นอยู่กับประเภทของตัวถูกดำเนินการ ตัวอย่างเช่น การใช้<<ตัวดำเนินการในC++ จะเลื่อนบิตในตัวแปรไปทางซ้าย 2 บิตหากและเป็นประเภทจำนวนเต็ม แต่ถ้าเป็นสตรีมเอาต์พุต โค้ดข้างต้นจะพยายามเขียนไปยังสตรีม เนื่องจากโอเวอร์โหลดตัวดำเนินการอนุญาตให้โปรแกรมเมอร์ดั้งเดิมเปลี่ยนความหมายปกติของตัวดำเนินการและทำให้โปรแกรมเมอร์คนต่อๆ ไปประหลาดใจ จึงถือเป็นแนวปฏิบัติที่ดีที่จะใช้โอเวอร์โหลดตัวดำเนินการอย่างระมัดระวัง (ผู้สร้างJavaตัดสินใจที่จะไม่ใช้คุณสมบัตินี้[ 3 ]แม้ว่าจะไม่จำเป็นต้องเป็นเพราะเหตุผลนี้ก็ตาม)a<<bababab

อีกประเด็นหนึ่งที่ละเอียดอ่อนกว่าเกี่ยวกับตัวดำเนินการคือ กฎบางอย่างจากคณิตศาสตร์อาจถูกคาดหวังผิดหรือถูกสันนิษฐานโดยไม่ได้ตั้งใจ ตัวอย่างเช่น คุณสมบัติการสลับที่ของ + (กล่าวคือa + b == b + a) ไม่ได้ใช้ได้เสมอไป ตัวอย่างเช่น เมื่อตัวถูกดำเนินการเป็นสตริง เนื่องจาก + มักถูกใช้งานเกินขอบเขตเพื่อทำการต่อสตริง (เช่น"bird" + "song"ให้ผลลัพธ์"birdsong"เป็น ในขณะที่"song" + "bird"ให้ผลลัพธ์เป็น"songbird") ข้อโต้แย้งทั่วไปสำหรับข้อโต้แย้งนี้มาจากคณิตศาสตร์โดยตรง: ในขณะที่ + มีคุณสมบัติการสลับที่กับจำนวนเต็ม (และโดยทั่วไปกับจำนวนเชิงซ้อนใดๆ) แต่จะไม่มีคุณสมบัติการสลับที่กับตัวแปร "ประเภท" อื่นๆ ในทางปฏิบัติ + ยังไม่เป็นไปตามคุณสมบัติการจัดกลุ่ม เสมอไป ตัวอย่างเช่น กับค่าทศนิยมเนื่องจากข้อผิดพลาดในการปัดเศษ อีกตัวอย่างหนึ่ง: ในคณิตศาสตร์ การคูณมีคุณสมบัติการสลับที่กับจำนวนจริงและจำนวนเชิงซ้อน แต่ไม่มีคุณสมบัติการสลับที่กับการคูณเมทริกซ์

แคตตาล็อก

มีการจัดประเภทของภาษาโปรแกรมที่ใช้กันทั่วไปบางภาษา โดยพิจารณาจากว่าตัวดำเนินการของภาษาเหล่านั้นสามารถโอเวอร์โหลดได้โดยโปรแกรมเมอร์หรือไม่ และตัวดำเนินการเหล่านั้นถูกจำกัดไว้เฉพาะชุดที่กำหนดไว้ล่วงหน้าหรือไม่

ผู้ปฏิบัติงานไม่สามารถรับน้ำหนักเกินได้โอเวอร์โหลดได้
กำหนดใหม่ได้[ 4 ]
ชุดจำนวนจำกัด

ลำดับเหตุการณ์ของการโอเวอร์โหลดผู้ปฏิบัติงาน

ทศวรรษ 1960

ข้อกำหนด ALGOL 68อนุญาตให้ผู้ปฏิบัติงานทำงานเกินกำลังได้[ 36 ]

ข้อความที่คัดมาจากข้อกำหนดภาษา ALGOL 68 (หน้า 177) ซึ่งมีการนิยามตัวดำเนินการโอเวอร์โหลด ¬, =, ≠ และabsไว้:

10.2.2. การดำเนินการกับตัวถูกดำเนินการแบบบูลีน a) op ∨ = ( bool a, b) bool :( a | true | b ); b) op ∧ = ( bool a, b) bool : ( a | b | false ); c) op ¬ = ( bool a) bool : ( a | false | true ); d) op = = ( bool a, b) bool :( a∧b ) ∨ ( ¬b∧¬a ); e) op ≠ = ( bool a, b) bool : ¬(a=b); f) op abs = ( bool a) int : ( a | 1 | 0 );

โปรดทราบว่าไม่จำเป็นต้องมีการประกาศพิเศษใดๆ เพื่อโอเวอร์โหลดตัวดำเนินการ และโปรแกรมเมอร์สามารถสร้างตัวดำเนินการใหม่ได้อย่างอิสระ สำหรับตัวดำเนินการแบบไดอะดิก สามารถกำหนดลำดับความสำคัญเมื่อเทียบกับตัวดำเนินการอื่นๆ ได้:

prio max = 9; op max = ( int a, b) int : ( a>b | a | b ); op ++ = ( ref int a ) int : ( a +:= 1 );

ทศวรรษ 1980

ภาษา Adaรองรับการโอเวอร์โหลดตัวดำเนินการมาตั้งแต่เริ่มแรก โดยมีการเผยแพร่มาตรฐานภาษา Ada 83 อย่างไรก็ตาม ผู้พัฒนาภาษาเลือกที่จะไม่อนุญาตให้มีการกำหนดตัวดำเนินการใหม่ อนุญาตให้โอเวอร์โหลดได้เฉพาะตัวดำเนินการที่มีอยู่แล้วในภาษาเท่านั้น โดยการกำหนดฟังก์ชันใหม่ที่มีตัวระบุ เช่น "+", "*", "&" เป็นต้น การแก้ไขภาษาในภายหลัง (ในปี 1995 และ 2005) ยังคงรักษาข้อจำกัดในการโอเวอร์โหลดตัวดำเนินการที่มีอยู่แล้วนี้ไว้

ในC++การโอเวอร์โหลดตัวดำเนินการมีความละเอียดกว่าในALGOL 68 [ 37 ]

ทศวรรษ 1990

นักออกแบบภาษา Javaที่Sun Microsystemsเลือกที่จะละเว้นการโอเวอร์โหลด[ 38 ] [ 39 ] [ 40 ]เมื่อถูกถามเกี่ยวกับการโอเวอร์โหลดตัวดำเนินการ Brian Goetz จากOracleตอบว่า " ประเภทค่ามาก่อน จากนั้นเราค่อยมาคุยกัน" ซึ่งแสดงให้เห็นว่าอาจมีการเพิ่มเข้ามาหลังจากProject Valhalla [ 41 ]

Pythonอนุญาตให้โอเวอร์โหลดตัวดำเนินการผ่านการใช้งานเมธอดที่มีชื่อพิเศษ[ 42 ]ตัวอย่างเช่น ตัวดำเนินการบวก (+) สามารถโอเวอร์โหลดได้โดยการใช้งานobj.__add__(self, other)เมธอด

Rubyอนุญาตให้ใช้การโอเวอร์โหลดตัวดำเนินการ (operator overloading) เพื่อเป็นตัวช่วยทางไวยากรณ์สำหรับการเรียกใช้เมธอดแบบง่ายๆ

ภาษา Luaอนุญาตให้ใช้การโอเวอร์โหลดตัวดำเนินการ (operator overloading) เป็นรูปแบบการเขียนโค้ดที่ช่วยให้เขียนเมธอดได้ง่ายขึ้น โดยมีคุณสมบัติเพิ่มเติมคือ หากตัวถูกดำเนินการตัวแรกไม่ได้กำหนดตัวดำเนินการนั้นไว้ ระบบจะใช้เมธอดของตัวถูกดำเนินการตัวที่สองแทน

ทศวรรษ 2000

Microsoft เพิ่มการโอเวอร์โหลดตัวดำเนินการให้กับC#ในปี 2001 และให้กับVisual Basic .NETในปี 2003 การโอเวอร์โหลดตัวดำเนินการของ C# มีไวยากรณ์คล้ายกับการโอเวอร์โหลดตัวดำเนินการของ C++ มาก: [ 43 ]

คลาสสาธารณะFraction { ตัวแปร int numerator ส่วนตัว; ตัวแปร int denominator ส่วนตัว;// ...public static Fraction operator + ( Fraction lhs , Fraction rhs ) => new Fraction ( lhs . numerator * rhs . denominator + rhs . numerator * lhs . denominator , lhs . denominator * rhs . denominator ); }

Scalaถือว่าตัวดำเนินการทั้งหมดเป็นเมธอด ดังนั้นจึงอนุญาตให้โอเวอร์โหลดตัวดำเนินการผ่านพร็อกซีได้

ทศวรรษ 2010

ในRakuนิยามของตัวดำเนินการทั้งหมดจะถูกมอบหมายให้กับฟังก์ชันเชิงศัพท์ ดังนั้น การใช้นิยามฟังก์ชัน ตัวดำเนินการสามารถถูกโอเวอร์โหลดหรือเพิ่มตัวดำเนินการใหม่ได้ ตัวอย่างเช่น ฟังก์ชันที่กำหนดไว้ใน ซอร์สโค้ด ของ Rakudoสำหรับการเพิ่มค่าออบเจ็กต์ Date ด้วย " +" คือ:

multi infix:<+> ( Date:D $d , Int:D $x ) { Date . new-from-daycount ( $d . daycount + $x ) } 

เนื่องจากมีการใช้คำว่า "multi" ฟังก์ชันจึงถูกเพิ่มเข้าไปในรายการตัวเลือก การเรียก ใช้แบบหลายทาง (multidispatch candidates) และ " +" จะถูกโอเวอร์โหลดเฉพาะในกรณีที่ตรงตามข้อจำกัดประเภทในลายเซ็นฟังก์ชันเท่านั้น แม้ว่าความสามารถในการโอเวอร์โหลดจะรวมถึง+, *, >=, ตัว ดำเนิน การแบบ postfix และ termiและอื่นๆ แต่ก็ยังอนุญาตให้โอเวอร์โหลดตัวดำเนินการวงเล็บปีกกาต่างๆ ได้ด้วย ได้แก่[x, y], x[y], x{y}, x(y)และ

Kotlinรองรับการโอเวอร์โหลดตัวดำเนินการมาตั้งแต่เริ่มสร้างโดยการเขียนทับฟังก์ชันที่มีชื่อเฉพาะ (เช่นplus(), inc(), rangeTo(), เป็นต้น) [ 44 ]

คลาสข้อมูลPoint ( val x : Int , val y : Int ) { operator fun plus ( other : Point ): Point { return Point ( this . x + other . x , this . y + other . y ) } }

เนื่องจากทั้ง Kotlin และ Java คอมไพล์เป็นไฟล์ .classดังนั้นเมื่อแปลงกลับเป็นJavaจะได้ผลลัพธ์ดังนี้:

คลาสสาธารณะPoint { // ฟิลด์และคอนสตรัคเตอร์...public Point plus ( Point other ) { return new Point ( this . x + other . x , this . y + other . y ); } }

การโอเวอร์โหลดตัวดำเนินการในRustสำเร็จได้โดยการนำคุณลักษณะ ไปใช้ ในstd::ops. [ 45 ]

ใช้std :: ops :: Add ;#[derive(Debug)] struct Point { x : i32 , y : i32 }impl Point { pub fn new ( x : i32 , y : i32 ) -> Self { Point { x , y } } }impl Add สำหรับPoint { type Output = Point ;fn add ( self , other : Point ) -> Point { Point { x : self . x + other . y , y : self . y + other . y } } }fn main () { let p1 : Point = Point :: new ( 1 , 2 ); let p2 : Point = Point :: new ( 3 , 4 ); let sum : Point = p1 + p2 ; println! ( "ผลรวมของ p1 และ p2: {:?}" , sum ); }

ดูเพิ่มเติม

ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=Operator_overloading&oldid=1359701869 "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ การโอเวอร์โหลดผู้ปฏิบัติงาน

ในการเขียนโปรแกรมคอมพิวเตอร์ การโอเวอร์โหลดตัวดำเนินการ ( operator overloading ) หรือบางครั้งเรียกว่า โพลีมอร์ฟิซึมเฉพาะกิจ ( operator ad hoc polymorphism ) เป็นกรณีเฉพาะของโพ...

เหตุผล

การโอเวอร์โหลดตัวดำเนินการเป็น น้ำตาลทางไวยากรณ์ และถูกใช้เพราะช่วยให้การเขียนโปรแกรมโดยใช้สัญลักษณ์ที่ใกล้เคียงกับโดเมนเป้าหมายมากขึ้น [ 1 ] และช่วยให้ประเภทที่ผู้ใช้กำหนดได้รับการสนับสนุนทางไวยากรณ์ในระดับเดียวกับประเภทที่สร้างขึ้นในภาษา เป็นเรื่องปกติ เช่น...

ตัวอย่าง

ในกรณีนี้ ตัวดำเนินการบวกถูกโอเวอร์โหลดเพื่อให้สามารถบวกกับชนิดข้อมูลที่ผู้ใช้กำหนดเอง Time ใน C++ ได้ :

คำวิจารณ์

การโอเวอร์โหลดตัวดำเนินการถูกวิพากษ์วิจารณ์ [ 2 ] เนื่องจากอนุญาตให้โปรแกรมเมอร์กำหนดความหมายของตัวดำเนินการใหม่โดยขึ้นอยู่กับประเภทของตัวถูกดำเนินการ ตัวอย่างเช่น การใช้ << ตัวดำเนินการใน C++ จะเลื่อนบิตในตัวแปรไปทางซ้าย 2 บิตหากและเป็นประเภทจำนวนเต็ม...