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

相关文章
|
4月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
80 2
|
4月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
90 0
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
88 4
|
2月前
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
51 5
|
2月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
44 2
|
2月前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
2月前
|
存储 分布式计算 编译器
深入计算机语言之C++:C到C++的过度-2
深入计算机语言之C++:C到C++的过度-2
|
2月前
|
编译器 Linux C语言
深入计算机语言之C++:C到C++的过度-1
深入计算机语言之C++:C到C++的过度-1
|
3月前
|
数据安全/隐私保护 C语言 C++
C++(七)封装
本文档详细介绍了C++封装的概念及其应用。封装通过权限控制对外提供接口并隐藏内部数据,增强代码的安全性和可维护性。文档首先解释了`class`中的权限修饰符(`public`、`private`、`protected`)的作用,并通过示例展示了如何使用封装实现栈结构。接着介绍了构造器和析构器的使用方法,包括初始化列表的引入以及它们在内存管理和对象生命周期中的重要性。最后,通过分文件编程的方式展示了如何将类定义和实现分离,提高代码的模块化和复用性。
|
4月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
103 4
下一篇
DataWorks