C++语言模板类对原生指针的封装与模拟

简介: C++|智能指针的智能性和指针性:模板类对原生指针的封装与模拟

Pointers are used for accessing the resources which are external to the program – like heap memory. So, for accessing the heap memory (if anything is created inside heap memory), pointers are used. When accessing any external resource we just use a copy of the resource. If we make any change to it, we just change it in the copied version. But, if we use a pointer to the resource, we’ll be able to change the original resource.

指针用于访问程序外部的资源,如堆内存。因此,为了访问堆内存(如果在堆内存中创建了任何内容),使用指针。当访问任何外部资源时,我们只使用该资源的一个副本。如果我们对其进行任何更改,我们只需在复制版本中进行更改。但是,如果我们使用指向资源的指针,我们将能够更改原始资源。

1 Problems with Normal Pointers
Take a look at the code below.

include

using namespace std;
class Rectangle {
private:
int length;
int breadth;
};
void fun()
{
// By taking a pointer p and
// dynamically creating object
// of class rectangle
Rectangle* p = new Rectangle();
}
int main()
{
// Infinite Loop
while(1) {
fun();
}
}//代码效果参考:http://www.zidongmutanji.com/bxxx/583062.html

In function fun, it creates a pointer that is pointing to the Rectangle object. The object Rectangle contains two integers, length and breadth. When the function fun ends, p will be destroyed as it is a local variable. But, the memory it consumed won’t be deallocated because we forgot to use delete p; at the end of the function. That means the memory won’t be free to be used by other resources. But, we don’t need the variable anymore, but we need the memory.

在fun函数中,它创建一个指向矩形对象的指针。对象矩形包含两个整数,长度和宽度。当函数fun结束时,p将被销毁,因为它是一个局部变量。但是,它消耗的内存不会被释放,因为我们忘记了使用delete p;在函数的末尾。这意味着内存将不能被其他资源使用。但是,我们不再需要这个变量,而是需要内存。

In function main, fun is called in an infinite loop. That means it’ll keep creating p. It’ll allocate more and more memory but won’t free them as we didn’t deallocate it. The memory that’s wasted can’t be used again. Which is a memory leak. The entire heap memory may become useless for this reason. C++11 comes up with a solution to this problem, Smart Pointer.

在函数main中,fun在无限循环中调用。这意味着它将继续创建p。它将分配越来越多的内存,但不会释放它们,因为我们没有释放它。浪费的内存不能再使用。这是内存泄漏。因此,整个堆内存可能会变得无用。C++11为这个问题提出了一个解决方案,即智能指针。

2 Introduction of Smart Pointers
As we’ve known unconsciously not deallocating a pointer causes a memory leak that may lead to crash of the program. Languages Java, C# has Garbage Collection Mechanisms to smartly deallocate unused memory to be used again. The programmer doesn’t have to worry about any memory leak. C++11 comes up with its own mechanism that’s Smart Pointer. When the object is destroyed it frees the memory as well. So, we don’t need to delete it as Smart Pointer does will handle it.

正如我们所知,不释放指针会导致内存泄漏,从而导致程序崩溃。在Java语言中,C#具有垃圾收集机制,可以巧妙地释放未使用的内存以供再次使用。程序员不必担心任何内存泄漏。C++11提出了自己的机制,即智能指针。当对象被销毁时,它也会释放内存。所以,我们不需要删除它(因为智能指针会处理它)。

A Smart Pointer is a wrapper class over a pointer with an operator like * and -> overloaded. The objects of the smart pointer class look like normal pointers. But, unlike Normal Pointers it can deallocate and free destroyed object memory.

智能指针是指针上的包装类,带有*和->重载运算符。智能指针类的对象看起来像普通指针。但是,与普通指针不同,它可以释放并释放被破坏的对象内存。

The idea is to take a class with a pointer, destructor and overloaded operators like * and ->. Since the destructor is automatically called when an object goes out of scope, the dynamically allocated memory would automatically be deleted (or reference count can be decremented). Consider the following simple SmartPtr class.

其思想是采用一个带有指针、析构函数和重载运算符(如*和->)的类。由于当对象超出作用域时会自动调用析构函数,因此动态分配的内存将自动删除(或者可以减少引用计数)。考虑以下简单的SmartPtr类。

include

using namespace std;
class SmartPtr {
int ptr; // Actual pointer
public:
// Constructor: Refer https:// www.geeksforgeeks.org/g-fact-93/
// for use of explicit keyword
explicit SmartPtr(int
p = NULL) { ptr = p; }
// Destructor
~SmartPtr() { delete (ptr); }
// Overloading dereferencing operator
int& operator() { return ptr; }
};
int main()
{
SmartPtr ptr(new int());
ptr = 20;
cout <<
ptr; // 20
// We don't need to call delete ptr: when the object
// ptr goes out of scope, the destructor for it is automatically
// called and destructor does delete ptr.
return 0;
}
This only works for int. So, we’ll have to create Smart Pointer for every object? No, there’s a solution, Template. In the code below as you can see T can be of any type. Read more about Template here.

这只适用于int。所以,我们必须为每个对象创建智能指针?不,有一个解决方案,模板。在下面的代码中,您可以看到T可以是任何类型。在此处阅读有关模板的更多信息。
//代码效果参考:http://www.zidongmutanji.com/bxxx/396322.html

include

using namespace std;
// A generic smart pointer class
template
class SmartPtr {
T ptr; // Actual pointer
public:
// Constructor
explicit SmartPtr(T
p = NULL) { ptr = p; }
// Destructor
~SmartPtr() { delete (ptr); }
// Overloading dereferencing operator
T& operator() { return ptr; }
// Overloading arrow operator so that
// members of T can be accessed
// like a pointer (useful if T represents
// a class or struct or union type)
T operator->() { return ptr; }
};
int main()
{
SmartPtr ptr(new int());
ptr = 20;
cout << *ptr; // 20
return 0;
}
Note: Smart pointers are also useful in the management of resources, such as file handles or network sockets.

注意:智能指针在资源管理中也很有用,例如文件句柄或网络套接字。

3 Types of Smart Pointers
3.1 unique_ptr

unique_ptr stores one pointer only. We can assign a different object by removing the current object from the pointer. Notice the code below. First, the unique_pointer is pointing to P1. But, then we remove P1 and assign P2 so the pointer now points to P2.

unique_ ptr仅存储一个指针。我们可以通过从指针中移除当前对象来分配不同的对象。请注意下面的代码。首先,首先,unique_pointer指针指向P1。但是,然后我们移除P1并分配P2,因此指针现在指向P2。

code demo:

include

using namespace std;

include

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 P1(new Rectangle(10, 5));
cout << P1->area() << endl; // This'll print 50
// unique_ptr P2(P1);
unique_ptr P2;
P2 = move(P1);
// This'll print 50
cout << P2->area() << endl;
// cout<area()<<endl;
return 0;
}
/

Output:
50
50
*/
std::unique_ptr was developed in C++11 as a replacement for std::auto_ptr.unique_ptr is a new facility with similar functionality, but with improved security (no fake copy assignments), added features (deleters) and support for arrays. It is a container for raw pointers. It explicitly prevents copying of its contained pointer as would happen with normal assignment i.e. it allows exactly one owner of the underlying pointer.

std::unique_ptr是在C++11中开发的,作为std::auto_ptr的替代,是一种具有类似功能的新工具,但具有改进的安全性(无假拷贝分配)、添加的功能(删除器)和对数组的支持。它是原始指针的容器。它显式地防止复制其包含的指针,就像正常分配一样,也就是说,它只允许基本指针的一个所有者。

3.2 shared_ptr

By using shared_ptr more than one pointer can point to this one object at a time and it’ll maintain a Reference Counter using use_count() method.

通过使用shared_ptr,一次可以有多个指针指向这个对象,它将使用use_count()方法维护一个引用计数器。

code demo:
//代码效果参考:http://www.zidongmutanji.com/zsjx/532281.html

include

using namespace std;

include

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 P1(new Rectangle(10, 5));
// This'll print 50
cout << P1->area() << endl;
shared_ptr P2;
P2 = P1;
// This'll print 50
cout << P2->area() << endl;
// This'll now not give an error,
cout << P1->area() << endl;
// This'll also print 50 now
// This'll print 2 as Reference Counter is 2
cout << P1.use_count() << endl;
return 0;
}
/
Output:
50
50
50
2
*/
3.3 weak_ptr
It’s much more similar to shared_ptr except it’ll not maintain a Reference Counter. In this case, a pointer will not have a stronghold on the object. The reason is if suppose pointers are holding the object and requesting for other objects then they may form a Deadlock.

它与shared_ptr更相似,只是它不维护引用计数器。在这种情况下,指针在对象上没有据点。原因是如果假设指针持有该对象并请求其他对象,则它们可能会形成死锁。

A weak_ptr is created as a copy of shared_ptr. It provides access to an object that is owned by one or more shared_ptr instances but does not participate in reference counting. The existence or destruction of weak_ptr has no effect on the shared_ptr or its other copies. It is required in some cases to break circular references between shared_ptr instances.

weakptr被创建为shared ptr的副本。它提供对一个或多个shared_ ptr实例拥有的对象的访问,但不参与引用计数。weakptr的存在或销毁对shared ptr或其其他副本没有影响。在某些情况下,需要中断shared_ ptr实例之间的循环引用。

Cyclic Dependency (Problems with shared_ptr): Let’s consider a scenario where we have two classes A and B, both have pointers to other classes. So, it’s always like A is pointing to B and B is pointing to A. Hence, use_count will never reach zero and they never get deleted.

循环依赖(shared_ptr的问题):让我们考虑一个场景,其中我们有两个类a和B,它们都有指向其他类的指针。所以,A总是指向B,B总是指向A。因此,use_count永远不会达到零,它们永远不会被删除。

This is the reason we use weak pointers(weak_ptr) as they are not reference counted. So, the class in which weak_ptr is declared doesn’t have a stronghold of it i.e. the ownership isn’t shared, but they can have access to these objects.

这就是我们使用弱指针(weak_ptr)的原因,因为它们不是引用计数的。因此,声明weak_ptrr的类没有它的据点,即所有权不共享,但它们可以访问这些对象。

So, in case of shared_ptr because of cyclic dependency use_count never reaches zero which is prevented using weak_ptr, which removes this problem by declaring A_ptr as weak_ptr, thus class A does not own it, only have access to it and we also need to check the validity of object as it may go out of scope.

因此,在shared_ptr的情况下,由于循环依赖性,use_count永远不会达到零,这是通过使用weak_ptr来防止的,它通过将Aptr声明为weak ptr来消除这个问题,因此类A不拥有它,只能访问它,我们还需要检查对象的有效性,因为它可能超出作用域。

总结一下,原生指针指向堆内存或其它资源可能存在的问题:

① 忘记delete,造成内存泄露;

② delete后还在使用;

③ 重复delete;

资源被指针指向,可以区分两种情况:

① 只有一个指针指向它(没有copy,但可以move)

② 有多个指针指向它(有copy)

核心就是所有权(ownership)的问题。

STL由此引入了三类模板类智能指针:

① unique_ptr<> // not copy, can move

② shared_ptr<> // eference counting

③ 为避免循环引用的问题,引入了weak_ptr<>

相关文章
|
8天前
|
编译器 C++
【C++核心】指针和引用案例详解
这篇文章详细讲解了C++中指针和引用的概念、使用场景和操作技巧,包括指针的定义、指针与数组、指针与函数的关系,以及引用的基本使用、注意事项和作为函数参数和返回值的用法。
14 3
|
29天前
|
C++
C++(十八)Smart Pointer 智能指针简介
智能指针是C++中用于管理动态分配内存的一种机制,通过自动释放不再使用的内存来防止内存泄漏。`auto_ptr`是早期的一种实现,但已被`shared_ptr`和`weak_ptr`取代。这些智能指针基于RAII(Resource Acquisition Is Initialization)原则,即资源获取即初始化。RAII确保对象在其生命周期结束时自动释放资源。通过重载`*`和`-&gt;`运算符,可以方便地访问和操作智能指针所指向的对象。
|
29天前
|
C++
C++(九)this指针
`this`指针是系统在创建对象时默认生成的,用于指向当前对象,便于使用。其特性包括:指向当前对象,适用于所有成员函数但不适用于初始化列表;作为隐含参数传递,不影响对象大小;类型为`ClassName* const`,指向不可变。`this`的作用在于避免参数与成员变量重名,并支持多重串联调用。例如,在`Stu`类中,通过`this-&gt;name`和`this-&gt;age`明确区分局部变量与成员变量,同时支持链式调用如`s.growUp().growUp()`。
|
2月前
|
存储 安全 C++
C++:指针引用普通变量适用场景
指针和引用都是C++提供的强大工具,它们在不同的场景下发挥着不可或缺的作用。了解两者的特点及适用场景,可以帮助开发者编写出更加高效、可读性更强的代码。在实际开发中,合理选择使用指针或引用是提高编程技巧的关键。
24 1
|
2月前
|
存储 C++
c++学习笔记06 指针
C++指针的详细学习笔记06,涵盖了指针的定义、使用、内存占用、空指针和野指针的概念,以及指针与数组、函数的关系和使用技巧。
30 0
|
15天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
60 30
|
4天前
|
并行计算 Unix Linux
超级好用的C++实用库之线程基类
超级好用的C++实用库之线程基类
12 4
|
4天前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
4天前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
|
4天前
|
存储 运维 监控
超级好用的C++实用库之日志类
超级好用的C++实用库之日志类
10 0
下一篇
无影云桌面