Công cụ bảo vệ mã nguồn .NET mạnh nhất, không thể unpack, miễn phí cho các khách hàng đầu tiên đăng ký.
Từ 1 tới 3 trên tổng số 3 kết quả

Đề tài: SmartPointer trong lập trình VC++?

  1. #1
    Ngày gia nhập
    09 2008
    Nơi ở
    Kĩ viện
    Bài viết
    169

    Thumbs down SmartPointer trong lập trình VC++?

    Lạy hồn!.

    Đông vãi chưởng, hix, muốn vào post cái bài cũng khó khăn, mạng rớt lên rớt xuống thế này .

    À, spam tý, đang bức xúc .

    Ai đã từng dùng C++ để làm một cái gì đó trên mức nhỏ một tý chắc hẳn đều vấp phải cái vấn đề củ chuối muôn thủa. "Memory Leak".

    Trích lời cụ bull.
    Cần hiểu là diệt memory leak chỉ để tránh thiếu memory khi ct đang chạy(vì ko chịu free) + tránh tốn memory cho mấy ct khác thôi, chứ có malloc mà ko free thì ở cuối ct thì OS như Windows,Mac hay Linux nó đều free hộ. Chỉ có DOS lởm là ko làm đc như thế thôi.

    Biết mỗi 2 kiểu chính.
    -Reference counting: Mỗi object có một cái counter. Nó đếm số thằng đang sử dụng object đó. Counter của object đó về 0 là ko ai thèm nó nữa -> xóa.
    Việc ref countering này thường được làm bởi smart pointer(Tham khảo boost, SGF cũng đc). Hoặc làm = tay(irrlicht, COM) .
    Ưu điểm: -Nhanh, gọn nhẹ
    Nhược điểm: Clyclic reference. Object A có cái pointer vào object B. Object B lại có pointer vào Object A. Dễ thấy là 2 thằng này nó ôm nhau, cả 2 thằng sẽ ko bao giờ bị xóa vì chúng nó đều có ref counter=1 -> leak
    Khắc phục: Weak pointer, một cái pointer mà ko tăng reference. Tự reset về null khi object bị delete
    Tham khảo : Boost

    -Garbage collection. Đại loại là có một cái object gọi là garbage collector, nó sẽ scan và tìm xem object nào ko ai point đến mà ko dựa vào reference counting. Tớ biết mỗi kiểu mark-and-sweep:
    -Có một cái gọi là root.
    -Mọi object tạo ra đều đc lưu vào array
    -Mọi object ở mức global đều nối trực tiếp vào root
    -Mỗi object có thể có pointer đến các object khác, các object con của nó, các object con lại nối vào parent object
    --> Có một cái dạng như cái cây

    Increment GC thì chịu.

    Khi lượng memory sử dụng đã lớn, việc collect garbage bắt đầu.

    Chạy từ gốc(root) đến ngọn, và đánh dấu các object có đc reference, các object con của các object đc reference
    Quét qua cả cái array, tìm những thằng ko đánh dấu và diệt
    Bỏ đánh dấu toàn bộ các object

    Dễ thấy là cách này dẹp đc cyclic reference vì nếu A point vào B và B point vào A trong khi ko có ai khác point vào A lẫn B thì sẽ như hình sau:


    Code:
    Root - ob2 - obj6
      \
       ob1
         \
         ob3
          /    \
        ob4  ob5
    
    A-B
    Ko có Một liên hệ nào giữa root và A-B cả -> chúng nó ko bị đánh dấu và sẽ bị chém.

    Ưu điểm: Đơn giản giải quyết đc cyclic reference
    Nhược điểm:
    -Overhead nhiều hơn, nhất là khi đang scan
    -Trong các implementation của C++, syntax của chúng nó khá xấu
    -Template phức tạp hơn Smart pointer vì cần có 2 loại pointer khác nhau: Global/scope pointer và member pointer để tạo ra cấu trúc tree như trên
    -Ko thể tạo mấy object mà có sử dụng garbage collector trên stack

    Khắc phục:
    -Cố mà chịu, đây là C++
    -Sử dụng Managed C++ (Windows only, nhưng mà nếu ko ngại bắt người dùng cài .NET thì ko phải vấn đề lớn), syntax đẹp hơn nhiều
    Bài này chưa đề cập tới giải pháp hầm hố là garbage collector, chỉ xin phép giới thiệu tí về SmartPointer.

    Nó đây.

    C++ Code:
    1. #ifndef _SGF_PTR_H_
    2. #define _SGF_PTR_H_
    3.  
    4. /// \brief A smart pointer for sgfObject
    5. /// \see sgfObject
    6. template<class T>
    7. class sgfPtr
    8. {
    9. public:
    10.         sgfPtr():ptr(0)
    11.         {
    12.         }
    13.         sgfPtr(const T* obj)
    14.                 :ptr(0)
    15.         {
    16.                 assign(obj);
    17.         }
    18.         sgfPtr(const sgfPtr<T>& obj)
    19.                 :ptr(0)
    20.         {
    21.                 assign(obj.ptr);
    22.         }
    23.         ~sgfPtr()
    24.         {
    25.                 if(ptr)
    26.                         ptr->decRef();
    27.         }
    28.  
    29.         sgfPtr& operator=(T* obj)
    30.         {
    31.                 assign(obj);
    32.                 return *this;
    33.         }
    34.  
    35.         sgfPtr& operator=(const sgfPtr<T>& obj)
    36.         {
    37.                 assign(obj.ptr);
    38.                 return *this;
    39.         }
    40.  
    41.         inline friend bool operator==(const T* other, const sgfPtr<T>& me)
    42.         {
    43.                 return(me.ptr==other);
    44.         }
    45.  
    46.         inline friend bool operator==(const sgfPtr<T>& me, const sgfPtr<T>& other)
    47.         {
    48.                 return(me.ptr==other.ptr);
    49.         }
    50.  
    51.         inline friend bool operator!=(const T* other, const sgfPtr<T>& me)
    52.         {
    53.                 return(me.ptr!=other);
    54.         }
    55.  
    56.         inline friend bool operator!=(const sgfPtr<T>& me, const sgfPtr<T>& other)
    57.         {
    58.                 return(me.ptr!=other.ptr);
    59.         }
    60.  
    61.         /// \brief Return the real pointer
    62.         /// \remarks This should only be used for null checking
    63.         T* getPtr() const
    64.         {
    65.                 return ptr;
    66.         }
    67.  
    68.         T* operator->() const
    69.         {
    70.                 return ptr;
    71.         }
    72.  
    73.         T& operator*() const
    74.         {
    75.                   return *ptr;
    76.         }
    77.  
    78.         operator T* () const
    79.         {
    80.                   return ptr;
    81.         }
    82.        
    83.         ///\brief Perform static_cast on the object
    84.         template<class T2>
    85.         T2* staticCast() const
    86.         {
    87.                 return static_cast<T2>(this);
    88.         }
    89.        
    90.         /// \brief Perform dynamic_cast on the object
    91.         template<class T2>
    92.         T2* dynamicCast() const
    93.         {
    94.                 return dynamic_cast<T2>(this);
    95.         }
    96.  
    97. private:
    98.         void assign(const T* obj)
    99.         {
    100.                 T* o=const_cast<T*>(obj);
    101.                 if(o)
    102.                         o->addRef();
    103.                 if(ptr)
    104.                         ptr->decRef();
    105.                 ptr=o;
    106.         }
    107.         T* ptr;
    108. };
    109.  
    110. #endif

    Và bạn đồng hành, một abstract object có khả năng tự xử.
    C++ Code:
    1. class sgfObject
    2. {
    3. public:
    4.         sgfObject():refCounter(0)
    5.         {
    6.         }
    7. protected:
    8.         virtual ~sgfObject()
    9.         {
    10.         }
    11. public:
    12.         /// \brief add reference
    13.         void addRef()
    14.         {
    15.                 ++refCounter;
    16.         }
    17.  
    18.         /// \brief remove reference
    19.         /// and delete the object if reference ==0
    20.         void decRef()
    21.         {
    22.                 --refCounter;
    23.                 if(!refCounter)
    24.                 {
    25.                         delete this;
    26.                 }
    27.         }
    28.  
    29. private:
    30.         int refCounter;
    31. };

    Thử với một ví dụ nhỏ.

    C++ Code:
    1. class TestClass: public sgfObject
    2. {
    3. public:
    4.     TestClass()
    5.     {
    6.         cout << "Hahaha! ta day\n";
    7.     }
    8.     virtual ~TestClass()
    9.     {
    10.         cout << "Chet toi roi\n";
    11.     }
    12. };
    13.  
    14. class Class1;
    15.  
    16. class Class2
    17. {
    18. private:
    19.     TestClass* ptr;
    20. public:
    21.     Class2(TestClass* ptr, Class1* sender)
    22.     {
    23.                 this->ptr = ptr;
    24.         cout << "Class 2 da duoc tao\n";
    25.     }
    26. public:
    27.     ~Class2()
    28.     {
    29.         cout << "Class 2 da ngom\n";
    30.     }
    31. };
    32.  
    33. class Class1
    34. {
    35. private:
    36.     TestClass* ptr;
    37. public:
    38.     Class1()
    39.     {
    40.         cout << "Class 1 da duoc tao\n";
    41.         ptr = new TestClass();
    42.         Class2* c2ptr = new Class2(ptr, this);
    43.         if(c2ptr)
    44.             delete c2ptr;
    45.     }
    46. public:
    47.     ~Class1()
    48.     {
    49.         cout << "Class 1 da ngom\n";
    50.     }
    51. };
    52.  
    53. int main()
    54. {
    55.     Class1* ptr = new Class1();
    56.     if(ptr)
    57.         delete ptr;
    58.     cin.get();
    59.     return 0;
    60. }


    Đừng thắc mắc tại sao lại lằng nhằng thế , NVD mới chỉ mô phỏng lại một tình huống siêu đơn giản. So với một cái project thật sự với hàng chục người cùng tham gia, hàng trăm con trỏ(thậm chí hàng ngìn ) và một mớ object ôm lẫn nhau thì thế là còn ít chán! .

    Kết quả.

    Class1 da duoc tao
    Hahaha! ta day
    Class2 da duoc tao
    Class2 da ngom
    Class1 da ngom
    Dễ thấy. đoạn code trên leak đẹp. Cả hai class sử dụng tới object TestClass đều đã ngủm. Nhưng cái object đó vẫn còn lưu luyến trần gian->leak.

    Sửa lại đoạn code trên.

    C++ Code:
    1. class Class2
    2. {
    3. private:
    4.     sgfPtr<TestClass> ptr;// <-----------------------Chú ý
    5. public:
    6.     Class2(TestClass* ptr, Class1* sender)
    7.     {
    8.         this->ptr = ptr;
    9.         cout << "Class 2 da duoc tao\n";
    10.     }
    11. public:
    12.     ~Class2()
    13.     {
    14.         cout << "Class 2 da ngom\n";
    15.     }
    16. };
    17.  
    18. class Class1
    19. {
    20. private:
    21.     sgfPtr<TestClass> ptr;// <-----------------------Chú ý
    22. public:
    23.     Class1()
    24.     {
    25.         cout << "Class 1 da duoc tao\n";
    26.         ptr = new TestClass();
    27.         Class2* c2ptr = new Class2(ptr, this);
    28.         if(c2ptr)
    29.             delete c2ptr;
    30.     }
    31. public:
    32.     ~Class1()
    33.     {
    34.         cout << "Class 1 da ngom\n";
    35.     }
    36. };

    Kết quả.

    Class1 da duoc tao
    Hahaha! ta day
    Class2 da duoc tao
    Class2 da ngom
    Class1 da ngom
    Chet toi roi
    Phù!. thế chứ lại.

    Bạn có thể nói rằng:
    -ok, nhưng nếu tôi chỉ thêm một phát cái "delete TestClass;" vào destructor của class Class1 thì sao?.

    Ai jà, nge hấp dẫn quá nhỉ. Nhưng đừng vội mừng với sáng kiến của mình, giả dụ, vì một lý do gì đó. Mà Class2 truyền con trỏ "TestClass" lại cho một class3 hay 4 gì khác. Thế rồi ta delete cái Class1 đi -> cái TestClass cũng đi theo. Lúc đó thì sao?. Rõ ràng vẫn còn đối tượng muốn sử dụng cái TestClass. Mà tiếc thay cái TestClass đã ra người thiên cổ->lỗi ngoài ý muốn .

    Sử dụng smartpointer sẽ đảm bảo được việc delete đối tượng được cấp phát động đúng lúc. Không quá sớm mà cũng không quá muộn(sớm thì lỗi, muộn thì leak). Khi ta vẫn còn cần, nó vẫn còn phải sống. Khi chắc chắn ta không cần nữa nó nữa, nó tự xử .
    Công cụ bảo vệ mã nguồn .NET mạnh nhất hiện tại, miễn phí cho các khách hàng đầu tiên đăng ký.
    Đã được chỉnh sửa lần cuối bởi NamVoDang : 28-08-2009 lúc 03:05 PM.
    Phá toái hư không - Bạch nhật thăng thiên.

  2. #2
    Ngày gia nhập
    07 2008
    Nơi ở
    /media/Anime
    Bài viết
    2,288

    Cám ơn bạn NamVoDang ! Bài viết rất có giá trị. Mình cũng đang cần.
    Càng yêu mèo thì mèo càng mập. Mèo càng mập ta lại càng yêu.

  3. #3
    Ngày gia nhập
    09 2008
    Nơi ở
    Kĩ viện
    Bài viết
    169

    Hừm, đọc lại vẫn chưa rõ ràng gì cả :(. Thôi làm phát nữa.

    Cáh khai báo smartpointer.
    C++ Code:
    1. sgfPtr<TestClass> ptr;

    Nó là một đối tượng được cấp phát trên stack. Khi nó = một pointer nào đó thì nó cộng Counter của cái object được pointer đó trỏ tới lên một đơn vị thể hiện có thêm một thằng đang sử dụng cái object đấy. Khi cái smartpointer đó được giải phóng khỏi stack ngĩa là đã bớt đi một thằng muốn xài cái object đó
    -> Counter lại giảm đi một. Mà khi Counter về 0 có ngĩa là chả ma nào thèm nữa -> cái object tự sát.
    Công cụ bảo vệ mã nguồn .NET mạnh nhất hiện tại, miễn phí cho các khách hàng đầu tiên đăng ký.
    Phá toái hư không - Bạch nhật thăng thiên.

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn