说白了,智能指针就是类似于指针的类对象,但是功能比指针多。
智能指针是一种在程序中管理动态分配的内存的工具。智能指针提供了一种机制来自动分配和释放内存,从而减少内存泄漏和悬挂指针的风险。智能指针通过将内存的所有权转移到指针对象本身,可以在对象不再需要时自动释放内存。
智能指针通常会在构造函数中分配内存,并在析构函数中释放内存。此外,智能指针还提供了一些额外的功能,如拷贝构造函数和移动构造函数,以便能够正确地管理指针的所有权。
常见的智能指针模板类型有:std::shared_ptr、std::unique_ptr和std::weak_ptr。
通过使用智能指针,程序员可以更安全地管理内存,并避免一些常见的内存管理错误。
auto_ptr
是C++98标准引入的智能指针,但在C++11标准中被弃用,并建议使用unique_ptr
替代。
我们以auto_ptr为例,来和普通指针进行比较,(因为所有智能指针在释放动态内存这里都是如此)
普通指针
void A()
{
int* a=new int; //为a和一个int值分配存储空间,保存地址
*a=39; //将值复制到动态内存中
return ; //删除a,值被保存到动态内存中
}
智能指针模板类
void B()
{
auto_ptra(new int);//为a和一个int值分配存储空间,保存地址
*a=10; //将值复制到动态内存中
return; //删除a,a的析构函数释放动态内存
}
shared_ptr和unique_ptr的行为和auto_ptr的行为相同。
注意:智能指针模板都位于std命名空间里
1.所有的智能指针类都有一个explicit构造函数,该构造函数以指针为参数
也就是说,C++不会自动将普通指针转换为智能指针(注意不是说不能转换哈)
#include
#include
using namespace std;
int main()
{
int* b = new int(4);
auto_ptrc=b;//这是不行的
cout << *c << endl;
}
但是我们换种方式就行了
#include
#include
using namespace std;
int main()
{
int* b = new int(4);
auto_ptrc(b);
cout << *c << endl;//结果是4
}
这也就意味着我们也能将智能指针赋给相同类型的普通指针
除了上面这个,如果a是一个智能指针对象,还可以对它进行解引用,用它来访问结构成员(->),将它赋给相同类型的常规指针。
注意了哈。我们要禁止将智能指针(是所有智能指针)指向非堆内存。因为智能指针会自动调用delete,这对于非堆内存来说是错误的。说白了,就是只能将智能指针指向动态开辟的内存。
我们看个例子
#include
#include
using namespace std;
int main()
{
int a = 2;
int* b = &a;
auto_ptrc(b);//这是不行的
cout << *c << endl;
}
我们先看下面这个例子
auto_ptra(new int(3));
auto_ptrb;
b=a;
如果a,b是普通指针,则两个指针1将同时指向同一片内存。这是不能接受的,因为程序将试图删除同一个对象两次——一次是a过期的时候,另一次是b过期的时候。那我们怎么避免这种问题呢?
1.进行深复制——重载赋值运算符
2.建立所有权概念:对于特定的对象,只能有一个智能指针拥有它,这样只有拥有对象的智能指针的构造函数会删除该对象。然后让赋值操作转让所有权。这是用于auto_ptr和unique_ptr的策略。
3.创建智能更高的指针,跟踪引用特定对象的智能指针数。这被称为引用计数。例如,赋值时,计数将加1,而指针过期时,计数将减一。仅当最后一个指针过期时,才调用delete。这是shared_ptr的策略。
我们看一个例子
#include
#include
using namespace std;
int main()
{
auto_ptra(new int(2));
cout << *a << endl; //结构是2
auto_ptrb;
b = a; //这是不行的
cout << *a << endl;
cout<< *b<
我们发现将a赋给b后,就不能对a解引用了。
这是因为auto_ptr奉行所有权原则——一个地址只能被一个智能指针指向。将a赋给b时,a放弃了那块地址的所有权,转交到了b上,这个时候我们可以对b解引用
那如果我们将auto_ptr换成shared_ptr会怎么样呢?
我们看看
#include
#include
using namespace std;
int main()
{
shared_ptra(new int(2));
cout << *a << endl; //结果是2
shared_ptrb;
b = a;
cout << *a << endl; //结果是2
cout << *b << endl; //结果是2
}
发现没有任何问题,因为shared_ptr遵循的不是所有权原则。所以同一块内存可以被多个shared_ptr指针指向
那unique_ptr呢?
#include
#include
using namespace std;
int main()
{
unique_ptra(new int(2));
cout << *a << endl;
unique_ptrb;
b = a; //编译器发现这里错误
cout << *a << endl;
cout << *b << endl;
}
unique_ptr也是采用所有权模型。但是使用unique_ptr智能指针时,程序不会等到运行阶段崩溃,而在编译阶段因下面代码出现错误
b=a;
它们的主要区别在于所有权的转移和安全性。
1. 所有权的转移:
- auto_ptr存在所有权的转移,即当一个auto_ptr对象给另一个对象赋值时,所有权会从原来的对象转移到新的对象上。这意味着原来的对象会变为空指针。这种转移可以导致潜在的问题,例如多个指针同时指向同一块内存,可能会导致内存泄漏或无效访问。
- unique_ptr在所有权的转移方面更加严格和安全。它只允许一个unique_ptr指针指向某块内存,不能复制或赋值给其他unique_ptr对象。当unique_ptr对象被赋值给另一个unique_ptr对象时,所有权会完全转移到新的对象上,而原来的对象会变为空指针。
auto_ptra(new int(2));
auto_ptrb;
b=a; //b接管2的所有权,a变为空指针
unique_ptrc(new int(2));
unique_ptrd;
d=c; //unique_ptr禁止赋值给另一个
2. 安全性:
- auto_ptr存在一些安全问题。例如,当auto_ptr对象被复制或赋值给其他对象时,原来的对象会变为空指针。这可能导致潜在的问题,例如多个指针同时指向同一块内存,可能会导致内存泄漏或无效访问。
- unique_ptr则提供了更高的安全性。它禁止复制和赋值操作,确保只有一个unique_ptr指针指向某块内存。这降低了指针操作的潜在风险,并且在编译时会进行类型检查,提高了代码的安全性。
总的来说,unique_ptr比auto_ptr更加灵活和安全。它是C++11引入的新智能指针,推荐在现代C++代码中使用。而auto_ptr已经被废弃,不建议在新的代码中使用。
有人说啊,unique_ptr不是不能赋给另一个unique_ptr吗?
但实际上下面这两种情况是可以的
#include
#include
using namespace std;
unique_ptr A()
{
unique_ptra(new int(2));
return a;
}
int main()
{
unique_ptrb = A();
cout << *b << endl;
}
A()返回一个临时对象unique_ptr,然后b接管了原本归返回的unique_ptr对象a,而a被销毁。这是允许的
unique_ptra(new int(2));
unique_ptrb;
b=std::move(a);//这是可以的
auto_ptr,shared_ptr均使用delete
unique_ptr使用delete[]和delete
所以使用new分配内存时,才能使用auto_ptr,shared_ptr
使用new[]时,只能使用unique_ptr
auto_ptr是C++98中引入的智能指针,用于管理动态分配的对象。虽然它已经被废弃,但仍可以了解其用法。以下是使用auto_ptr的一般步骤:
包含头文件:要使用auto_ptr,首先需要包含头文件
创建对象:使用new关键字创建一个对象,并将其分配给auto_ptr指针。
std::auto_ptr myPtr(new int);
使用对象:使用auto_ptr指针操作对象。
*myPtr = 10;
std::cout << *myPtr << std::endl;
所有权转移:可以将auto_ptr指针赋值给其他auto_ptr对象,从一个对象转移到另一个对象。这将导致原来的auto_ptr对象为空指针。
std::auto_ptr myPtr2 = myPtr;
销毁对象:当auto_ptr对象超出范围时,它会自动释放其所拥有的对象。这将调用被动指针的析构函数,并使用delete操作符来释放内存。
注意事项:
请注意,尽管auto_ptr可能看起来很简单,但由于其存在的问题和局限性,建议使用更现代的智能指针unique_ptr或shared_ptr来代替auto_ptr。
unique_ptr是C++11中引入的智能指针,用于管理动态分配的对象。它提供了独占式所有权,只能有一个unique_ptr指向特定的对象。以下是使用unique_ptr的一般步骤:
包含头文件:要使用unique_ptr,首先需要包含头文件
创建对象:使用new关键字创建一个对象,并将其分配给unique_ptr指针。
std::unique_ptr myPtr(new int);
使用对象:使用unique_ptr指针操作对象。
*myPtr = 10;
std::cout << *myPtr << std::endl;
所有权转移:可以将unique_ptr指针赋值给其他unique_ptr对象,从一个对象转移到另一个对象。这会导致原来的unique_ptr对象为空指针。
std::unique_ptr myPtr2 = std::move(myPtr);
销毁对象:当unique_ptr对象超出范围时,它会自动释放其所拥有的对象。这将调用unique_ptr的析构函数,并使用delete操作符来释放内存。
使用自定义删除器:unique_ptr可以使用自定义的删除器来释放对象。可以使用lambda表达式、函数指针或函数对象作为删除器。
std::unique_ptr myPtr(new int, [](int* p) { delete p; });
或者使用std::default_delete作为默认的删除器。
std::unique_ptr myPtr(new int); // 默认使用std::default_delete
请注意,unique_ptr提供了更严格的所有权管理,不允许多个指针指向同一对象,因此可以更安全地使用和传递指针。它还提供了移动语义,允许高效地转移所有权。这使得unique_ptr成为首选的智能指针类型。
同时,unique_ptr还提供了make_unique函数(C++14及以上版本),用于更方便地创建和初始化unique_ptr对象。
auto myPtr = std::make_unique(10);
总而言之,unique_ptr是一种推荐使用的智能指针,可以更安全地管理动态分配的对象,并提供了更好的语义和性能。
shared_ptr是C++11中引入的智能指针,用于共享对象的所有权。它可以有多个shared_ptr指向同一个对象,当最后一个指针超出范围时,对象才会被销毁。以下是使用shared_ptr的一般步骤:
包含头文件:要使用shared_ptr,首先需要包含头文件
创建对象:可以使用make_shared函数(C++11及以上版本)或直接使用shared_ptr构造函数来创建对象。
std::shared_ptr myPtr = std::make_shared(10);
使用对象:使用shared_ptr指针操作对象。
*myPtr = 20;
std::cout << *myPtr << std::endl;
共享所有权:可以将shared_ptr指针赋值给其他shared_ptr对象,使它们共享相同的对象。
std::shared_ptr myPtr2 = myPtr;
获取引用计数:可以使用use_count函数获取当前有多少个shared_ptr指向相同的对象。
std::cout << myPtr.use_count() << std::endl;
重置指针:可以使用reset函数将shared_ptr指针重置为空指针或指向其他对象。
myPtr.reset();
自定义删除器:shared_ptr可以使用自定义的删除器来释放对象。可以使用lambda表达式、函数指针或函数对象作为删除器。
std::shared_ptr myPtr(new int, [](int* p) { delete p; });
或者使用std::default_delete作为默认的删除器。
std::shared_ptr myPtr(new int); // 默认使用std::default_delete
总而言之,shared_ptr是一种用于共享对象所有权的智能指针,可以安全地管理对象的生命周期,避免内存泄漏和野指针的问题。它是一种非常强大和实用的智能指针类型,特别适用于多个指针需要共享同一个对象的情况。
weak_ptr是C++11中引入的智能指针,用于解决shared_ptr的循环引用问题。它是一种弱引用,不会增加对象的引用计数。以下是使用weak_ptr的一般步骤:
包含头文件:要使用weak_ptr,首先需要包含头文件
创建weak_ptr:可以通过将shared_ptr转换为weak_ptr来创建weak_ptr对象。可以使用lock函数将weak_ptr转换为shared_ptr来操作对象。
std::shared_ptr mySharedPtr = std::make_shared(10);
std::weak_ptr myWeakPtr(mySharedPtr);
检查对象是否有效:可以使用expired函数来检查对象是否有效,即是否已经被销毁。
if (myWeakPtr.expired()) {
std::cout << "Weak pointer is expired." << std::endl;
} else {
std::cout << "Weak pointer is still valid." << std::endl;
}
获取对象:可以使用lock函数将weak_ptr转换为shared_ptr来操作对象。该函数会返回一个有效的shared_ptr,或者一个空的shared_ptr(如果对象已经被销毁)。
std::shared_ptr mySharedPtr2 = myWeakPtr.lock();
if (mySharedPtr2) {
// 对象仍然有效
*mySharedPtr2 = 20;
std::cout << *mySharedPtr2 << std::endl;
} else {
// 对象已经被销毁
std::cout << "Weak pointer is expired." << std::endl;
}
总而言之,weak_ptr是一种弱引用的智能指针,用于解决shared_ptr的循环引用问题。它可以用来检查对象是否有效,并且可以通过lock函数获取对象的shared_ptr以进行操作。使用weak_ptr可以避免循环引用导致的内存泄漏问题。
weak_ptr是C++11引入的一种智能指针,具有以下特点:
弱引用:weak_ptr是对shared_ptr的弱引用,不会增加引用计数。它允许我们观察和访问由shared_ptr管理的对象,但不会拥有对象的所有权。当最后一个shared_ptr析构时,weak_ptr将自动失效。
防止循环引用:weak_ptr可以用于解决shared_ptr的循环引用问题。如果两个对象相互持有shared_ptr指针,它们之间将形成循环引用,导致内存泄漏。通过将其中一个对象的SharedPtr转换为weak_ptr,可以打破循环引用,防止内存泄漏。
安全使用:当通过weak_ptr访问对象时,需要进行有效性检查。通过调用weak_ptr的expired()方法,可以检查weak_ptr是否过期(即shared_ptr是否已被释放)。如果weak_ptr未过期,可以通过调用lock()方法获取一个可用的shared_ptr来访问对象。
不拥有对象所有权:weak_ptr不拥有对象的所有权,因此不能直接操作对象或调用对象的方法。它只是提供一种观察和访问由shared_ptr管理的对象的方式。
总的来说,weak_ptr提供了一种安全地观察由shared_ptr管理的对象的方式,并且可以避免循环引用导致的内存泄漏问题。它在某些场景下非常有用,但需要注意有效性检查和使用时的限制。