shared_ptr ไปยังอาร์เรย์: ควรใช้หรือไม่


172

shared_ptrเพียงแค่การสอบถามเกี่ยวกับการขนาดเล็ก

เป็นวิธีปฏิบัติที่ดีที่จะใช้การshared_ptrชี้ไปที่อาร์เรย์หรือไม่ ตัวอย่างเช่น,

shared_ptr<int> sp(new int[10]);

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


2
FWIT std::vectorคุณยังอาจพิจารณาเพียงแค่ใช้ คุณจะต้องระมัดระวังในการส่งอาเรย์โดยใช้การอ้างอิงเพื่อที่คุณจะไม่ได้ทำสำเนา ไวยากรณ์สำหรับการเข้าถึงข้อมูลนั้นสะอาดกว่า shared_ptr และการปรับขนาดมันง่ายมาก และคุณจะได้รับความดี STL ทั้งหมดที่คุณต้องการ
Nicu Stiurca

6
std::arrayถ้าขนาดของอาร์เรย์จะถูกกำหนดที่รวบรวมเวลาคุณยังอาจพิจารณาใช้ มันเกือบจะเหมือนกันกับอาเรย์ดิบ แต่มีความหมายที่เหมาะสมสำหรับใช้ในส่วนประกอบห้องสมุดส่วนใหญ่ โดยเฉพาะอย่างยิ่งวัตถุประเภทนั้นจะถูกทำลายด้วยไม่ได้delete delete[]ซึ่งแตกต่างจากvectorมันเก็บข้อมูลโดยตรงในวัตถุดังนั้นคุณจะไม่ได้รับการจัดสรรเพิ่มเติม
celtschk

คำตอบ:


268

ด้วยภาษา C ++ 17 , shared_ptrสามารถนำมาใช้ในการจัดการอาร์เรย์จัดสรร shared_ptrอาร์กิวเมนต์แม่แบบในกรณีนี้จะต้องเป็นหรือT[N] T[]ดังนั้นคุณอาจเขียน

shared_ptr<int[]> sp(new int[10]);

จาก n4659, [util.smartptr.shared.const]

  template<class Y> explicit shared_ptr(Y* p);

ต้องการ: Yจะเป็นประเภทที่สมบูรณ์ การแสดงออกdelete[] pเมื่อTเป็นประเภทอาร์เรย์หรือdelete pเมื่อTไม่ได้เป็นประเภทอาร์เรย์จะต้องมีพฤติกรรมที่กำหนดไว้อย่างดีและจะไม่โยนข้อยกเว้น
...
ข้อสังเกต:เมื่อTเป็นประเภทอาร์เรย์คอนสตรัคนี้จะได้มีส่วนร่วมในการแก้ปัญหาเกินเว้นแต่การแสดงออกdelete[] pเป็นรูปแบบที่ดีและทั้งTเป็นU[N]และY(*)[N]เป็นแปลงสภาพให้แก่T*หรือTเป็น U[]และเป็นแปลงสภาพให้แก่Y(*)[] T*...

เพื่อรองรับสิ่งนี้ประเภทสมาชิกelement_typeจะถูกกำหนดเป็น

using element_type = remove_extent_t<T>;

องค์ประกอบอาร์เรย์สามารถเข้าถึงได้โดยใช้ operator[]

  element_type& operator[](ptrdiff_t i) const;

get() != 0 && i >= 0ต้องใช้: ถ้าTเป็นU[N], i < N. ...
ข้อสังเกต:เมื่อTไม่ได้เป็นประเภทอาเรย์มันจะไม่ได้ระบุว่าฟังก์ชั่นสมาชิกนี้จะมีการประกาศ ถ้ามันถูกประกาศมันจะไม่ระบุว่าประเภทการคืนของมันคืออะไรยกเว้นว่าการประกาศ (แม้ว่าไม่จำเป็นต้องนิยาม) ของฟังก์ชั่นจะต้องมีรูปแบบที่ดี


ก่อนที่จะมี C ++ 17 , shared_ptrอาจจะไม่ได้ถูกนำมาใช้ในการจัดการอาร์เรย์จัดสรร โดยค่าเริ่มต้นshared_ptrจะเรียกdeleteวัตถุที่มีการจัดการเมื่อไม่มีการอ้างอิงอีกต่อไป อย่างไรก็ตามเมื่อคุณจัดสรรโดยใช้new[]คุณต้องโทรdelete[]และไม่deleteให้เป็นอิสระจากทรัพยากร

เพื่อที่จะใช้shared_ptrกับอาร์เรย์ได้อย่างถูกต้องคุณต้องกำหนด deleter ที่กำหนดเอง

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

สร้าง shared_ptr ดังนี้:

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

ตอนนี้shared_ptrจะเรียกอย่างถูกต้องdelete[]เมื่อทำลายวัตถุที่มีการจัดการ

deleter ที่กำหนดเองด้านบนอาจถูกแทนที่ด้วย

  • std::default_deleteเชี่ยวชาญบางส่วนสำหรับประเภทอาร์เรย์

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
  • การแสดงออกแลมบ์ดา

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });

นอกจากนี้หากคุณต้องการใช้งานร่วมกันจริงกับวัตถุที่มีการจัดการ a unique_ptrเหมาะกว่าสำหรับงานนี้เนื่องจากมีความเชี่ยวชาญบางส่วนสำหรับประเภทอาร์เรย์

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]

การเปลี่ยนแปลงที่แนะนำโดย C ++ Extensions สำหรับ Library Fundamentals

pre-C ++ 17 ทางเลือกอื่น ๆ ที่ระบุไว้ข้างต้นได้รับการจัดทำโดยข้อกำหนดพื้นฐานทางเทคนิคของLibrary Fundamentalsซึ่งเพิ่มขึ้นshared_ptrเพื่อให้สามารถทำงานนอกกรอบสำหรับกรณีต่างๆเมื่อเป็นเจ้าของอาร์เรย์ของวัตถุ ร่างปัจจุบันของshared_ptrการเปลี่ยนแปลงที่กำหนดไว้สำหรับการ TS นี้สามารถพบได้ในN4082 การเปลี่ยนแปลงเหล่านี้จะสามารถเข้าถึงได้ผ่านstd::experimentalnamespace และรวมอยู่ใน<experimental/memory>ส่วนหัว การเปลี่ยนแปลงที่เกี่ยวข้องบางประการเพื่อสนับสนุนshared_ptrอาร์เรย์คือ:

- คำจำกัดความของการelement_typeเปลี่ยนแปลงประเภทสมาชิก

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

- operator[]กำลังเพิ่มสมาชิก

 element_type& operator[](ptrdiff_t i) const noexcept;

- ไม่เหมือนกับunique_ptrความเชี่ยวชาญเฉพาะบางส่วนสำหรับอาร์เรย์ทั้งสองshared_ptr<T[]>และshared_ptr<T[N]>จะใช้ได้และทั้งคู่จะส่งผลให้delete[]ถูกเรียกใช้บนอาร์เรย์ของวัตถุที่มีการจัดการ

 template<class Y> explicit shared_ptr(Y* p);

ต้องการ : Yจะเป็นประเภทที่สมบูรณ์ การแสดงออกdelete[] pเมื่อTเป็นประเภทอาร์เรย์หรือdelete pเมื่อTไม่ได้เป็นประเภทอาร์เรย์จะต้องมีรูปแบบที่ดีจะต้องมีพฤติกรรมที่กำหนดไว้อย่างดีและจะไม่โยนข้อยกเว้น เมื่อTเป็นU[N], Y(*)[N]จะต้องแปลงสภาพให้แก่T*; เมื่อTเป็นU[], Y(*)[]จะต้องแปลงสภาพให้แก่T*; มิฉะนั้นจะต้องแปลงสภาพให้แก่Y*T*


9
+1 หมายเหตุ: shared-arrayนอกจากนี้ยังมีการเพิ่มของ
jogojapan

5
@ tshah06 shared_ptr::getส่งคืนตัวชี้ไปยังวัตถุที่มีการจัดการ ดังนั้นคุณสามารถใช้มันเป็นsp.get()[0] = 1; ... sp.get()[9] = 10;
Praetorian

55
ALT: std::shared_ptr<int> sp( new int[10], std::default_delete<int[]>() );ดูen.cppreference.com/w/cpp/memory/default_delete ด้วย
yohjp

2
@ Jeremy ถ้าขนาดเป็นที่รู้จักกันในเวลารวบรวมไม่จำเป็นต้องเขียนชั้นเรียนที่std::shared_ptr<std::array<int,N>>ควรจะเพียงพอ
Praetorian

13
เหตุใดจึงunique_ptrมีความเชี่ยวชาญเฉพาะบางส่วน แต่shared_ptrไม่ได้ทำ
Adam

28

shared_ptr<vector<int>>ทางเลือกที่เป็นไปได้ง่ายขึ้นว่าคุณอาจจะสามารถใช้เป็น


5
ใช่แล้ว. หรือเวกเตอร์เป็นชุดของอาเรย์ - มันมีการแสดงในหน่วยความจำเดียวกัน (รวมถึงเมตาดาต้า) แต่ปรับขนาดได้ ไม่มีสถานการณ์ใด ๆ ที่คุณต้องการอาร์เรย์ แต่ไม่สามารถใช้เวกเตอร์ได้
Timmmm

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

4
shared_ptr<array<int, 6>>แล้วคุณอาจจะสามารถใช้
Timmmm

10
ความแตกต่างอื่น ๆ คือว่ามันเป็นเพียงเล็กน้อยที่ใหญ่กว่าและช้ากว่าอาร์เรย์ดิบ โดยทั่วไปไม่ใช่ปัญหาจริงๆ แต่อย่าทำเป็นว่า 1 == 1.1
แอนดรู

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