มีการโพสต์มากมายที่บ่นเกี่ยวกับการบรรทุกเกินพิกัด
ฉันรู้สึกว่าต้องอธิบายแนวคิด "โอเปอเรเตอร์การบรรทุกเกินพิกัด" เพื่อเสนอมุมมองทางเลือกในแนวคิดนี้
รหัสทำให้งง?
อาร์กิวเมนต์นี้เป็นความเข้าใจผิด
การทำให้งงเป็นไปได้ในทุกภาษา ...
มันเป็นเรื่องง่ายที่จะทำให้งงงวยรหัสใน C หรือ Java ผ่านฟังก์ชั่น / วิธีการที่มันเป็นใน C ++ ผ่านการดำเนินการเกินพิกัด:
// C++
T operator + (const T & a, const T & b) // add ?
{
T c ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
// Java
static T add (T a, T b) // add ?
{
T c = new T() ;
c.value = a.value - b.value ; // subtract !!!
return c ;
}
/* C */
T add (T a, T b) /* add ? */
{
T c ;
c.value = a.value - b.value ; /* subtract !!! */
return c ;
}
... แม้จะอยู่ในอินเตอร์เฟสมาตรฐานของ Java
สำหรับตัวอย่างอื่นมาดูCloneable
อินเตอร์เฟสใน Java:
คุณควรจะโคลนวัตถุที่ใช้อินเตอร์เฟซนี้ แต่คุณสามารถโกหก และสร้างวัตถุต่าง ๆ ในความเป็นจริงอินเทอร์เฟซนี้อ่อนแอดังนั้นคุณสามารถส่งคืนวัตถุชนิดอื่นพร้อมกันเพียงเพื่อความสนุก:
class MySincereHandShake implements Cloneable
{
public Object clone()
{
return new MyVengefulKickInYourHead() ;
}
}
เนื่องจากCloneable
อินเตอร์เฟซสามารถถูกใช้ในทางที่ผิด / obfuscated มันควรถูกแบนในบริเวณเดียวกันกับตัวดำเนินการ C + + เกินพิกัดที่ควรจะเป็น?
เราสามารถโอเวอร์โหลดtoString()
เมธอดของMyComplexNumber
คลาสเพื่อให้มันส่งคืนชั่วโมงที่ทำให้เป็นสตริงของวัน ควรtoString()
ห้ามการบรรทุกเกินพิกัดเช่นกัน? เราสามารถก่อวินาศกรรมMyComplexNumber.equals
เพื่อให้มันคืนค่าแบบสุ่มแก้ไขตัวถูกดำเนินการ ... ฯลฯ ฯลฯ
ใน Java เช่นเดียวกับใน C ++ หรือภาษาใด ๆ โปรแกรมเมอร์ต้องเคารพความหมายขั้นต่ำเมื่อเขียนโค้ด ซึ่งหมายถึงการใช้งานadd
ฟังก์ชั่นที่เพิ่มและCloneable
วิธีการใช้งานที่โคลนและ++
ผู้ประกอบการกว่าการเพิ่มขึ้น
มีอะไรน่างงงวยอยู่แล้ว?
ตอนนี้เรารู้แล้วว่าโค้ดสามารถก่อวินาศกรรมได้แม้ผ่านวิธีการ Java แบบดั้งเดิมเราสามารถถามตัวเองเกี่ยวกับการใช้งานจริงของตัวดำเนินการโอเวอร์โหลดใน C ++ ได้หรือไม่
สัญลักษณที่ชัดเจนและเปนธรรมชาติ
เราจะเปรียบเทียบด้านล่างสำหรับกรณีที่แตกต่างกันรหัส "เดียวกัน" ใน Java และ C ++ เพื่อให้ทราบถึงรูปแบบการเข้ารหัสที่ชัดเจนยิ่งขึ้น
เปรียบเทียบธรรมชาติ:
// C++ comparison for built-ins and user-defined types
bool isEqual = A == B ;
bool isNotEqual = A != B ;
bool isLesser = A < B ;
bool isLesserOrEqual = A <= B ;
// Java comparison for user-defined types
boolean isEqual = A.equals(B) ;
boolean isNotEqual = ! A.equals(B) ;
boolean isLesser = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual = A.comparesTo(B) <= 0 ;
โปรดทราบว่า A และ B อาจเป็นประเภทใดก็ได้ใน C ++ ตราบใดที่มีการระบุตัวดำเนินการโอเวอร์โหลด ใน Java เมื่อ A และ B ไม่ใช่แบบดั้งเดิมรหัสอาจสับสนมากแม้สำหรับวัตถุที่เหมือนดั่งเดิม (BigInteger ฯลฯ ) ...
ตัวเข้าถึงอาร์เรย์ / คอนเทนเนอร์ตามธรรมชาติและตัวห้อย:
// C++ container accessors, more natural
value = myArray[25] ; // subscript operator
value = myVector[25] ; // subscript operator
value = myString[25] ; // subscript operator
value = myMap["25"] ; // subscript operator
myArray[25] = value ; // subscript operator
myVector[25] = value ; // subscript operator
myString[25] = value ; // subscript operator
myMap["25"] = value ; // subscript operator
// Java container accessors, each one has its special notation
value = myArray[25] ; // subscript operator
value = myVector.get(25) ; // method get
value = myString.charAt(25) ; // method charAt
value = myMap.get("25") ; // method get
myArray[25] = value ; // subscript operator
myVector.set(25, value) ; // method set
myMap.put("25", value) ; // method put
ใน Java เราเห็นว่าแต่ละคอนเทนเนอร์ทำสิ่งเดียวกัน (เข้าถึงเนื้อหาผ่านดัชนีหรือตัวระบุ) เรามีวิธีที่แตกต่างในการทำซึ่งทำให้เกิดความสับสน
ใน C ++ แต่ละคอนเทนเนอร์ใช้วิธีเดียวกันในการเข้าถึงเนื้อหาด้วยตัวดำเนินการที่มากเกินไป
การจัดการประเภทขั้นสูงตามธรรมชาติ
ตัวอย่างด้านล่างใช้ Matrix
วัตถุซึ่งพบโดยใช้ลิงก์แรกที่พบใน Google สำหรับ " Java Matrix object " และ " C ++ Matrix object ":
// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E = A * (B / 2) ;
E += (A - B) * (C + D) ;
F = E ; // deep copy of the matrix
// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ; // deep copy of the matrix
และนี่ไม่ได้ จำกัด เฉพาะเมทริกซ์ BigInteger
และBigDecimal
ชั้นเรียนของ Java ต้องทนทุกข์ทรมานจากความสับสนฟุ่มเฟื่อยเดียวกันขณะที่เทียบเท่าใน C ++ เป็นเป็นที่ชัดเจนเป็นตัวชนิด
ตัววนซ้ำตามธรรมชาติ:
// C++ Random Access iterators
++it ; // move to the next item
--it ; // move to the previous item
it += 5 ; // move to the next 5th item (random access)
value = *it ; // gets the value of the current item
*it = 3.1415 ; // sets the value 3.1415 to the current item
(*it).foo() ; // call method foo() of the current item
// Java ListIterator<E> "bi-directional" iterators
value = it.next() ; // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ; // sets the value 3.1415 to the current item
ฟังก์ชั่นธรรมชาติ:
// C++ Functors
myFunctorObject("Hello World", 42) ;
// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;
การต่อข้อความ:
// C++ stream handling (with the << operator)
stringStream << "Hello " << 25 << " World" ;
fileStream << "Hello " << 25 << " World" ;
outputStream << "Hello " << 25 << " World" ;
networkStream << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;
// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;
ตกลงใน Java คุณสามารถใช้MyString = "Hello " + 25 + " World" ;
... แต่รอสักครู่: นี่คือการใช้งานตัวดำเนินการมากเกินไปใช่ไหม? มันไม่ใช่การโกงหรอ ???
:-D
รหัสทั่วไป?
ตัวดำเนินการแก้ไขรหัสทั่วไปที่เหมือนกันควรจะสามารถใช้งานได้ทั้งสำหรับบิวด์อิน / ตัวดั้งเดิม (ซึ่งไม่มีส่วนต่อประสานใน Java), วัตถุมาตรฐาน (ซึ่งไม่สามารถมีอินเตอร์เฟสที่ถูกต้อง) และวัตถุที่ผู้ใช้กำหนด
ตัวอย่างเช่นการคำนวณค่าเฉลี่ยของสองค่าของประเภทใด ๆ :
// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
return (p_lhs + p_rhs) / 2 ;
}
int intValue = getAverage(25, 42) ;
double doubleValue = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix matrixValue = getAverage(mA, mB) ; // mA, mB are Matrix
// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.
การอภิปรายผู้ปฏิบัติงานมากไป
ตอนนี้เราได้เห็นการเปรียบเทียบที่เป็นธรรมระหว่างรหัส C ++ โดยใช้ตัวดำเนินการโอเวอร์โหลดและรหัสเดียวกันใน Java ตอนนี้เราสามารถพูดถึง "ตัวดำเนินการเกินพิกัด" เป็นแนวคิด
ผู้ประกอบการเกินพิกัดที่มีอยู่ตั้งแต่ก่อนคอมพิวเตอร์
แม้ภายนอกของวิทยาศาสตร์คอมพิวเตอร์ที่มีการดำเนินงานมาก: ตัวอย่างเช่นในวิชาคณิตศาสตร์ประกอบการชอบ+
, -
, *
ฯลฯ มีมากเกินไป
อันที่จริงความหมายของ+
, -
, *
ฯลฯ การเปลี่ยนแปลงขึ้นอยู่กับชนิดของตัวถูกดำเนินการที่ (numerics เวกเตอร์, ฟังก์ชั่นคลื่นควอนตัม, การฝึกอบรม ฯลฯ )
พวกเราส่วนใหญ่ซึ่งเป็นส่วนหนึ่งของหลักสูตรวิทยาศาสตร์ของเราได้เรียนรู้ความสำคัญหลายประการสำหรับผู้ประกอบการขึ้นอยู่กับประเภทของตัวถูกดำเนินการ เราพบว่าพวกเขาสับสนพวกเขา?
ผู้ประกอบการมากไปขึ้นอยู่กับตัวถูกดำเนินการ
นี่เป็นส่วนที่สำคัญที่สุดของการบรรทุกเกินพิกัด: เช่นเดียวกับในคณิตศาสตร์หรือในฟิสิกส์การดำเนินการขึ้นอยู่กับประเภทของตัวถูกดำเนินการ
ดังนั้นรู้ประเภทของตัวถูกดำเนินการและคุณจะรู้ผลของการดำเนินการ
แม้แต่ C และ Java ก็ยังมีผู้ให้บริการโหลดมากเกินไป
ใน C พฤติกรรมที่แท้จริงของผู้ปฏิบัติงานจะเปลี่ยนไปตามตัวถูกดำเนินการ ตัวอย่างเช่นการเพิ่มสองจำนวนเต็มแตกต่างจากการเพิ่มสองคู่หรือแม้กระทั่งหนึ่งจำนวนเต็มและหนึ่งคู่ มีแม้กระทั่งโดเมนเลขคณิตทั้งตัวชี้ (โดยไม่ต้องร่ายคุณสามารถเพิ่มจำนวนเต็มให้กับตัวชี้ แต่คุณไม่สามารถเพิ่มสองพอยน์เตอร์ ... )
ใน Java ไม่มีตัวชี้ทางคณิตศาสตร์ แต่บางคนยังพบว่าการต่อสตริงโดยไม่มีตัว+
ดำเนินการจะไร้สาระมากพอที่จะพิสูจน์ข้อยกเว้นใน "โอเปอเรเตอร์การบรรทุกเกินพิกัดเป็นความชั่วร้าย"
เป็นเพียงแค่คุณในฐานะ C (สำหรับเหตุผลทางประวัติศาสตร์) หรือ Java (สำหรับเหตุผลส่วนตัวดูด้านล่าง) coder คุณไม่สามารถให้บริการของคุณเอง
ใน C ++ ตัวดำเนินการมากไปไม่ใช่ตัวเลือก ...
ใน C ++ การโอเวอร์โหลดของผู้ประกอบการสำหรับประเภทในตัวเป็นไปไม่ได้ (และนี่เป็นสิ่งที่ดี) แต่ประเภทที่ผู้ใช้กำหนดเองสามารถมีตัวดำเนินการที่ผู้ใช้กำหนดเองมากเกินไป
ดังที่ได้กล่าวไปแล้วก่อนหน้านี้ใน C ++ และในทางตรงกันข้ามกับ Java ประเภทผู้ใช้จะไม่ถือว่าเป็นพลเมืองชั้นสองของภาษาเมื่อเทียบกับประเภทในตัว ดังนั้นหากประเภทในตัวมีตัวดำเนินการประเภทผู้ใช้ก็ควรจะมีเช่นกัน
ความจริงก็คือเหมือนtoString()
, clone()
, equals()
วิธีการสำหรับ Java ( เช่นกึ่งมาตรฐานเหมือน ), C ++ มากไปประกอบเป็นส่วนหนึ่งมากจาก C ++ ที่จะกลายเป็นธรรมชาติเป็นผู้ประกอบการเดิม C หรือก่อน Java วิธีการที่กล่าวถึง
เมื่อรวมกับการเขียนโปรแกรมแม่แบบการใช้งานมากเกินไปกลายเป็นรูปแบบการออกแบบที่รู้จักกันดี ในความเป็นจริงคุณไม่สามารถไปไกลได้ใน STL โดยไม่ต้องใช้โอเปอเรเตอร์ที่โอเวอร์โหลดและโอเปอร์เรเตอร์สำหรับคลาสของคุณเอง
... แต่ไม่ควรถูกทำร้าย
การบรรทุกเกินพิกัดของผู้ประกอบการควรพยายามเคารพความหมายของผู้ปฏิบัติงาน อย่าลบใน+
โอเปอเรเตอร์ (เช่น "ห้ามลบในadd
ฟังก์ชั่น" หรือ "คืนอึในclone
เมธอด")
การบรรทุกมากเกินไปอาจเป็นอันตรายได้เพราะอาจทำให้เกิดความคลุมเครือ ดังนั้นจึงควรสำรองไว้สำหรับกรณีที่กำหนดไว้อย่างดี สำหรับ&&
และ||
ไม่เคยเกินพวกเขาจนกว่าคุณจะรู้ว่าสิ่งที่คุณกำลังทำเพราะคุณจะสูญเสียการประเมินการลัดวงจรที่ผู้ประกอบการพื้นเมือง&&
และ||
เพลิดเพลิน
ดังนั้น ... ตกลง ... แล้วทำไมมันเป็นไปไม่ได้ใน Java?
เพราะ James Gosling พูดอย่างนั้น:
ฉันออกจากการใช้งานเกินกำลังเป็นทางเลือกที่ค่อนข้างเป็นส่วนตัวเพราะฉันเห็นคนจำนวนมากเกินไปใช้มันใน C ++
เจมส์กอสลิง ที่มา: http://www.gotw.ca/publications/c_family_interview.htm
โปรดเปรียบเทียบข้อความของ Gosling ด้านบนกับ Stroustrup ด้านล่าง:
การตัดสินใจในการออกแบบ C ++ หลายอย่างนั้นทำให้พวกเขาไม่ชอบที่จะบังคับให้คนทำสิ่งต่าง ๆ โดยเฉพาะ [... ] บ่อยครั้งที่ฉันถูกล่อลวงให้ทำสิ่งที่ฉันไม่ชอบด้วยตัวเองฉันไม่ได้ทำเช่นนั้นเพราะฉันไม่คิดว่า สิทธิที่จะบังคับให้มุมมองของฉันกับคนอื่น ๆ
Bjarne Stroustrup ที่มา: การออกแบบและวิวัฒนาการของ C ++ (พื้นหลังทั่วไป 1.3)
ผู้ประกอบการที่มากไปจะได้ประโยชน์จาก Java หรือไม่
วัตถุบางอย่างจะได้รับประโยชน์อย่างมากจากการบรรทุกเกินพิกัด (คอนกรีตหรือชนิดตัวเลขเช่น BigDecimal จำนวนเชิงซ้อนเมทริกซ์ภาชนะบรรจุตัววนซ้ำตัวเปรียบเทียบตัวแยกวิเคราะห์ ฯลฯ )
ใน C ++ คุณสามารถได้รับประโยชน์จากผลประโยชน์นี้เนื่องจากความอ่อนน้อมถ่อมตนของ Stroustrup ใน Java คุณเพียงแค่เมาเพราะตัวเลือกของ Goslingเลือกส่วนบุคคล
สามารถเพิ่มลงใน Java ได้หรือไม่
เหตุผลที่ไม่เพิ่มตัวดำเนินการมากเกินไปใน Java อาจเป็นการรวมตัวกันของการเมืองภายในการแพ้ต่อคุณสมบัติความไม่ไว้วางใจของนักพัฒนา เวลาที่จะเขียนข้อมูลจำเพาะที่ถูกต้อง ฯลฯ
ดังนั้นอย่ากลั้นหายใจรอคุณสมบัตินี้ ...
แต่พวกเขาทำใน C # !!!
ใช่...
แม้ว่าสิ่งนี้จะห่างไกลจากความแตกต่างเพียงอย่างเดียวระหว่างสองภาษา แต่สิ่งนี้ไม่เคยทำให้ฉันผิดหวัง
เห็นได้ชัดว่า C # folks ด้วย"ทุกดั้งเดิมคือstruct
และstruct
มาจากวัตถุ"ทำให้ถูกต้องตั้งแต่ครั้งแรก
แม้จะมี FUD ทั้งหมดกับการใช้งานเกินพิกัดที่กำหนดไว้แล้วภาษาต่อไปนี้รองรับ: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, ทับทิม , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...
ภาษาจำนวนมากที่มีปรัชญา (และบางครั้งก็เป็นปฏิปักษ์) ที่แตกต่างกันมากมาย
อาหารสมอง...