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

相关文章
|
12天前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
6天前
|
安全 编译器 C++
C++一分钟之-模板元编程实例:类型 traits
【7月更文挑战第15天】C++的模板元编程利用编译时计算提升性能,类型traits是其中的关键,用于查询和修改类型信息。文章探讨了如何使用和避免过度复杂化、误用模板特化及依赖特定编译器的问题。示例展示了`is_same`类型trait的实现,用于检查类型相等。通过`add_pointer`和`remove_reference`等traits,可以构建更复杂的类型转换逻辑。类型traits增强了代码效率和安全性,是深入C++编程的必备工具。
24 11
|
9天前
|
Java 编译器 Linux
【c++】模板进阶
本文详细介绍了C++中的模板技术,包括非类型模板参数的概念、如何使用它解决静态栈的问题,以及模板特化,如函数模板特化和类模板特化的过程,以提升代码的灵活性和针对性。同时讨论了模板可能导致的代码膨胀和编译时间增加的问题。
9 2
|
9天前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
|
9天前
|
安全 编译器 程序员
【C++11】智能指针
【C++11】智能指针
4 0
|
17天前
|
编译器 C语言 C++
【C++】模板初阶(下)
C++的函数模板实例化分为隐式和显式。隐式实例化由编译器根据实参推断类型,如`Add(a1, a2)`,但`Add(a1, d1)`因类型不一致而失败。显式实例化如`Add&lt;double&gt;(a1, d1)`则直接指定类型。模板函数不支持自动类型转换,优先调用非模板函数。类模板类似,用于创建处理多种数据类型的类,如`Vector&lt;T&gt;`。实例化类模板如`Vector&lt;int&gt;`和`Vector&lt;double&gt;`创建具体类型对象。模板使用时,函数模板定义可分头文件和实现文件,但类模板通常全部放头文件以避免链接错误。
|
9天前
|
设计模式 安全 编译器
【C++11】特殊类设计
【C++11】特殊类设计
29 10
|
14天前
|
C++
C++友元函数和友元类的使用
C++中的友元(friend)是一种机制,允许类或函数访问其他类的私有成员,以实现数据共享或特殊功能。友元分为两类:类友元和函数友元。类友元允许一个类访问另一个类的私有数据,而函数友元是非成员函数,可以直接访问类的私有成员。虽然提供了便利,但友元破坏了封装性,应谨慎使用。
42 9
|
9天前
|
存储 编译器 C语言
【C++基础 】类和对象(上)
【C++基础 】类和对象(上)
|
17天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。