Câu hỏi Con trỏ thông minh là gì và khi nào tôi nên sử dụng con trỏ?


Con trỏ thông minh là gì và khi nào tôi nên sử dụng con trỏ?


1429
2017-09-20 00:09


gốc


Xem câu hỏi này: <br> Con trỏ thông minh: Hoặc ai sở hữu con bạn - Martin York
Lưu ý rằng việc thực hiện std :: auto_ptr trong Visual Studio 2005 là khủng khiếp bị hỏng. <br>http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=98871<br> http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101842 Thay vào đó, hãy sử dụng các tăng cường. - Richard
Hai bài viết tuyệt vời về chủ đề: - Con trỏ thông minh - Cái gì, Tại sao, cái nào? - - Vị Thầy của Tuần lễ # 25 - Lazer
Đây là chương của Alexandrescu (miễn phí) trên gritty nitty của việc tạo ra con trỏ thông minh của các hương vị khác nhau: informit.com/articles/article.aspx?p=31529  Trong triển khai của mình, anh sử dụng đối số mẫu làm "chính sách" để chỉ định thuộc tính nào anh muốn (ví dụ: tính tham chiếu), trong khi thư viện chuẩn sử dụng các lớp riêng biệt. Lưu ý rằng ông cũng đã viết trước khi tham khảo rvalue đã có sẵn để làm cho một cái gì đó như std :: unique_ptr có thể. - metal


Các câu trả lời:


Một con trỏ thông minh là một lớp bao bọc một con trỏ C ++ (hoặc 'trần'), để quản lý tuổi thọ của đối tượng đang được trỏ đến. Không có loại con trỏ thông minh nào, nhưng tất cả chúng đều cố gắng trừu tượng một con trỏ thô theo cách thực tế.

Con trỏ thông minh nên được ưu tiên hơn con trỏ thô. Nếu bạn cảm thấy bạn cần phải sử dụng con trỏ (đầu tiên hãy xem xét nếu bạn có thật không do), bạn thường sẽ muốn sử dụng một con trỏ thông minh vì điều này có thể làm giảm bớt nhiều vấn đề với con trỏ thô, chủ yếu là quên để xóa đối tượng và rò rỉ bộ nhớ.

Với con trỏ thô, lập trình viên phải phá hủy một cách rõ ràng đối tượng khi nó không còn hữu dụng nữa.

// 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...?

Một con trỏ thông minh bằng cách so sánh xác định một chính sách như khi đối tượng bị phá hủy. Bạn vẫn phải tạo đối tượng, nhưng bạn không còn phải lo lắng về việc phá hủy nó nữa.

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

Chính sách đơn giản nhất được sử dụng liên quan đến phạm vi của đối tượng trình bao bọc con trỏ thông minh, chẳng hạn như được triển khai bởi boost::scoped_ptr hoặc là std::unique_ptr.

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

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

Lưu ý rằng scoped_ptr không thể sao chép các bản sao. Điều này ngăn không cho con trỏ bị xóa nhiều lần (không chính xác). Tuy nhiên, bạn có thể chuyển các tham chiếu đến nó xung quanh các chức năng khác mà bạn gọi.

Con trỏ có phạm vi là hữu ích khi bạn muốn kết nối vòng đời của đối tượng với một khối mã cụ thể hoặc nếu bạn nhúng nó làm dữ liệu thành viên bên trong một đối tượng khác, thời gian tồn tại của đối tượng khác đó. Đối tượng tồn tại cho đến khi khối chứa mã được thoát, hoặc cho đến khi đối tượng chứa chính nó bị hủy.

Một chính sách con trỏ thông minh phức tạp hơn bao gồm việc tham chiếu đếm con trỏ. Điều này cho phép con trỏ được sao chép. Khi "tham chiếu" cuối cùng đối tượng bị hủy, đối tượng sẽ bị xóa. Chính sách này được thực hiện bởi boost::shared_ptr và std::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.

Con trỏ được tính tham chiếu rất hữu ích khi thời gian tồn tại của đối tượng của bạn phức tạp hơn nhiều và không được gắn trực tiếp với một phần mã cụ thể hoặc đối tượng khác.

Có một nhược điểm đối với con trỏ được tính tham chiếu - khả năng tạo tham chiếu lơ lửng:

// 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!

Một khả năng khác là tạo tham chiếu vòng tròn:

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

boost::shared_ptr<Owner> p1 (new Owner());
boost::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!

Để giải quyết vấn đề này, cả Boost và C ++ 11 đã định nghĩa weak_ptr để xác định tham chiếu yếu (chưa được tính) cho shared_ptr.


CẬP NHẬT

Câu trả lời này khá cũ, và do đó mô tả những gì là 'tốt' vào thời điểm đó, đó là con trỏ thông minh được cung cấp bởi thư viện Boost. Kể từ C ++ 11, thư viện chuẩn đã cung cấp đủ các loại con trỏ thông minh và vì vậy bạn nên ưu tiên sử dụng std::unique_ptr, std::shared_ptr và std::weak_ptr.

Cũng có std::auto_ptr. Nó giống như một con trỏ có phạm vi, ngoại trừ việc nó cũng có khả năng nguy hiểm "đặc biệt" được sao chép - điều này cũng bất ngờ chuyển quyền sở hữu! Nó không được chấp nhận trong các tiêu chuẩn mới nhất, vì vậy bạn không nên sử dụng nó. Sử dụng std::unique_ptr thay thế.

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.

1641
2017-09-20 00:48



Ý bạn là std::auto_ptr<MyObject> p1 (new MyObject()); thay vì std::auto_ptr<MyObject> p1 (new Owner());? - Mateen Ulhaq
Câu trả lời tuyệt vời. Nó sẽ được tốt đẹp nếu nó đã được cập nhật cho c + + 11. Tôi tìm thấy câu trả lời này tìm kiếm thông tin về tiêu chuẩn 11 mới và sẽ rất tuyệt nếu các khách truy cập trong tương lai có thể tìm thấy thông tin cập nhật. Tôi biết auto_ptr đã không được chấp nhận. Tôi tin rằng shated_ptr và weak_ptr tồn tại như được mô tả, và tôi nghĩ scoped_ptr bây giờ là unique_ptr trong tiêu chuẩn. Nếu điều này là đúng, câu trả lời này có thể được cập nhật không? - SaulBack
Để nói rằng khả năng tạo ra một tham chiếu lơ lửng là một nhược điểm đối với các con trỏ được tính tham chiếu là hoàn toàn điên rồ. Các tham chiếu có thể lúng túng có thể là một nhược điểm của bất kỳ con trỏ C ++ nào. Trong thực tế, nó là chính xác nhược điểm đó con trỏ thông minh nào được dự định làm giảm bớt. - anthropomorphic
Nếu bạn khai báo một con trỏ đến một con trỏ thông minh (như đã được thực hiện trong ví dụ), bạn cố ý từ bỏ tất cả các lợi ích của con trỏ thông minh. Đây không phải là một nhược điểm hoặc một lỗ hổng thiết kế, nó là cách sử dụng ngu ngốc nhất có thể tưởng tượng được. - anthropomorphic
Câu trả lời hay. Nó sẽ được tốt đẹp nếu nó đã được cập nhật cho C + + 14. Hey, whaddaya biết, ai đó đã đưa ra nhận xét tương tự về C ++ 11, cách đây hơn ba năm ... :-( - einpoklum


Dưới đây là một câu trả lời đơn giản cho những ngày này của C ++ hiện đại:

  • Con trỏ thông minh là gì? 
    Đó là một loại giá trị có thể được sử dụng như một con trỏ, nhưng cung cấp tính năng bổ sung của quản lý bộ nhớ tự động: Khi con trỏ không còn sử dụng, bộ nhớ nó trỏ đến được deallocated (xem thêm định nghĩa chi tiết hơn trên Wikipedia).
  • Khi nào tôi nên sử dụng? 
    Trong mã liên quan đến việc theo dõi quyền sở hữu một phần bộ nhớ, phân bổ hoặc phân bổ; con trỏ thông minh thường tiết kiệm cho bạn sự cần thiết phải làm những điều này một cách rõ ràng.
  • Nhưng con trỏ thông minh nào tôi nên sử dụng trong trường hợp đó?
    • Sử dụng std::unique_ptr khi bạn không có ý định giữ nhiều tham chiếu đến cùng một đối tượng. Ví dụ, sử dụng nó cho một con trỏ đến bộ nhớ được cấp phát khi nhập một số phạm vi và được phân bổ khi thoát khỏi phạm vi.
    • Sử dụng std::shared_ptr khi bạn muốn tham chiếu đến đối tượng của mình từ nhiều nơi - và không muốn nó bị phân bổ cho đến khi tất cả các tham chiếu này tự biến mất.
    • Sử dụng std::weak_ptr khi bạn muốn tham chiếu đến đối tượng của mình từ nhiều nơi - cho những tham chiếu mà bạn có thể bỏ qua và deallocate (vì vậy chúng sẽ chỉ chú ý đối tượng đã biến mất khi bạn cố gắng dereference).
    • Không sử dụng boost:: con trỏ thông minh hoặc std::auto_ptr ngoại trừ trong những trường hợp đặc biệt mà bạn có thể đọc nếu bạn phải.
  • Này, tôi không hỏi cái nào nên dùng! 
    Ah, nhưng bạn thực sự muốn, thừa nhận nó.
  • Vậy khi nào tôi nên sử dụng con trỏ thông thường? 
    Chủ yếu là trong mã đó là không biết đến quyền sở hữu bộ nhớ. Điều này thường sẽ được trong các chức năng mà có được một con trỏ từ một nơi nào khác và không phân bổ, de-cấp phát hoặc lưu trữ một bản sao của con trỏ mà outlasts thực hiện của họ.

170
2018-05-09 19:06



Cần lưu ý rằng trong khi các con trỏ thông minh (sở hữu) giúp quản lý bộ nhớ thích hợp, các con trỏ thô (không sở hữu) vẫn hữu ích cho các mục đích tổ chức khác trong cấu trúc dữ liệu. Herb Sutter đã trình bày tuyệt vời về vấn đề này tại CppCon 2016, bạn có thể xem trên YouTube: Rò rỉ-Tự do trong C ++ ... Theo mặc định. - wiktor.wandachowicz
Câu trả lời dễ hiểu đầu tiên thực sự đơn giản mà tôi tìm thấy trên web! Cảm ơn! - Bernd


Con trỏ thông minh là một loại giống như con trỏ với một số chức năng bổ sung, ví dụ: bộ nhớ tự động deallocation, tham khảo đếm vv

Giới thiệu nhỏ có sẵn trên trang Con trỏ thông minh - Cái gì, Tại sao, cái nào?.

Một trong những loại con trỏ thông minh đơn giản là std::auto_ptr (chương 20.4.5 của tiêu chuẩn C ++), cho phép xử lý tự động bộ nhớ khi nó nằm ngoài phạm vi và mạnh hơn việc sử dụng con trỏ đơn giản khi các ngoại lệ được ném ra, mặc dù ít linh hoạt hơn.

Một loại tiện lợi khác là boost::shared_ptr mà thực hiện đếm tham chiếu và tự động deallocates bộ nhớ khi không có tham chiếu đến đối tượng vẫn còn. Điều này giúp tránh rò rỉ bộ nhớ và dễ sử dụng để triển khai RAII.

Chủ đề được đề cập sâu trong sách "Mẫu C ++: Hướng dẫn đầy đủ" của David Vandevoorde, Nicolai M. Josuttis, chương Chương 20. Con trỏ thông minh. Một số chủ đề được đề cập:

  • Bảo vệ chống ngoại lệ
  • Chủ sở hữu, (lưu ý, std :: auto_ptr đang triển khai loại con trỏ thông minh như vậy)
  • Mua lại tài nguyên là khởi tạo (Điều này thường được sử dụng để quản lý tài nguyên ngoại lệ an toàn trong C ++)
  • Giới hạn chủ
  • Tham khảo Đếm
  • Truy cập đồng thời
  • Phá hủy và thỏa thuận

97
2017-09-20 00:32



1 là câu trả lời duy nhất trong danh sách này (trong số 18 câu trả lời hiện hoạt) mà thậm chí còn đề cập đến RAII. - WhozCraig


Định nghĩa được cung cấp bởi Chris, Sergdev và Llyod là chính xác. Tôi thích một định nghĩa đơn giản hơn, chỉ để giữ cho cuộc sống của tôi đơn giản: Một con trỏ thông minh đơn giản là một lớp quá tải ->* toán tử. Điều đó có nghĩa là đối tượng của bạn ngữ nghĩa trông giống như một con trỏ nhưng bạn có thể làm cho nó làm những thứ mát mẻ hơn, bao gồm cả tính tham chiếu, hủy diệt tự động, v.v. shared_ptr và auto_ptr là đủ trong hầu hết các trường hợp, nhưng đi kèm với bộ riêng của họ về idiosyncrasies nhỏ.


33
2017-09-20 01:53





Một con trỏ thông minh giống như một con trỏ thông thường (được gõ), giống như "char *", ngoại trừ khi con trỏ chính nó đi ra khỏi phạm vi thì những gì nó trỏ đến cũng bị xóa. Bạn có thể sử dụng nó như bạn sẽ là một con trỏ thông thường, bằng cách sử dụng "->", nhưng không phải nếu bạn cần một con trỏ thực tế cho dữ liệu. Đối với điều đó, bạn có thể sử dụng "& * ptr".

Nó rất hữu ích cho:

  • Các đối tượng phải được cấp phát mới, nhưng bạn muốn có cùng tuổi thọ như một thứ gì đó trên ngăn xếp đó. Nếu đối tượng được gán cho một con trỏ thông minh, thì chúng sẽ bị xóa khi chương trình thoát khỏi hàm / khối đó.

  • Các thành viên dữ liệu của các lớp, để khi đối tượng bị xóa, tất cả dữ liệu thuộc sở hữu cũng bị xóa, mà không có bất kỳ mã đặc biệt nào trong trình phá hủy (bạn sẽ cần phải chắc chắn rằng destructor là ảo, gần như luôn luôn là một điều tốt để làm) .

Bạn có thể không phải muốn sử dụng con trỏ thông minh khi:

  • ... con trỏ không nên sở hữu dữ liệu ... tức là khi bạn chỉ sử dụng dữ liệu, nhưng bạn muốn nó tồn tại chức năng mà bạn đang tham chiếu đến nó.
  • ... con trỏ thông minh không phải là chính nó sẽ bị phá hủy tại một số điểm. Bạn không muốn nó nằm trong bộ nhớ mà không bao giờ bị phá hủy (chẳng hạn như trong một đối tượng được phân bổ động nhưng sẽ không bị xóa một cách rõ ràng).
  • ... hai con trỏ thông minh có thể trỏ đến cùng một dữ liệu. (Có, tuy nhiên, thậm chí con trỏ thông minh hơn sẽ xử lý điều đó ... được gọi là đếm tham chiếu.)

Xem thêm:


26
2017-09-20 00:13





Hầu hết các loại con trỏ thông minh đều xử lý việc xử lý đối tượng trỏ đến đối tượng của bạn. Nó rất tiện dụng bởi vì bạn không phải suy nghĩ về việc xử lý các đối tượng thủ công nữa.

Các con trỏ thông minh được sử dụng phổ biến nhất là std::tr1::shared_ptr (hoặc là boost::shared_ptr) và, ít phổ biến hơn, std::auto_ptr. Tôi khuyên bạn nên sử dụng thường xuyên shared_ptr.

shared_ptr rất linh hoạt và xử lý nhiều kịch bản xử lý khác nhau, bao gồm các trường hợp đối tượng cần phải được "vượt qua ranh giới DLL" (trường hợp cơn ác mộng phổ biến nếu khác nhau) libcs được sử dụng giữa mã của bạn và các tệp DLL).


14
2017-09-20 00:14





Một con trỏ thông minh là một đối tượng hoạt động giống như một con trỏ, nhưng cung cấp thêm quyền kiểm soát về xây dựng, hủy diệt, sao chép, di chuyển và dereferencing.

Người ta có thể thực hiện một con trỏ thông minh của riêng mình, nhưng nhiều thư viện cũng cung cấp triển khai con trỏ thông minh với mỗi ưu điểm và nhược điểm khác nhau.

Ví dụ, Tăng cường cung cấp các triển khai con trỏ thông minh sau đây:

  • shared_ptr<T> là một con trỏ đến T sử dụng số tham chiếu để xác định khi nào đối tượng không còn cần thiết nữa.
  • scoped_ptr<T> là một con trỏ tự động bị xóa khi nó nằm ngoài phạm vi. Không thể chuyển nhượng được.
  • intrusive_ptr<T>là một con trỏ đếm tham chiếu khác. Nó cung cấp hiệu suất tốt hơn shared_ptr, nhưng yêu cầu loại T để cung cấp cơ chế đếm tham chiếu của riêng mình.
  • weak_ptr<T> là một con trỏ yếu, làm việc cùng với shared_ptr để tránh tham chiếu vòng tròn.
  • shared_array<T> giống như shared_ptr, nhưng đối với mảng T.
  • scoped_array<T> giống như scoped_ptr, nhưng đối với mảng T.

Đây chỉ là một mô tả tuyến tính của mỗi và có thể được sử dụng theo nhu cầu, để biết thêm chi tiết và ví dụ người ta có thể xem tài liệu của Boost.

Ngoài ra, thư viện chuẩn C ++ cung cấp ba con trỏ thông minh; std::unique_ptr cho quyền sở hữu duy nhất, std::shared_ptr cho quyền sở hữu được chia sẻ và std::weak_ptr. std::auto_ptr tồn tại trong C ++ 03 nhưng hiện không được chấp nhận.


14
2018-03-12 09:51



Vui lòng giải thích lý do scoped_ptr không giống như tuyên bố cục bộ const unique_ptr - cũng bị xóa khi thoát khỏi phạm vi. - einpoklum


Đây là Liên kết cho các câu trả lời tương tự: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Một con trỏ thông minh là một đối tượng hoạt động, trông và cảm thấy giống như một con trỏ bình thường nhưng cung cấp nhiều chức năng hơn. Trong C ++, các con trỏ thông minh được thực hiện như các lớp mẫu đóng gói một con trỏ và ghi đè các toán tử con trỏ chuẩn. Họ có một số lợi thế so với con trỏ thông thường. Chúng được bảo đảm được khởi tạo như một con trỏ null hoặc con trỏ trỏ tới một đối tượng heap. Indirection thông qua một con trỏ null được chọn. Không xóa là bao giờ cần thiết. Các đối tượng được tự động giải phóng khi con trỏ cuối cùng đến chúng đã biến mất. Một vấn đề quan trọng với những con trỏ thông minh này là không giống như con trỏ thông thường, chúng không tôn trọng sự thừa kế. Con trỏ thông minh không hấp dẫn đối với mã đa hình. Đưa ra dưới đây là một ví dụ cho việc thực hiện các con trỏ thông minh.

Thí dụ: 

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:
               //...
};

Lớp này thực hiện một con trỏ thông minh đến một đối tượng kiểu X. Đối tượng chính nó nằm trên heap. Đây là cách sử dụng nó:

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

Giống như các toán tử bị quá tải khác, p sẽ hoạt động giống như một con trỏ thông thường,

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

9
2018-03-07 09:03