1. Một số vấn đề với con trỏ thông thường trong C/C++
a. Memory Leaks:
Điều này xảy ra khi bộ nhớ được cấp phát nhiều lần bỏi một chương trình nhưng không bao giờ được giải phóng. Điều này dẫn đến việc tiêu thụ bộ nhớ quá mức cuối cùng dẫn đến sự cố hệ thống VD:
#include <iostream>
using namespace std; class Rectangle {
private: int length; int breadth;
}; void fun() { Rectangle* p = new Rectangle();
} int main() { while(1) { fun(); //ham fun tao 1 con tro Rectangle }
}
b. Dangling Pointer:
Là con trỏ trỏ đến dữ liệu không hợp lệ hoặc dữ liệu không hợp lệ nữa (dữ liệu đã từng hợp lệ) VD:
Class *object = new Class();
Class *object2 = object; delete object;
object = nullptr;
//Dễ thấy sau khi giải phóng cho obj thì obj2 bây h sẽ trỏ đến thứ gì đó không còn hợp lệ nữa
Điều này có thể xảy ra ngay cả đối với các đối tượng trong vùng nhớ Stack (Memory Layout):
Object *method() { Object object; return &object;
} Object *object2 = method();
//Có thể thấy obj2 chính là 1 dangling pointer vì obj2 trỏ đến obj nhưng khi kết thúc hàm thì nó sẽ bị xóa khỏi Stack
c. Wild Pointer:
Con trỏ chưa được khởi tạo được gọi là con trỏ hoang dã vì chúng trỏ đến một số vị trí bộ nhớ tùy ý và có thể khiến chương trình bị lỗi hoặc hoạt động không mong muốn VD:
// C program that demonstrated wild pointers
int main()
{ /* wild pointer */ int* p; /* Some unknown memory location is being corrupted. This should never be done. */ *p = 12;
}
d. Data Inconsistency:
Dữ liệu khộng nhất quán xảy ra khi một số dữ liệu được lưu trữ trong bộ nhớ nhưng không được cập nhật một cách nhất quán
e. Buffer Overflow
Tràn bộ đệm được sử dụng để ghi dữ liệu vào một địa chỉ bộ nhớ nằm ngoài khối bộ nhớ được cấp phát. Điều này dẫn đến việc dữ liệu bị hỏng có thể bị khai thác bỏi những kẻ tấn công nguy hiểm
2. SMART POINTER
- Con trỏ thông minh là 1 trình bao bọc trên một con trỏ có toán tử như * và ->
- Các đối tượng của con trỏ thông minh trông giống như con trỏ bình thường, và nó có thể tự giải phóng và giải phóng bộ nhớ của đối tượng bị hủy
a. Các loại Smart Pointer
- auto_ptr
- unique_ptr
- shared_ptr
- weak_ptr
Cách tạo 1 Smart Pointer mà không cần thư viện mẫu
#include <iostream>
using namespace std;
class SmartPtr { int *ptr;
public: explicit SmartPtr(int * p = NULL) {ptr = p;} //yêu cầu dùng hàm khởi tọa này //Destructor ~SmartPtr() {delete (ptr);} //Overloading dereferencing operator int& operator*() {return *ptr;}
}; int main()
{ SmartPtr ptr(new int()); //SmartPtr ptr; *ptr = 20; cout << *ptr; return 0;
}
Cách tạo 1 Smart Pointer cho mọi đối tượng (Dùng Template)
#include <iostream>
using namespace std; class Person {
public: void display() { cout << "Hello, I'm a person!" << endl; }
}; template <class T> class SmartPointer{ T* ptr;
public: SmartPointer(T* p = NULL) {ptr = p;} ~SmartPointer() {delete ptr;}; T& operator*() {return (*ptr);} T* operator->() {return ptr;}
}; int main() { SmartPointer<int> ptr(new int()); *ptr = 20; cout << *ptr << endl; SmartPointer<Person> p (new Person()); p->display(); return 0;
}
b. Unique_Pointer
- Chỉ lưu trữ 1 con trỏ
- Chúng ta có thể gán một đối tượng khác bằng cách xóa đối tượng hiện tại khỏi con trỏ
#include <iostream>
#include <memory>
using namespace std; class Rectangle { int length; int breadth;
public: Rectangle(int l, int b) { length = l; breadth = b; } int area() {return length * breadth;}
}; int main() { unique_ptr<Rectangle> P1(new Rectangle(10,5)); cout << P1->area() << endl; //unique_ptr<Rectangle> P2(P1); unique_ptr<Rectangle> P2; P2 = move(P1); cout << P2->area() << endl; return 0;
}
c. Shared Pointer
- Bằng cách sử dụng shared pointer nhiều con trỏ có thể trỏ đến một đối tượng tại một thời điểm
- Nó duy trì bộ đém tham chiếu bằng phương thức use_count().
#include <iostream>
#include <memory>
using namespace std; class Rectangle { int length; int breadth;
public: Rectangle(int l, int b) { length = l; breadth = b; } int area() { return length * breadth;}
}; int main() { shared_ptr<Rectangle> P1(new Rectangle(10,5)); cout << P1 -> area() << endl; shared_ptr<Rectangle> P2; P2 = P1; shared_ptr<Rectangle> P3; P3 = P1; cout << P2 -> area() << endl; cout << P1 -> area() << endl; cout << P1.use_count() << endl; cout << P2.use_count() << endl;
}
Trường hợp Memory Leak
#include <iostream>
#include <memory>
using namespace std; class Person {
public: std::shared_ptr<Person> friendPtr; std::string name; Person(const std::string& n) : name(n) { cout << "Created! Use count: " << friendPtr.use_count() << std::endl; } ~Person() { cout << name << "destroyed!" << endl; }
}; int main() { shared_ptr<Person> person1(new Person("Toan")); shared_ptr<Person> person2(new Person("Quynh")); person1->friendPtr = person2; person2->friendPtr = person1; cout << "After creating objects: " << endl; cout << "Alice use count:" << person1.use_count() << endl; cout << "Bob use count:" << person2.use_count() << endl; std::cout << "Address of Alice's friend: " << person1->friendPtr.get() << std::endl; std::cout << "Address of Bob's friend: " << person2->friendPtr.get() << std::endl; return 0;
}
d. Weak_Pointer (Update sau)