ตัวชี้อัจฉริยะคืออะไรและควรใช้เมื่อใด


1819

ตัวชี้อัจฉริยะคืออะไรและควรใช้เมื่อใด



2
โปรดทราบว่าการใช้ std :: auto_ptr ใน Visual Studio 2005 นั้นใช้งานไม่ได้ <br> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=98871 <br> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101842ใช้ เพิ่มวัตถุแทน
ริชาร์ด

25
สองบทความที่ยอดเยี่ยมเกี่ยวกับเรื่อง: - ตัวชี้สมาร์ท - อะไรทำไมซึ่ง? - Guru of the Week # 25
Lazer

1
นี่คือบท (ฟรี) ของ Alexandrescu เกี่ยวกับความกล้าหาญในการสร้างพอยน์เตอร์อัจฉริยะที่มีรสชาติที่แตกต่าง: informit.com/articles/article.aspx?p=31529 ในการดำเนินการของเขาเขาใช้อาร์กิวเมนต์แม่แบบเป็น "นโยบาย" เพื่อระบุคุณลักษณะที่เขาต้องการ ( เช่นการนับการอ้างอิง) ในขณะที่ไลบรารีมาตรฐานใช้คลาสแยกต่างหาก โปรดทราบว่าเขายังเขียนก่อนที่จะมีการอ้างอิงค่า rvalue เพื่อทำให้บางอย่างเช่น std :: unique_ptr เป็นไปได้
โลหะ

ฉันต้องการเพิ่มอีกหนึ่งจุดไปที่คำถามข้างต้นตัวชี้สมาร์ท std :: shared_ptr ไม่มีตัวดำเนินการตัวห้อยและไม่สนับสนุนเลขคณิต ponter เราสามารถใช้ get () เพื่อให้ได้ตัวชี้ในตัว
suresh m

คำตอบ:


1883

UPDATE

คำตอบนี้ค่อนข้างเก่าและอธิบายสิ่งที่ 'ดี' ในเวลานั้นซึ่งเป็นตัวชี้สมาร์ทที่ได้รับจากห้องสมุด Boost ตั้งแต่ C ++ 11, ไลบรารีมาตรฐานได้จัดเตรียมพอยน์เตอร์พอยน์เตอร์ให้เพียงพอ, ดังนั้นคุณควรใช้และstd::unique_ptr, .std::shared_ptrstd::weak_ptr

std::auto_ptrนอกจากนี้ยังมี มันเป็นเหมือนตัวชี้ที่กำหนดขอบเขตยกเว้นว่ามันมีความสามารถที่เป็นอันตราย "พิเศษ" ที่จะคัดลอกซึ่งยังโอนความเป็นเจ้าของโดยไม่คาดคิด
มันเลิกใช้แล้วใน C ++ 11 และนำออกใน C ++ 17ดังนั้นคุณไม่ควรใช้

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

คำตอบเดิม ๆ

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

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

ด้วยตัวชี้แบบดิบโปรแกรมเมอร์ต้องทำลายวัตถุอย่างชัดเจนเมื่อไม่มีประโยชน์อีกต่อไป

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

ตัวชี้สมาร์ทโดยการเปรียบเทียบกำหนดนโยบายว่าเมื่อวัตถุถูกทำลาย คุณยังต้องสร้างวัตถุ แต่คุณไม่ต้องกังวลกับการทำลายมันอีกต่อไป

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

นโยบายที่ง่ายในการใช้งานที่เกี่ยวข้องกับขอบเขตของวัตถุชี้เสื้อคลุมสมาร์ทเช่นดำเนินการโดยหรือboost::scoped_ptrstd::unique_ptr

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

โปรดทราบว่าstd::unique_ptrไม่สามารถคัดลอกอินสแตนซ์ได้ สิ่งนี้ป้องกันไม่ให้ตัวชี้ถูกลบหลายครั้ง (ไม่ถูกต้อง) อย่างไรก็ตามคุณสามารถส่งต่อการอ้างอิงไปยังฟังก์ชันอื่นที่คุณโทรได้

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

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

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

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

มีหนึ่งอุปสรรคในการอ้างอิงพอยน์เตอร์นับ - ความเป็นไปได้ของการสร้างการอ้างอิงห้อย:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

ความเป็นไปได้อีกอย่างก็คือการสร้างการอ้างอิงแบบวงกลม:

struct Owner {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

เมื่อต้องการแก้ไขปัญหานี้ทั้ง Boost และ C ++ 11 ได้กำหนดweak_ptrที่จะกำหนดอ่อนแอ (ไม่ได้นับ) shared_ptrอ้างอิงถึง


7
คุณหมายถึงstd::auto_ptr<MyObject> p1 (new MyObject());แทนstd::auto_ptr<MyObject> p1 (new Owner());?
Mateen Ulhaq

35
คำตอบที่ยอดเยี่ยม มันจะดีถ้ามันได้รับการปรับปรุงสำหรับ c ++ 11 ฉันพบคำตอบนี้เพื่อหาข้อมูลเกี่ยวกับมาตรฐานใหม่ 11 รายการและจะดีถ้าผู้เข้าชมในอนาคตสามารถค้นหาข้อมูลที่อัปเดตได้ ฉันรู้ว่า auto_ptr เลิกใช้แล้ว ฉันเชื่อว่า shated_ptr และ weak_ptr มีอยู่ตามที่อธิบายไว้และฉันคิดว่า scoped_ptr ตอนนี้เป็น unique_ptr ในมาตรฐาน หากเป็นจริงคำตอบนี้จะได้รับการอัปเดตไหม
SaulBack

16
การบอกว่าความเป็นไปได้ของการสร้างการอ้างอิงที่ห้อยต่องแต่งนั้นเป็นข้อเสียเปรียบสำหรับการอ้างอิงพอยน์เตอร์นับเป็นบ้าอย่างแน่นอน อ้างอิงห้อยเป็นไปได้คืออุปสรรคของการใด ๆ c ++ ชี้ ในความเป็นจริงมันเป็นตรงที่เสียเปรียบซึ่งชี้สมาร์ทมีวัตถุประสงค์เพื่อบรรเทา
Michael Dorst

16
หากคุณประกาศตัวชี้ไปยังตัวชี้สมาร์ท (เช่นเดียวกับที่ทำในตัวอย่าง) คุณจงละทิ้งประโยชน์ทั้งหมดของตัวชี้สมาร์ท นี่ไม่ใช่ข้อเสียเปรียบหรือข้อบกพร่องในการออกแบบ แต่เป็นการใช้งานที่งี่เง่าที่สุดเท่าที่จะเป็นไปได้
Michael Dorst

3
A const std::auto_ptrปลอดภัยที่จะใช้ถ้าคุณติดอยู่กับ C ++ 03 ฉันใช้มันสำหรับรูปแบบ pimpl ค่อนข้างมากจนกระทั่งฉันได้เข้าถึง C ++ 11
Toby Speight

303

นี่คือคำตอบง่ายๆสำหรับยุคปัจจุบันของ C ++ (C ++ 11 และใหม่กว่า):

  • ตัวชี้สมาร์ทคืออะไร?
    เป็นประเภทที่มีค่าสามารถนำมาใช้เช่นตัวชี้ แต่ซึ่งมีคุณลักษณะเพิ่มเติมของการจัดการหน่วยความจำอัตโนมัติ: เมื่อตัวชี้สมาร์ทไม่ได้อยู่ในการใช้งานหน่วยความจำจะชี้ไปที่จะ deallocated (ดูคำนิยามรายละเอียดเพิ่มเติมเกี่ยวกับวิกิพีเดีย )
  • เมื่อใดที่ฉันควรใช้
    ในรหัสที่เกี่ยวข้องกับการติดตามความเป็นเจ้าของชิ้นส่วนของหน่วยความจำจัดสรรหรือยกเลิกการจัดสรร ตัวชี้สมาร์ทมักจะช่วยให้คุณไม่ต้องทำสิ่งเหล่านี้อย่างชัดเจน
  • แต่สมาร์ทพ้อยท์ใดที่ฉันควรใช้ในกรณีเหล่านี้
    • ใช้std::unique_ptrเมื่อคุณไม่ต้องการถือการอ้างอิงหลายรายการกับวัตถุเดียวกัน ตัวอย่างเช่นใช้สำหรับตัวชี้ไปยังหน่วยความจำซึ่งได้รับการจัดสรรในการเข้าสู่ขอบเขตและยกเลิกการจัดสรรเมื่อออกจากขอบเขต
    • ใช้std::shared_ptrเมื่อคุณต้องการอ้างถึงวัตถุของคุณจากหลาย ๆ ที่ - และไม่ต้องการให้วัตถุของคุณถูกจัดสรรคืนจนกว่าการอ้างอิงทั้งหมดเหล่านี้จะหายไปเอง
    • ใช้std::weak_ptrเมื่อคุณต้องการอ้างถึงวัตถุของคุณจากหลาย ๆ ที่ - สำหรับการอ้างอิงที่ไม่สนใจและยกเลิกการจัดสรร (ดังนั้นพวกเขาจะเพิ่งทราบว่าวัตถุนั้นหายไปเมื่อคุณพยายามอ้างถึง)
    • อย่าใช้boost::ตัวชี้อัจฉริยะหรือstd::auto_ptrยกเว้นในกรณีพิเศษที่คุณสามารถอ่านได้หากคุณต้องการ
  • เฮ้ฉันไม่ได้ถามว่าจะใช้อันไหน!
    อ่า แต่คุณต้องการยอมรับมันจริงๆ
  • ดังนั้นเมื่อไรฉันจึงควรใช้พอยน์เตอร์ปกติ?
    ส่วนใหญ่ในรหัสที่ลืมความเป็นเจ้าของหน่วยความจำ โดยทั่วไปแล้วจะอยู่ในฟังก์ชันที่รับตัวชี้จากที่อื่นและไม่จัดสรรหรือไม่จัดสรรและไม่เก็บสำเนาของตัวชี้ที่อยู่ได้นานกว่าการดำเนินการ

5
เป็นที่น่าสังเกตว่าในขณะที่ตัวชี้สมาร์ท (เป็นเจ้าของ) ช่วยในการจัดการหน่วยความจำที่เหมาะสมตัวชี้แบบ raw (ไม่ใช่เจ้าของ) ยังคงมีประโยชน์สำหรับวัตถุประสงค์อื่น ๆ ขององค์กรในโครงสร้างข้อมูล Herb Sutter นำเสนอที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้ใน CppCon 2016 ที่คุณสามารถเห็นบน YouTube: Leak-Freedom ใน C ++ ... โดยค่าเริ่มต้น
wiktor.wandachowicz

1
@ wiktor.wandachowicz T*คือstd::unique_ptr<T>สิ่งที่std::weak_ptr<T>จะเป็นstd::shared_ptr<T>
Caleth

@Caleth: ไม่ฉันจะไม่พูดอย่างนั้น
einpoklum

1
@TonyTannous: ด้วยความเคารพ - เป็นการแก้ไขครั้งใหญ่ และฉันไม่รู้สึกคำตอบของฉันซึ่งเป็นนามธรรมต้องการมัน ฉันขอแนะนำให้คุณทำตัวอย่างเป็นคำตอบที่แยกต่างหากเชื่อมโยงไปยังความคิดเห็น
einpoklum

112

ตัวชี้สมาร์ทเป็นชนิดที่คล้ายกับตัวชี้พร้อมฟังก์ชันเพิ่มเติมบางอย่างเช่นการจัดสรรคืนหน่วยความจำอัตโนมัติการนับการอ้างอิงเป็นต้น

อินโทรขนาดเล็กมีอยู่ในหน้าสมาร์ทพอยน์เตอร์ - อะไร, ทำไม, ซึ่ง? .

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

อีกประเภทที่สะดวกคือboost::shared_ptrใช้การนับการอ้างอิงและยกเลิกการจัดสรรหน่วยความจำโดยอัตโนมัติเมื่อไม่มีการอ้างอิงไปยังวัตถุ นี้จะช่วยให้หลีกเลี่ยงการรั่วไหลของหน่วยความจำและเป็นเรื่องง่ายที่จะใช้ในการดำเนินการRAII

หัวเรื่องถูกครอบคลุมในเชิงลึกในหนังสือ"เทมเพลต C ++: คู่มือฉบับสมบูรณ์" โดย David Vandevoorde, Nicolai M. Josuttis , บทที่ 20 บทที่สมาร์ทพอยน์เตอร์ บางหัวข้อครอบคลุม:


2
คำเตือนstd::auto_ptrถูกเลิกใช้และไม่สนับสนุนอย่างมากเนื่องจากคุณสามารถถ่ายโอนความเป็นเจ้าของโดยไม่ได้ตั้งใจ - C ++ 11 เอาความต้องการของ Boost, การใช้งาน: std::unique_ptr, std::shared_ptrและstd::weak_ptr
ninMonkey

42

คำจำกัดความที่จัดทำโดย Chris, Sergdev และ Llyod นั้นถูกต้อง ฉันชอบคำจำกัดความที่ง่ายกว่า แต่เพื่อให้ชีวิตของฉันง่ายขึ้น: ตัวชี้ที่ชาญฉลาดเป็นเพียงคลาสที่เกินพิกัด-> และ*ตัวดำเนินการ ซึ่งหมายความว่าวัตถุของคุณมีความหมายคล้ายกับตัวชี้ แต่คุณสามารถทำให้สิ่งต่าง ๆ น่าสนใจรวมถึงการนับการอ้างอิงการทำลายอัตโนมัติเป็นต้น shared_ptrและauto_ptrเพียงพอในกรณีส่วนใหญ่ แต่มาพร้อมกับชุดนิสัยแปลก ๆ


30

ตัวชี้สมาร์ทนั้นเหมือนตัวชี้ปกติ (พิมพ์) เช่น "char *" ยกเว้นเมื่อตัวชี้นั้นออกนอกขอบเขตสิ่งที่ชี้ไปจะถูกลบเช่นกัน คุณสามารถใช้งานได้เหมือนตัวชี้ปกติโดยใช้ "->" แต่ไม่จำเป็นถ้าคุณต้องการตัวชี้ที่แท้จริงไปยังข้อมูล เพื่อที่คุณสามารถใช้ "& * ptr"

มันมีประโยชน์สำหรับ:

  • วัตถุที่ต้องจัดสรรใหม่ แต่คุณต้องการมีอายุการใช้งานเท่ากับของบางอย่างในสแต็กนั้น หากวัตถุถูกกำหนดให้กับตัวชี้สมาร์ทแล้วพวกเขาจะถูกลบเมื่อโปรแกรมออกจากฟังก์ชั่น / บล็อก

  • ข้อมูลสมาชิกของคลาสดังนั้นเมื่อวัตถุถูกลบข้อมูลที่เป็นเจ้าของทั้งหมดจะถูกลบเช่นกันโดยไม่มีรหัสพิเศษใด ๆ ใน destructor (คุณจะต้องแน่ใจว่า destructor นั้นเสมือนจริงซึ่งเป็นสิ่งที่ดีที่จะทำ) .

คุณอาจไม่ต้องการใช้ตัวชี้สมาร์ทเมื่อ:

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

ดูสิ่งนี้ด้วย:


18

พอยน์เตอร์สมาร์ทชนิดต่าง ๆ ส่วนใหญ่จัดการการกำจัดของตัวชี้ไปยังวัตถุสำหรับคุณ. มีประโยชน์มากเพราะคุณไม่ต้องคิดเกี่ยวกับการกำจัดวัตถุด้วยตนเองอีกต่อไป

ส่วนใหญ่ที่ใช้กันทั่วไปสมาร์ทชี้std::tr1::shared_ptr(หรือboost::shared_ptr) std::auto_ptrและน้อยกว่าปกติ shared_ptrผมขอแนะนำให้ใช้งานปกติของ

shared_ptrมีความหลากหลายมากและจัดการกับสถานการณ์การกำจัดที่หลากหลายรวมถึงกรณีที่วัตถุต้อง "ผ่านข้ามขอบเขต DLL" (กรณีฝันร้ายทั่วไปหากlibcมีการใช้ s ที่แตกต่างกันระหว่างรหัสและ DLL ของคุณ)


18

ตัวชี้สมาร์ทเป็นวัตถุที่ทำหน้าที่เหมือนตัวชี้ แต่ยังให้การควบคุมในการก่อสร้างการทำลายการคัดลอกการย้ายและการยกเลิกการอ้างอิง

หนึ่งสามารถใช้ตัวชี้สมาร์ทของตัวเอง แต่ห้องสมุดหลายแห่งยังให้การใช้งานตัวชี้สมาร์ทแต่ละคนมีข้อดีและข้อเสียที่แตกต่างกัน

ตัวอย่างเช่นBoostให้การใช้งานตัวชี้สมาร์ทต่อไปนี้:

  • shared_ptr<T>เป็นตัวชี้การTใช้การนับการอ้างอิงเพื่อกำหนดว่าเมื่อใดที่วัตถุไม่ต้องการอีกต่อไป
  • scoped_ptr<T>เป็นตัวชี้ที่ถูกลบโดยอัตโนมัติเมื่อออกนอกขอบเขต ไม่สามารถกำหนดได้
  • intrusive_ptr<T>เป็นอีกหนึ่งตัวชี้การนับการอ้างอิง มันให้ประสิทธิภาพที่ดีกว่าshared_ptrแต่ต้องการประเภทTเพื่อให้กลไกการนับการอ้างอิงของตัวเอง
  • weak_ptr<T>เป็นตัวชี้ที่อ่อนแอทำงานร่วมกับshared_ptrเพื่อหลีกเลี่ยงการอ้างอิงแบบวงกลม
  • shared_array<T>เป็นเหมือนแต่สำหรับอาร์เรย์shared_ptrT
  • scoped_array<T>เป็นเหมือนแต่สำหรับอาร์เรย์scoped_ptrT

เหล่านี้เป็นเพียงคำอธิบายเชิงเส้นของแต่ละคำและสามารถใช้ตามต้องการได้สำหรับรายละเอียดเพิ่มเติมและตัวอย่างหนึ่งสามารถดูเอกสารของ Boost

นอกจากนี้ไลบรารีมาตรฐาน C ++ ยังมีตัวชี้อัจฉริยะสามตัว std::unique_ptrสำหรับเจ้าของที่ไม่ซ้ำกันสำหรับเจ้าของร่วมกันและstd::shared_ptr มีอยู่ใน C ++ 03 แต่ขณะนี้เลิกใช้แล้วstd::weak_ptrstd::auto_ptr


โปรดอธิบายว่าทำไมscoped_ptrไม่เหมือนประกาศในพื้นที่const unique_ptr- ซึ่งจะถูกลบออกเมื่อออกจากขอบเขต
einpoklum

11

นี่คือลิงค์สำหรับคำตอบที่คล้ายกัน: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

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

ตัวอย่าง:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

คลาสนี้ใช้ตัวชี้สมาร์ทกับวัตถุประเภท X วัตถุนั้นอยู่ในฮีป นี่คือวิธีการใช้งาน:

smart_pointer <employee> p= employee("Harris",1333);

เช่นเดียวกับตัวดำเนินการโอเวอร์โหลดอื่น ๆ p จะทำงานเหมือนตัวชี้ปกติ

cout<<*p;
p->raise_salary(0.5);

9

http://en.wikipedia.org/wiki/Smart_pointer

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


6

ให้ T เป็นคลาสในตัวชี้การสอนนี้ใน C ++ สามารถแบ่งออกเป็น 3 ประเภท:

1) ตัวชี้ดิบ :

T a;  
T * _ptr = &a; 

พวกเขาเก็บที่อยู่หน่วยความจำไปยังตำแหน่งในหน่วยความจำ ใช้ด้วยความระมัดระวังเนื่องจากโปรแกรมซับซ้อนยากต่อการติดตาม

ตัวชี้ที่มีข้อมูล const หรือที่อยู่ {อ่านย้อนหลัง}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

ชี้ไปยังชนิดข้อมูล T ซึ่งเป็นค่าคงที่ หมายความว่าคุณไม่สามารถเปลี่ยนชนิดข้อมูลโดยใช้ตัวชี้ เช่น*ptr1 = 19; จะไม่ทำงาน. แต่คุณสามารถย้ายตัวชี้ เช่นptr1++ , ptr1--; ฯลฯ จะทำงาน อ่านย้อนหลัง: ตัวชี้เพื่อพิมพ์ T ซึ่งเป็น const

  T * const ptr2 ;

ตัวชี้ const ไปยังชนิดข้อมูล T หมายความว่าคุณไม่สามารถย้ายตัวชี้ได้ แต่คุณสามารถเปลี่ยนค่าที่ตัวชี้ชี้ไปได้ เช่น*ptr2 = 19จะทำงานได้ แต่ptr2++ ; ptr2--จะไม่ทำงาน อ่านย้อนหลัง: ตัวชี้ const ไปยังประเภท T

const T * const ptr3 ; 

ตัวชี้ const ไปยัง const ข้อมูลชนิด T หมายความว่าคุณไม่สามารถย้ายตัวชี้ไม่สามารถเปลี่ยนตัวชี้ชนิดข้อมูลเป็นตัวชี้ได้ เช่น ptr3-- ; ptr3++ ; *ptr3 = 19;จะไม่ทำงาน

3) ตัวชี้อัจฉริยะ : { #include <memory>}

ตัวชี้ที่ใช้ร่วมกัน :

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

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

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

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

ดู: std :: weak_ptr มีประโยชน์เมื่อใด?

Unique Pointer: ตัวชี้อัจฉริยะที่มีน้ำหนักเบาพร้อมความเป็นเจ้าของ ใช้เมื่อตัวชี้ชี้ไปที่วัตถุที่ไม่ซ้ำกันโดยไม่แบ่งปันวัตถุระหว่างตัวชี้

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

ในการเปลี่ยนวัตถุที่ชี้ไปตาม PTR ที่ไม่ซ้ำกันให้ใช้ซีแมนทิกส์ของการย้าย

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

การอ้างอิง: โดยพื้นฐานแล้วพวกมันอาจจะเป็นตัวชี้ const เช่นตัวชี้ซึ่งเป็น const และไม่สามารถย้ายด้วยไวยากรณ์ที่ดีกว่า

ดู: อะไรคือความแตกต่างระหว่างตัวแปรตัวชี้และตัวแปรอ้างอิงใน C ++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

การอ้างอิง: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ ขอบคุณ Andre สำหรับการชี้ให้เห็นคำถามนี้


3

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

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


2

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

คุณสามารถใช้ตัวชี้เหล่านี้ได้ในลักษณะเดียวกันกับการจัดสรรใด ๆ ใน Java ในจาวา Garbage Collector ทำกลอุบายในขณะที่ใน Smart Pointers กลอุบายทำโดย Destructors


1

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

เหนือสิ่งอื่นใด (อธิบายได้ดีในคำตอบอื่น ๆ ) การใช้ตัวชี้สมาร์ทเป็นวิธีแก้ปัญหาที่เป็นไปได้เราจะใช้คลาสนามธรรมเป็นชนิดส่งคืนฟังก์ชันได้อย่างไร ซึ่งทำเครื่องหมายว่าซ้ำกับคำถามนี้ อย่างไรก็ตามคำถามแรกที่ถามว่าอยากให้ระบุคลาสพื้นฐาน (หรืออันใดอันหนึ่ง) ในรูปแบบ return ใน C ++ คือ "คุณหมายถึงอะไรจริง ๆ " มีการสนทนาที่ดี (พร้อมการอ้างอิงเพิ่มเติม) ของการเขียนโปรแกรมเชิงวัตถุเชิงสำนวนใน C ++ (และวิธีนี้แตกต่างกับภาษาอื่น ๆ ) ในเอกสารประกอบของไลบรารีตัวชี้บูสเตอร์คอนเทนเนอร์. โดยสรุปใน C ++ คุณต้องคิดถึงความเป็นเจ้าของ พอยน์เตอร์อัจฉริยะตัวใดที่ช่วยคุณ แต่ไม่ใช่โซลูชันเดียวหรือเป็นโซลูชั่นที่สมบูรณ์เสมอ (ไม่ได้ให้สำเนาที่หลากหลาย) และไม่ได้เป็นโซลูชันที่คุณต้องการเปิดเผยในส่วนต่อประสานของคุณเสมอ (และฟังก์ชั่นคืนเสียงน่ากลัว มากเหมือนอินเทอร์เฟซ) มันอาจจะเพียงพอที่จะกลับมาอ้างอิงเช่น แต่ในทุกกรณีเหล่านี้ (ชี้สมาร์ทชี้ภาชนะหรือเพียงแค่กลับอ้างอิง) คุณมีการเปลี่ยนแปลงกลับมาจากการที่ค่ารูปแบบของบางอ้างอิง หากคุณต้องการคัดลอกคุณอาจต้องเพิ่ม "idiom" สำเร็จรูปมากขึ้นหรือย้ายเกิน OOP (หรือมิฉะนั้น) OOP ใน C ++ เพื่อ polymorphism ทั่วไปมากขึ้นโดยใช้ห้องสมุดเช่นAdobe PolyหรือBoost.TypeErasure.

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