ทำไมไม่แนะนำพอยน์เตอร์เมื่อเข้ารหัสด้วย C ++


45

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


40
กรุณาลิงค์ไปยัง "ที่ไหนสักแห่ง" บริบทอาจมีความเกี่ยวข้องมาก

1
หวังว่าคำถามนี้มีประโยชน์สำหรับคุณ
Garet Claborn

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

1
การเพิ่มความคิดเห็นของ @Dunk บางครั้งตัวรวบรวมขยะในตัวในภาษาระดับสูงก็ไม่ทำงาน ยกตัวอย่างเช่นถังขยะของ ActionScript 3 ไม่ได้ยกตัวอย่างเช่น มีข้อผิดพลาดอยู่ในขณะนี้ที่เกี่ยวข้องกับNetConnectionอินสแตนซ์ที่ตัดการเชื่อมต่อจากเซิร์ฟเวอร์ ( stackoverflow.com/questions/14780456/… ) รวมถึงปัญหาที่มีวัตถุหลายรายการในโปรแกรมที่จะปฏิเสธโดยเฉพาะอย่างยิ่งที่จะรวบรวม ...
Panzercrisis

... ( adobe.com/devnet/actionscript/learning/as3-fundamentals/… - ค้นหาGCRoots are never garbage collected.และย่อหน้าที่เริ่มต้นด้วยThe MMgc is considered a conservative collector for mark/sweep.) ในทางเทคนิคนี่เป็นปัญหาใน Adobe Virtual Machine 2 ไม่ใช่ AS3 แต่เมื่อคุณมีปัญหาเช่นนี้ในภาษาระดับสูงที่มีการรวบรวมขยะในตัวคุณมักจะไม่มีวิธีที่แท้จริงในการแก้ไขข้อบกพร่อง ปัญหาเหล่านี้ออกจากโปรแกรมอย่างสมบูรณ์ ...
Panzercrisis

คำตอบ:


58

ฉันคิดว่าพวกเขาหมายความว่าคุณควรใช้พอยน์เตอร์อัจฉริยะแทนพอยน์เตอร์ปกติ

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

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

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


22
โดยทั่วไปแล้วจะไม่เก็บขยะอย่างเคร่งครัดในความรู้สึกคลาสสิกนับอ้างอิงเพิ่มเติม อย่างน้อยในสมาร์ท PTR ฉันเคย ([boost | std] :: shared_ptr)
Doug T.

3
คำตอบนี้มีข้อ จำกัด อย่างมากสำหรับตัวชี้อัจฉริยะซึ่งเป็นเพียงปัญหาเล็ก ๆ นอกจากนี้ตัวชี้สมาร์ทไม่ใช่การรวบรวมขยะ
deadalnix

3
นอกจากนี้ RAII โดยทั่วไป
Klaim

3
ไม่นี่ไม่ใช่สิ่งที่ตั้งใจ มันเป็นเพียงแง่มุมเดียวและไม่ใช่สิ่งที่สำคัญที่สุด
Konrad Rudolph

ฉันคิดว่าสมาร์ทพอยน์เตอร์เป็นสิ่งสำคัญที่สุด แต่ฉันเห็นด้วยที่มีคนอื่นอีกมากมาย
DeadMG

97

เนื่องจากฉันเป็นคนที่ตีพิมพ์การโต้เถียง“ อย่าใช้ตัวชี้ f * cking”ฉันรู้สึกว่าฉันควรแสดงความคิดเห็นที่นี่

ประการแรกในฐานะที่เป็นการถกเถียงอย่างชัดเจนมันแสดงถึงมุมมองที่รุนแรง มีมีการใช้งานที่ถูกต้องแน่นอนของ (ดิบ) ชี้ แต่ฉัน (และโปรแกรมเมอร์ C ++ มืออาชีพมากมาย) ยืนยันว่ากรณีเหล่านี้หายากเหลือเกิน แต่สิ่งที่เราหมายถึงจริงๆมีดังต่อไปนี้:

ครั้งแรก:

พอยน์เตอร์ดิบต้องไม่อยู่ในความทรงจำของตนเอง

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

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

ปัญหานี้สามารถขจัดได้ทั้งหมดโดยไม่มีค่าใช้จ่ายเพียงแค่ใช้ตัวชี้อัจฉริยะแทนตัวชี้แบบดิบ (ข้อแม้: สิ่งนี้ยังต้องใช้การคิดแน่นอนตัวชี้ที่ใช้ร่วมกันสามารถนำไปสู่วัฏจักรและทำให้เกิดการรั่วไหลของหน่วยความจำอีกครั้ง หลีกเลี่ยง)

ประการที่สอง:

การใช้ตัวชี้ส่วนใหญ่ใน C ++ นั้นไม่จำเป็น

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

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

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


5
เฮ้อ ... นี่น่าจะเป็นคำตอบที่มีผู้โหวตมากถึง 30+ คน ... โดยเฉพาะอย่างยิ่งในประเด็นที่สอง พอยน์เตอร์แทบไม่จำเป็นแม้แต่ใน C ++ ที่ทันสมัย
Charles Salvia

1
สิ่งที่เกี่ยวกับเช่น วัตถุ Gui เป็นเจ้าของวัตถุเอกสารจำนวนมาก มันมีสิ่งเหล่านี้เป็นตัวชี้ทั้งสองเพื่อให้สามารถประกาศไปข้างหน้า (หลีกเลี่ยงการคอมไพล์ใหม่) และเพื่อให้วัตถุสามารถเริ่มต้นได้เมื่อสร้าง (กับใหม่) แทนที่จะถูกสร้างขึ้นบนสแต็กในสภาวะว่างเปล่า ดูเหมือนว่าการใช้พอยน์เตอร์ที่ถูกต้องอย่างสมบูรณ์ - วัตถุที่ห่อหุ้มทั้งหมดไม่ควรอยู่ในกองเหรอ?
Martin Beckett

4
@ Martin GUI วัตถุเป็นหนึ่งในกรณีที่กราฟวัตถุตัวชี้เป็นทางออกที่ดีที่สุด แต่คำสั่งที่ต่อต้านพอยน์เตอร์ที่เป็นเจ้าของหน่วยความจำยังคงยืนอยู่ ใช้พอยน์เตอร์ที่ใช้ร่วมกันตลอดหรือพัฒนารูปแบบความเป็นเจ้าของที่เหมาะสมและมีความสัมพันธ์ที่อ่อนแอ (= ไม่ใช่เจ้าของ) ระหว่างตัวควบคุมผ่านตัวชี้แบบดิบ
Konrad Rudolph

1
std::unique_ptr@ VF1 นอกจากนี้ทำไมไม่ptr_vec? แต่โดยปกติแล้วเวกเตอร์ที่มีค่าจะยังคงสลับได้เร็วขึ้น (โดยเฉพาะกับซีแมนทิกส์ของการย้าย)
Konrad Rudolph

1
@gaazkam ไม่มีใครบังคับให้คุณใช้ตู้คอนเทนเนอร์มาตรฐานเท่านั้น Boost เช่นมีการใช้งานแผนที่ด้วยการสนับสนุนประเภทที่ไม่สมบูรณ์ อีกวิธีคือใช้ลบประเภท; boost::variantด้วย a recursive_wrapperน่าจะเป็นทางออกที่ฉันโปรดปรานเพื่อเป็นตัวแทนของ DAG
Konrad Rudolph

15

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


11

ค่อนข้างง่ายความคิด C คือ "มีปัญหาหรือไม่ใช้ตัวชี้" คุณสามารถเห็นสิ่งนี้ในสตริง C, พอยน์เตอร์ของฟังก์ชั่น, พอยน์เตอร์ -as-iterators, ตัวชี้ไปยังตัวชี้, โมฆะตัวชี้ - แม้ในวันแรก ๆ ของ C ++ พร้อมด้วยสมาชิกพอยน์เตอร์

แต่ใน C ++ คุณสามารถใช้ค่าสำหรับภารกิจเหล่านี้ได้หลายอย่างหรือทั้งหมด ต้องการฟังก์ชั่นนามธรรมหรือไม่? std::function. มันคือคุณค่าที่เป็นหน้าที่ std::string? มันคือคุณค่านั่นคือสตริง คุณสามารถดูวิธีการที่คล้ายกันทั่ว C ++ สิ่งนี้ทำให้การวิเคราะห์โค้ดง่ายขึ้นอย่างมากสำหรับทั้งมนุษย์และคอมไพเลอร์


ใน C ++: มีปัญหาใช่ไหม ใช้ตัวชี้ ตอนนี้คุณมีปัญหา 2 ข้อ ...
Daniel Zazula

10

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

การเลือกเครื่องมือเพื่อจุดประสงค์ที่แน่นอนทำให้โค้ดง่ายขึ้นและแสดงให้เห็นชัดเจนยิ่งขึ้น - ตัววนซ้ำสำหรับตัววนซ้ำตัวชี้อัจฉริยะสำหรับการจัดการตลอดเวลาเป็นต้น


3

นอกจากเหตุผลที่ระบุไว้แล้วมีสิ่งที่ชัดเจน: การเพิ่มประสิทธิภาพที่ดีขึ้น การวิเคราะห์นามแฝงนั้นซับซ้อนเกินไปในการนำเสนอของตัวคำนวณเลขคณิตในขณะที่การอ้างอิงเป็นการบอกถึงเครื่องมือเพิ่มประสิทธิภาพดังนั้นการวิเคราะห์นามแฝงที่ลึกซึ้งยิ่งขึ้นจึงเป็นไปได้หากใช้การอ้างอิงเท่านั้น


2

นอกจากความเสี่ยงของการรั่วไหลของหน่วยความจำที่ระบุโดยตัวชี้ @jmquigley และเลขคณิตของตัวชี้ถือได้ว่าเป็นปัญหาเพราะพอยน์เตอร์สามารถชี้ไปทุกที่ในหน่วยความจำทำให้เกิด "ยากที่จะหาบั๊ก" และ "ความเสี่ยง

นั่นคือเหตุผลที่พวกเขาเกือบถูกทอดทิ้งใน C # และ Java


คาดว่าพวกเขาจะไม่ "ถูกยกเลิก" ใน C # คำตอบนี้ไม่ดีมีการสะกดคำที่น่ากลัวและคำสั่งที่ไม่ถูกต้องที่น่ากลัว
Ramhound

1
พวกเขาเกือบถูกทอดทิ้ง ฉันขอโทษที่ไม่ได้พูดภาษาเจ้าของ
k3b

1
เฮ้เราช่วยสะกดได้ คุณหมายถึงว่าจะคาดหวังหรือยกเว้น?
DeveloperDon

1
ละทิ้งเกือบใน C # คุณยังสามารถเปิดใช้งานตัวชี้ได้โดยระบุunsafeคำหลัก
linquize

-1

C ++รองรับC , คุณสมบัติส่วนใหญ่, รวมถึง Objects และ Classes Cมีพอยน์เตอร์และสิ่งอื่น ๆ อยู่แล้ว

พอยน์เตอร์เป็นเทคนิคที่มีประโยชน์มากซึ่งสามารถรวมเข้ากับ Object Orientation และC ++รองรับได้ แต่เทคนิคนี้เป็นเรื่องยากที่จะสอนและยากที่จะเข้าใจและมันง่ายมากที่จะทำให้เกิดข้อผิดพลาดที่ไม่พึงประสงค์

ภาษาการเขียนโปรแกรมใหม่ ๆ หลายคนแสร้งว่าไม่ใช้พอยน์เตอร์กับวัตถุเช่น Java, .NET, Delphi, Vala, PHP, Scala แต่พอยน์เตอร์ยังคงใช้อยู่ "เบื้องหลัง" เทคนิค "ตัวชี้ที่ซ่อนอยู่" เหล่านี้เรียกว่า "การอ้างอิง"

อย่างไรก็ตามผมพิจารณาตัวชี้ (s) เป็นรูปแบบการเขียนโปรแกรมเป็นวิธีที่ถูกต้องในการแก้ปัญหาบางอย่างเช่นเดียวกับการเขียนโปรแกรมเชิงวัตถุไม่

นักพัฒนาอื่น ๆ อาจมีความคิดเห็นที่ต่างออกไป แต่ฉันขอแนะนำให้นักเรียนและโปรแกรมเมอร์เรียนรู้วิธี:

(1) ใช้ตัวชี้โดยไม่มีวัตถุ

(2) วัตถุที่ไม่มีตัวชี้

(3) ตัวชี้ไปยังวัตถุที่ชัดเจน

(4) ตัวชี้ "ซ่อน" ไปยังวัตถุ ( การอ้างอิง AKA ) ;-)

เพื่อให้.

แม้ว่าจะสอนยากและเรียนรู้ได้ยาก Object Pascal (Delphi, FreePascal, อื่น ๆ ) และC++(ไม่ใช่ Java หรือ C #) สามารถใช้สำหรับเป้าหมายเหล่านั้นได้

และในภายหลังผู้เขียนโปรแกรมมือใหม่สามารถย้ายไปที่ "การชี้ไปที่วัตถุ" ภาษาการเขียนโปรแกรมเช่น: Java, C #, Object Oriented PHP และอื่น ๆ


19
C ++ เป็นจำนวนมากมากกว่า "C กับคลาส" ที่เริ่มต้นจาก
David Thornley

เหตุใดคุณจึงรวม C ++ และ C ไว้ในเครื่องหมายอัญประกาศ และ "ซ่อน", "อ้างอิง" และทุกอย่างอื่นหรือไม่ คุณเป็น "พนักงานขาย" และไม่ได้เข้าร่วมใน "โปรแกรม" หรือไม่?
phresnel

ฉันควรทำให้พวกเขาเป็นตัวหนา คำพูดสามารถใช้เพื่อเน้น แต่ยังตรงข้าม
umlcat

-6

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

ตัวอย่าง:

class MyClass1 {
    public:
        void A (void);
        void B (void);
        void C (void);
    private:
        DWORD dwclass;
};

class MyClass2 {
    public:
        int C (int i);
};

void MyClass1::A (void) {
    MyClass2 *myclass= new MyClass2;
    dwclass=(DWORD)myclass;
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    int i = myclass->C(0); // or int i=((MyClass2 *)dwclass)->C(0);
}

void MyClass1::B (void) {
    MyClass2 *myclass= (MyClass2 *)dwclass;
    delete myclass;
}

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

//-------------------------------------
class CSRecordSet : public CSObject
//-------------------------------------
{
public:
    CSRecordSet();
    virtual ~CSRecordSet();
    // Constructor
    bool Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef);
    //Open, find, close
    int OpenRst(bool bReadBlanks=0,bool bCheckLastSql=0,bool bForceLoad=0,bool bMessage=1);     // for a given SQL
    int FindRecord(bool bNext);         // for a given SQL
    // TABLE must be ordered by the same fields that will be seek
    bool SeekRecord(int nFieldIndex, char *key, int length=0);  // CRT bsearch
    bool SeekRecord(int nFieldIndex, long key);     
    bool SeekRecord(int nFieldIndex, double key, int decimals);     
    bool SeekRecord(XSEK *SEK);     
    bool DeleteRecord(void);
    bool Close(void);
    // Record Position:
    bool IsEOF(void);           // pointer out of bound
    bool IsLAST(void);          // TRUE if last record
    bool IsBOF(void);           // pointer out of bound
    bool IsOpen(void);
    bool Move(long lRows);      // returns FALSE if out of bound
    void MoveNextNotEof(void);  // eof is tested
    void MoveNext(void);        // eof is not tested
    void MovePrev(void);        // bof is tested
    void MoveLast(void);
    void MoveFirst(void);
    void SetAbsolutePosition(long lRows);
    long GetAbsolutePosition(void);
    void GoToLast(void); // Restore position after a Filter
    // Table info
    long GetRecordCount(void);
    int GetRstTableNumber(void);
    int GetRecordLength(void); //includes stamp (sizeof char)
    int GetTableType(void);
    // Field info
    int GetFieldCount(void);
    void GetFieldName(int nFieldIndex, char *pbuffer);
    int GetFieldIndex(const char *sFieldName);
    int GetFieldSize(int nFieldIndex);
    int GetFieldDGSize(int nFieldIndex); // String size (i.e. dg_Boolean)
    long GetRecordID(void);
    int GetStandardFieldCount(void);
    bool IsMemoFileTable(void);
    bool IsNumberField(int nFieldIndex);
    int GetFieldType(int nFieldIndex);
    // Read Field value
    bool GetFieldValue(int nFieldIndex, XdbVar& var);
    bool GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer);
    char *GetMemoField(int nMemoFieldIndex, char *pbuffer, int buf_size);
    bool GetBinaryField(unsigned char *buffer,long *buf_size);
    // Write Field value
    void Edit(void); // required
    bool SetFieldValue(int nFieldIndex, XdbVar& var);
    bool SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer);
    bool Update(void); // required
    // pointer to the same lpSql
    LPXSQL GetSQL(void);
};

//---------------------------------------------------
CSRecordSet::CSRecordSet(){
//---------------------------------------------------
    pClass |= (CS_bAttach);
}
CSRecordSet::~CSRecordSet(){
    if(pObject) delete (CXdbRecordset*)pObject;
}
bool CSRecordSet::Create(CSDataBase* pDataBase,CSQueryDef* pQueryDef){
    CXdbQueryDef *qr=(CXdbQueryDef*)pQueryDef->GetObject();
    CXdbTables *db=(CXdbTables*)pDataBase->GetObject();
    CXdbRecordset *rst = new CXdbRecordset(db,qr);
    if(rst==NULL) return 0;
    pObject = (unsigned long) rst;
    return 1;
}
bool CSRecordSet::Close(void){
    return ((CXdbRecordset*)pObject)->Close();
}
int CSRecordSet::OpenRst(bool bReadBlanks,bool bCheckLastSql,bool bForceLoad, bool bMessage){
    unsigned long dw=0L;
    if(bReadBlanks) dw|=SQL_bReadBlanks;
    if(bCheckLastSql) dw|=SQL_bCheckLastSql;
    if(bMessage) dw|=SQL_bRstMessage;
    if(bForceLoad) dw|=SQL_bForceLoad;

    return ((CXdbRecordset*)pObject)->OpenEx(dw);
}
int CSRecordSet::FindRecord(bool bNext){
    return ((CXdbRecordset*)pObject)->FindRecordEx(bNext);
}
bool CSRecordSet::DeleteRecord(void){
    return ((CXdbRecordset*)pObject)->DeleteEx();
}
bool CSRecordSet::IsEOF(void){
    return ((CXdbRecordset*)pObject)->IsEOF();
}
bool CSRecordSet::IsLAST(void){
    return ((CXdbRecordset*)pObject)->IsLAST();
}
bool CSRecordSet::IsBOF(void){
    return ((CXdbRecordset*)pObject)->IsBOF();
}
bool CSRecordSet::IsOpen(void){
    return ((CXdbRecordset*)pObject)->IsOpen();
}
bool CSRecordSet::Move(long lRows){
    return ((CXdbRecordset*)pObject)->MoveEx(lRows);
}
void CSRecordSet::MoveNextNotEof(void){
    ((CXdbRecordset*)pObject)->MoveNextNotEof();
}
void CSRecordSet::MoveNext(void){
    ((CXdbRecordset*)pObject)->MoveNext();
}
void CSRecordSet::MovePrev(void){
    ((CXdbRecordset*)pObject)->MovePrev();
}
void CSRecordSet::MoveLast(void){
    ((CXdbRecordset*)pObject)->MoveLast();
}
void CSRecordSet::MoveFirst(void){
    ((CXdbRecordset*)pObject)->MoveFirst();
}
void CSRecordSet::SetAbsolutePosition(long lRows){
    ((CXdbRecordset*)pObject)->SetAbsolutePosition(lRows);
}
long CSRecordSet::GetAbsolutePosition(void){
    return ((CXdbRecordset*)pObject)->m_AbsolutePosition;
}
long CSRecordSet::GetRecordCount(void){
    return ((CXdbRecordset*)pObject)->GetRecordCount();
}
int CSRecordSet::GetFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetFieldCount();
}
int CSRecordSet::GetRstTableNumber(void){
    return ((CXdbRecordset*)pObject)->GetRstTableNumber();
}
void CSRecordSet::GetFieldName(int nFieldIndex, char *pbuffer){
    ((CXdbRecordset*)pObject)->GetFieldName(nFieldIndex,pbuffer);
}
int CSRecordSet::GetFieldIndex(const char *sFieldName){
    return ((CXdbRecordset*)pObject)->GetFieldIndex(sFieldName);
}
bool CSRecordSet::IsMemoFileTable(void){
    return ((CXdbRecordset*)pObject)->IsMemoFileTable();
}
bool CSRecordSet::IsNumberField(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->IsNumberField(nFieldIndex);
}
bool CSRecordSet::GetFieldValueIntoBuffer(int nFieldIndex,char *pbuffer){
    return ((CXdbRecordset*)pObject)->GetFieldValueIntoBuffer(nFieldIndex,pbuffer);
}
void CSRecordSet::Edit(void){
    ((CXdbRecordset*)pObject)->Edit();
}
bool CSRecordSet::Update(void){
    return ((CXdbRecordset*)pObject)->Update();
}
bool CSRecordSet::SetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->SetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SetFieldValueFromBuffer(int nFieldIndex,const char *pbuffer){
    return ((CXdbRecordset*)pObject)->SetFieldValueFromBuffer(nFieldIndex,pbuffer);
}
bool CSRecordSet::GetFieldValue(int nFieldIndex, XdbVar& var){
    return ((CXdbRecordset*)pObject)->GetFieldValue(nFieldIndex,var);
}
bool CSRecordSet::SeekRecord(XSEK *SEK){
    return ((CXdbRecordset*)pObject)->TableSeek(SEK);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,char *key, int length){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,key,length);
}
bool CSRecordSet::SeekRecord(int nFieldIndex,long i){
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,i);
}
bool CSRecordSet::SeekRecord(int nFieldIndex, double d, int decimals)
{
    return ((CXdbRecordset*)pObject)->TableSeek(nFieldIndex,d,decimals);
}
int CSRecordSet::GetRecordLength(void){
    return ((CXdbRecordset*)pObject)->GetRecordLength();
}
char *CSRecordSet::GetMemoField(int nMemoFieldIndex,char *pbuffer, int BUFFER_SIZE){
    return ((CXdbRecordset*)pObject)->GetMemoField(nMemoFieldIndex,pbuffer,BUFFER_SIZE);
}
bool CSRecordSet::GetBinaryField(unsigned char *buffer,long *buf_size){
    return ((CXdbRecordset*)pObject)->GetBinaryField(buffer,buf_size);
}
LPXSQL CSRecordSet::GetSQL(void){
    return ((CXdbRecordset*)pObject)->GetSQL();
}
void CSRecordSet::GoToLast(void){
    ((CXdbRecordset*)pObject)->GoToLast();
}
long CSRecordSet::GetRecordID(void){
    return ((CXdbRecordset*)pObject)->GetRecordID();
}
int CSRecordSet::GetStandardFieldCount(void){
    return ((CXdbRecordset*)pObject)->GetStandardFieldCount();
}
int CSRecordSet::GetTableType(void){
    return ((CXdbRecordset*)pObject)->GetTableType();
}
int CSRecordSet::GetFieldType(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldType(nFieldIndex);
}
int CSRecordSet::GetFieldDGSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldDGSize(nFieldIndex);
}
int CSRecordSet::GetFieldSize(int nFieldIndex){
    return ((CXdbRecordset*)pObject)->GetFieldSize(nFieldIndex);
}

แก้ไข: ร้องขอโดย DeadMG:

void nimportequoidumomentquecaroule(void) {

    short i = -4;
    unsigned short j=(unsigned short)i;

}

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

1
ผู้ส่งที่DWORDไม่เหมาะสมและอาจไม่ถูกต้อง (DWORD ไม่จำเป็นต้องกว้างพอที่จะถือตัวชี้) หากคุณต้องการตัวชี้ที่ไม่มีการพิมพ์ให้ใช้void*- แต่เมื่อคุณพบว่าตัวเองต้องการสิ่งนั้นใน C ++ คุณมักจะมีปัญหาการออกแบบในรหัสของคุณคุณควรแก้ไข
Mat

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

@ แมทตัวอย่างนั้นใช้สำหรับระบบปฏิบัติการ 32 บิตและคอมไพเลอร์ VC6 ++
Salvador

6
หากพูดถึงโค้ดที่ไม่ดีในคอมไพเลอร์โบราณอย่างแน่นอน ไม่เป็นไรขอบคุณ.
DeadMG
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.