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++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
8月前
|
存储 算法 安全
c++模板进阶操作——非类型模板参数、模板的特化以及模板的分离编译
在 C++ 中,仿函数(Functor)是指重载了函数调用运算符()的对象。仿函数可以像普通函数一样被调用,但它们实际上是对象,可以携带状态并具有更多功能。与普通函数相比,仿函数具有更强的灵活性和可扩展性。仿函数通常通过定义一个包含operator()的类来实现。public:// 重载函数调用运算符Add add;// 创建 Add 类的对象// 使用仿函数return 0;
272 0
|
8月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
219 0
|
11月前
|
编译器 C++
模板(C++)
本内容主要讲解了C++中的函数模板与类模板。函数模板是一个与类型无关的函数家族,使用时根据实参类型生成特定版本,其定义可用`typename`或`class`作为关键字。函数模板实例化分为隐式和显式,前者由编译器推导类型,后者手动指定类型。同时,非模板函数优先于同名模板函数调用,且模板函数不支持自动类型转换。类模板则通过在类名后加`&lt;&gt;`指定类型实例化,生成具体类。最后,语录鼓励大家继续努力,技术不断进步!
|
编译器 C++
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
㉿㉿㉿c++模板的初阶(通俗易懂简化版)㉿㉿㉿
|
12月前
|
安全 C++
【c++】模板详解(2)
本文深入探讨了C++模板的高级特性,包括非类型模板参数、模板特化和模板分离编译。通过具体代码示例,详细讲解了非类型参数的应用场景及其限制,函数模板和类模板的特化方式,以及分离编译时可能出现的链接错误及解决方案。最后总结了模板的优点如提高代码复用性和类型安全,以及缺点如增加编译时间和代码复杂度。通过本文的学习,读者可以进一步加深对C++模板的理解并灵活应用于实际编程中。
191 0
|
12月前
|
存储 安全 算法
深入理解C++模板编程:从基础到进阶
在C++编程中,模板是实现泛型编程的关键工具。模板使得代码能够适用于不同的数据类型,极大地提升了代码复用性、灵活性和可维护性。本文将深入探讨模板编程的基础知识,包括函数模板和类模板的定义、使用、以及它们的实例化和匹配规则。
|
12月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
10月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
412 12
|
8月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
348 0