Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Kết quả 1 đến 3 của 3
  1. #1
    Ngày tham gia
    Sep 2015
    Bài viết
    0

    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[IMG]images/smilies/Cry.gif[/IMG] .

    À, spam tý, đang bức xúc [IMG]images/smilies/dont_know.gif[/IMG] .

    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:


    Mã:
    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.


    Mã:
    #ifndef _SGF_PTR_H_#define _SGF_PTR_H_ /// \brief A smart pointer for sgfObject/// \see sgfObjecttemplate<class T>class sgfPtr{public:        sgfPtr():ptr(0)        {        }        sgfPtr(const T* obj)                :ptr(0)        {                assign(obj);        }        sgfPtr(const sgfPtr<T>& obj)                :ptr(0)        {                assign(obj.ptr);        }        ~sgfPtr()        {                if(ptr)                        ptr->decRef();        }         sgfPtr& operator=(T* obj)        {                assign(obj);                return *this;        }         sgfPtr& operator=(const sgfPtr<T>& obj)        {                assign(obj.ptr);                return *this;        }         inline friend bool operator==(const T* other, const sgfPtr<T>& me)        {                return(me.ptr==other);        }         inline friend bool operator==(const sgfPtr<T>& me, const sgfPtr<T>& other)        {                return(me.ptr==other.ptr);        }         inline friend bool operator!=(const T* other, const sgfPtr<T>& me)        {                return(me.ptr!=other);        }         inline friend bool operator!=(const sgfPtr<T>& me, const sgfPtr<T>& other)        {                return(me.ptr!=other.ptr);        }         /// \brief Return the real pointer        /// 
    emarks This should only be used for null checking        T* getPtr() const        {                return ptr;        }         T* operator->() const        {                return ptr;        }         T& operator*() const        {                  return *ptr;        }         operator T* () const        {                  return ptr;        }               ///\brief Perform static_cast on the object        template<class T2>        T2* staticCast() const        {                return static_cast<T2>(this);        }               /// \brief Perform dynamic_cast on the object        template<class T2>        T2* dynamicCast() const        {                return dynamic_cast<T2>(this);        } private:        void assign(const T* obj)        {                T* o=const_cast<T*>(obj);                if(o)                        o->addRef();                if(ptr)                        ptr->decRef();                ptr=o;        }        T* ptr;}; #endif
    Và bạn đồng hành, một abstract object có khả năng tự xử.

    Mã:
    class sgfObject{public:        sgfObject():refCounter(0)        {        }protected:        virtual ~sgfObject()        {        }public:        /// \brief add reference        void addRef()         {                ++refCounter;        }         /// \brief remove reference        /// and delete the object if reference ==0        void decRef()        {                --refCounter;                if(!refCounter)                {                        delete this;                }        } private:        int refCounter;};
    Thử với một ví dụ nhỏ.


    Mã:
    class Test;public:    TestClass()    {        cout << "Hahaha! ta day
    ";    }    virtual ~TestClass()    {        cout << "Chet toi roi
    ";    }}; class Class1; class Class2{private:    TestClass* ptr;public:    Class2(TestClass* ptr, Class1* sender)    {                this->ptr = ptr;        cout << "Class 2 da duoc tao
    ";    }public:    ~Class2()    {        cout << "Class 2 da ngom
    ";    }}; class Class1{private:    TestClass* ptr;public:    Class1()    {        cout << "Class 1 da duoc tao
    ";        ptr = new TestClass();        Class2* c2ptr = new Class2(ptr, this);        if(c2ptr)            delete c2ptr;    }public:    ~Class1()    {        cout << "Class 1 da ngom
    ";    }}; int main(){    Class1* ptr = new Class1();    if(ptr)        delete ptr;    cin.get();    return 0;}
    [IMG]images/smilies/Surprised.gif[/IMG]

    Đừng thắc mắc tại sao lại lằng nhằng thế [IMG]images/smilies/waiting.gif[/IMG] , 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 [IMG]images/smilies/21.gif[/IMG] ) và một mớ object ôm lẫn nhau thì thế là còn ít chán![IMG]images/smilies/Cry.gif[/IMG] .

    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.


    Mã:
    class Class2{private:    sgfPtr<TestClass> ptr;// <-----------------------Chú ýpublic:    Class2(TestClass* ptr, Class1* sender)    {        this->ptr = ptr;        cout << "Class 2 da duoc tao
    ";    }public:    ~Class2()    {        cout << "Class 2 da ngom
    ";    }}; class Class1{private:    sgfPtr<TestClass> ptr;// <-----------------------Chú ýpublic:    Class1()    {        cout << "Class 1 da duoc tao
    ";        ptr = new TestClass();        Class2* c2ptr = new Class2(ptr, this);        if(c2ptr)            delete c2ptr;    }public:    ~Class1()    {        cout << "Class 1 da ngom
    ";    }};
    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 [IMG]images/smilies/Cry.gif[/IMG] .

    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ử [IMG]images/smilies/wave.gif[/IMG] .

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Cám ơn bạn NamVoDang ! Bài viết rất có giá trị. Mình cũng đang cần.

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    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.

    Mã:
    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.

 

 

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •