【C++11】智能指针深度详解(什么是智能指针?为什么需要智能指针?如何使用智能指针?)

news2024/9/24 9:25:12

目录

一、前言

二、 智能指针的引入 --- 内存泄露

💢什么是内存泄漏?💢

💢内存泄漏有那些危害?💢

💢内存泄漏的原因?💢 

💢解决内存泄漏的方法 💢

💢总结 💢

三、智能指针的基本概念

🔥智能指针的案例分析 🔥

🔥智能指针的使用及原理🔥 

🍑RAll思想 (智能指针指导思想)

🍍智能指针的原理 

四、C++库里的智能指针 

💧概述 💧

💧unique_ptr 详解 💧

💧shared_ptr 详解 💧 

💧weak_ptr 详解 💧

五、智能指针常考面试题 

六、共勉 


一、前言

智能指针 这个名词听着挺唬人,其实也没啥,无非就是会自动销毁 new 出来的对象,对于日常使用来说,还是挺方便的,毕竟 C/C++ 可没有隔壁 Java 的垃圾回收机制 GC,得自己清理垃圾, 智能指针 可以自动完成垃圾清理这个工作

二、 智能指针的引入 --- 内存泄露

💢什么是内存泄漏?💢

内存泄漏(memory leak)是指程序在动态分配内存后未能正确释放导致这些内存无法被回收和重用。

  • 内存泄漏 会逐渐耗尽系统内存资源,导致程序性能下降,最终可能导致程序崩溃或系统不稳定。内存泄漏在长期运行的程序(如服务器应用程序)中特别危险。 

💢内存泄漏有那些危害?💢

内存泄漏的典型表现包括:

  • 程序的内存使用量不断增加,甚至超出可用内存。
  • 程序运行速度变慢,响应时间变长。
  • 系统资源耗尽,可能导致系统崩溃或冻结。

💢内存泄漏的原因?💢 

内存泄漏通常由以下几种原因引起: 

1. 忘记释放内存: 

  • 程序员动态分配了内存但没有调用 deletefree 释放内存。 
void example() 
{
    int* ptr = new int(42);
    // 忘记调用 delete ptr --- 导致内存泄漏
}

2. 丢失指针: 

  •  动态分配的内存的指针被覆盖或丢失,导致无法释放内存。
void example() 
{
    int* ptr = new int(42);
    ptr = new int(43);  // 原来的内存泄漏
    delete ptr;  // 释放新分配的内存

    // 原来的内存无法释放 --- 导致内存泄漏 
}

3. 循环引用: 

  • 循环引用发生在两个或多个对象互相引用对方,形成一个环,导致它们无法被正确释放。 
  •  假设我们有两个类 AB它们相互引用对方。我们使用普通指针来演示这种情况:
#include <iostream>

class B;  // 前向声明

class A 
{
public:
    B* b;  // A 持有 B 的指针
    ~A() { std::cout << "A destroyed\n"; }
};

class B 
{
public:
    A* a;  // B 持有 A 的指针
    ~B() { std::cout << "B destroyed\n"; }
};

void createCycle() 
{
    A* a = new A();
    B* b = new B();

    a->b = b;  // A 持有 B
    b->a = a;  // B 持有 A

    // 此时 a 和 b 都不会被释放,导致内存泄漏
}

int main() 
{
    createCycle();
    return 0;
}
  •  运行结果我不敢看: (*/ω\*),因为像一坨💩

纳尼?A 和 B的析构函数都没有被调用,妥妥的内存泄漏了!  

事后,据某位亲身经历这次事件的大牛回忆说:“喔,当时的内存布局是这个样子的!” 

  • 对于普通指针,我们需要手动调用 delete 来释放内存。如果我们忘记了这一点,内存将无法释放,导致内存泄漏。这种情况在智能指针中也会出现,如果不使用正确的类型,就会导致内存泄漏

💢解决内存泄漏的方法 💢

手动管理内存

  • 严格遵循动态分配内存后必须释放内存的规则。
  • 使用工具(如 Valgrind)来检测内存泄漏。

使用智能指针: 

  • std::unique_ptr独占所有权,超出作用域时自动释放内存。
  • std::shared_ptr共享所有权,引用计数为零时自动释放内存。
  • std::weak_ptrstd::shared_ptr 搭配使用,解决循环引用问题。

💢总结 💢

内存泄漏是一个严重的问题,会导致程序资源耗尽、性能下降甚至崩溃。通过使用智能指针、工具检测和良好的编程实践,可以有效地预防和解决内存泄漏问题,确保程序的稳定性和可靠性。 

所以【C++11】为了方便大家的代码书写,同时怕大家忘记最后的 内存释放而导致 内存泄漏问题,加入了 智能指针,下面就让我们开启 智能指针 的学习把!! 


三、智能指针的基本概念

🔥智能指针的案例分析 🔥

根据上面的学习我们得知内存泄漏是指因为疏忽或者错误,造成程序未能释放已经不再使用的内存的情况,如下就是一个典型的例子: 

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	int* p1 = new int[10];
	int* p2 = new int[10];
	cout << div() << endl;
	delete[] p1;
	delete[] p2;
}
int main()
{
	try
	{
		Func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

 代码分析:

div() 函数

  • 这个函数读取两个整数 ab
  • 如果 b 等于 0,则抛出一个 invalid_argument 异常,信息是 "除0错误"。
  • 否则,返回 a / b 的结果。

Func() 函数 

  • 分配两个整数数组 p1p2,每个包含 10 个元素。
  • 调用 div() 函数并输出其返回值。
  • 释放之前分配的数组 p1p2

main() 函数 

  • 尝试调用 Func() 函数。
  • 如果捕获到任何异常(继承自 exception),输出异常的消息。

内存泄漏分析: 

Func() 函数中,如果 div() 函数抛出异常(例如输入的 b 为 0),代码将不会继续执行,导致 delete[] p1delete[] p2 不会被调用,从而产生内存泄漏。

为了避免内存泄漏,可以使用 智能指针 来管理内存。例如 std::unique_ptr: 

#include <memory>

void Func()
{
    std::unique_ptr<int[]> p1(new int[10]);
    std::unique_ptr<int[]> p2(new int[10]);
    cout << div() << endl;
}
  • 这样,无论是否发生异常,智能指针都会自动释放内存,确保没有内存泄漏。 

那么,下文将详细的讲解  ---- 智能指针 


🔥智能指针的使用及原理🔥 

🍑RAll思想 (智能指针指导思想)

RAII是一种利用对象生命周期来控制程序资源(例如内存、文件句柄、网络连接、互斥量等)的简单技术.

获取到资源以后去初始化一个对象,将资源交给对象管理:资源获取即初始化

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。 借此,我们实际是把管理一份资源的责任托管给了一个对象,这种做法有两大好处:

  1. 不需要显式的释放资源
  2. 采用这种方式,对象所需的资源在其生命周期内始终有效

如下我们来使用 RAII的思想 设计一个智能指针SmartPtr 

//实现一个最简易的智能指针
template<class T>
class smart_ptr
{
public:
	smart_ptr(T* ptr = nullptr)
		: _ptr(ptr)
	{}
	
    //对象析构时自动释放所管理的资源
	~smart_ptr()
	{
		cout << "delete " << _ptr << endl;

		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}
private:
	T* _ptr;
};
  • 此时,我们使用智能指针来代替裸指针,并让它发生除0错误: 
int div()
{
	int a, b;
	cin >> a >> b;

	if (b == 0)
	{
		throw invalid_argument("除0错误");
	}

	return a / b;
}

void func()
{
	smart_ptr<int> p1(new int);
	smart_ptr<int> p2(new int);

	cout << div() << endl;
}

int main()
{
	try
	{
		func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

  • 可以看到,刚才由于抛异常未能释放的资源现在可以正常释放. 

🍍智能指针的原理 

      我们所写的这个简单的智能指针smart_ptr还不能称其为智能指针,因为它还不具有指针的行为指针可以解引用,可以通过->去访问所指向空间内的内容,所以为了让它向指针一样,我们还需重载*->运算符. 

template<class T>
class smart_ptr
{
public:
	smart_ptr(T* ptr = nullptr)
		: _ptr(ptr)
	{}

	~smart_ptr()
	{
		cout << "delete " << _ptr << endl;

		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;
		}
	}

	T& operator*() const
	{
		return *_ptr;
	}

	T* get() const
	{
		return _ptr;
	}

	T* operator->() const  //T*  = const T* _ptr
	{
		return _ptr;
	}
private:
	T* _ptr;
};
  •  但是这样的智能指针是有问题的,试一下它的拷贝?
int main()
{
	smart_ptr<int> sp1(new int);
	smart_ptr<int> sp2(sp1);//拷贝构造
 
	smart_ptr<int> sp3(new int);
	smart_ptr<int> sp4(new int);
	sp3 = sp4;//拷贝赋值
	return 0;
}

此时我要拿sp1拷贝给sp2完成拷贝构造,或者是拿sp3赋值给sp3都会存在问题,导致程序崩溃,原因如下:

  • 编译器默认生成的拷贝构造函数对内置类型完成值拷贝(浅拷贝),因此sp1拷贝sp2后,相当于sp1和sp2管理了同一块内存空间,所以当sp1和sp2析构时就会导致这块空间被释放两次,程序崩溃。

  • 类似的,把sp4赋值给sp3时,相当于sp3和sp4管理的都是原来sp3管理的空间,当sp3和sp4析构时就会导致这块空间被释放两次,并且还会导致sp4原来管理的空间没有得到释放。

那么,怎么去解决这个问题呢?

所以,接下来,我们来介绍几种C++标准库里的智能指针,来探究如何解决此问题


四、C++库里的智能指针 

💧概述 💧

C++智能指针-------是一类对象,它们封装了原始指针,并提供了一种机制来自动管理和释放所指向的对象。智能指针通过自动管理内存生命周期,减少了手动内存管理带来的复杂性和错误风险。

unique_ptr

  • 这是一种独占所有权的智能指针,不允许拷贝构造,但支持移动语义 move。
  • 这意味着同一时间只能有一个unique_ptr指向给定的资源。
  • 这避免了多个指针指向同一块内存的情况,减少了内存泄漏的风险。

shared_ptr

  • 这是一种引用计数智能指针,它允许多个指针拥有同一个对象
  • 当最后一个拥有该对象的shared_ptr被销毁时,它会自动释放所指向的内存。
  • 使用std::make_shared可以高效地创建一个shared_ptr实例。

weak_ptr

  • 它是一种与shared_ptr配合使用的智能指针,主要用于解决shared_ptr循环引用的问题。
  • weak_ptr不会增加引用计数,因此可以用来打破潜在的循环引用,防止内存无法释放。
  • 在访问所引用的对象前必须先转换为 std::shared_ptr
  • std::weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

头文件

#include <memory>


💧unique_ptr 详解 💧

unique_ptr 是 C++11 引入的一种智能指针,提供独占式所有权的内存管理。它确保一个时刻只有一个指针拥有所管理的对象,并在指针超出作用域时自动释放内存。下面详细讲解 unique_ptr 的用法和用途。

 1. 创建 unique_ptr

可以使用 new 关键字创建动态对象,并将其指针传递给 unique_ptr: 

#include <iostream>
#include <memory> // 包含智能指针的头文件

int main() 
{
    std::unique_ptr<int> ptr(new int(10)); // 创建一个指向整数 10 的 unique_ptr
    std::cout << "Value: " << *ptr << std::endl; // 输出 10
    return 0; // ptr 超出作用域时自动释放内存
}


2. 使用 make_unique(C++14 起) 

make_unique 是一种更安全的创建 unique_ptr 的方法,避免了使用 new 关键字: 

#include <iostream>
#include <memory>

int main() 
{
    unique_ptr<int> ptr = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
    std::cout << "Value: " << *ptr << std::endl; // 输出 10
    return 0; // ptr 超出作用域时自动释放内存
}

 3. 使用 std::move 转移所有权

unique_ptr 不允许复制,但可以通过 std::move 转移所有权: 

#include <iostream>
#include <memory>

int main() 
{
    auto ptr1 = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
    std::unique_ptr<int> ptr2 = std::move(ptr1); // 转移所有权给 ptr2
    if (!ptr1) {
        std::cout << "ptr1 is null" << std::endl; // ptr1 现在为空
    }
    std::cout << "Value: " << *ptr2 << std::endl; // 输出 10
    return 0; // ptr2 超出作用域时自动释放内存
}


4. 管理动态数组 

unique_ptr 可以管理动态分配的数组: 

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int[]> arr(new int[5]); // 创建一个指向包含 5 个整数的数组的 unique_ptr
    for (int i = 0; i < 5; ++i) 
    {
        arr[i] = i * 10; // 赋值
    }

    for (int i = 0; i < 5; ++i) 
    {
        std::cout << arr[i] << " "; // 输出数组元素
    }
    std::cout << std::endl;
    return 0; // arr 超出作用域时自动释放内存
}

5. unique_ptr的单一所有权 

下面的示例代码展示了如何使用 unique_ptr 来管理动态分配的内存: 

#include <iostream>
#include <memory>

int main() 
{
    std::unique_ptr<int> ptr1 = std::make_unique<int>(10); // 创建一个指向整数 10 的 unique_ptr
    std::cout << "Value: " << *ptr1 << std::endl; // 输出 10

    // 试图复制 unique_ptr 会导致编译错误
    // std::unique_ptr<int> ptr2 = ptr1; // 错误,unique_ptr 不可复制

    return 0; // ptr1 超出作用域时自动释放内存
}

总结
std::unique_ptr 的用途主要有以下几个方面: 

  • 自动管理动态内存:避免显式调用 delete,防止内存泄漏。
  • 独占所有权语义:确保对象在同一时刻只有一个所有者,防止多个指针同时管理同一对象带来的问题。
  • 转移所有权:通过 std::move 可以安全地转移对象的所有权。
  • RAII(资源获取即初始化):保证资源在对象创建时被获取,并在对象销毁时被释放。

总的来说,unique_ptr 是 C++11 提供的一种强大工具,用于安全、简洁地管理动态内存和其他资源。通过理解和正确使用 unique_ptr,可以显著提高代码的健壮性和可维护性。 


💧shared_ptr 详解 💧 

shared_ptr 是 C++11 引入的一种智能指针,用于共享所有权的内存管理。多个 shared_ptr 对象可以指向同一个对象,当最后一个 shared_ptr 释放时,该对象的内存才会被释放。下面详细讲解 shared_ptr 的用法和用途。 

1. 创建 shared_ptr 

可以使用 make_shared 或直接使用构造函数创建 shared_ptr 

#include <iostream>
#include <memory>

int main() 
{
    // 使用 make_shared 创建 shared_ptr
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
    std::cout << "Value: " << *ptr1 << std::endl; // 输出 10

    // 使用构造函数创建 shared_ptr
    std::shared_ptr<int> ptr2(new int(20));
    std::cout << "Value: " << *ptr2 << std::endl; // 输出 20

    return 0;
}

2. 共享所有权

多个 shared_ptr 可以指向同一个对象,并共享该对象的所有权:

#include <iostream>
#include <memory>

int main() 
{
    std::shared_ptr<int> ptr1 = std::make_shared<int>(30);

    // ptr2 共享 ptr1 所指向的对象
    std::shared_ptr<int> ptr2 = ptr1;

    std::cout << "Value from ptr1: " << *ptr1 << std::endl; // 输出 30
    std::cout << "Value from ptr2: " << *ptr2 << std::endl; // 输出 30

    // 引用计数
    std::cout << "Use count: " << ptr1.use_count() << std::endl; // 输出 2

    return 0;
}


3. 自动释放内存 

当最后一个 shared_ptr 超出作用域时,所管理的对象会被自动释放: 

#include <iostream>
#include <memory>

void createAndDestroy() 
{
    std::shared_ptr<int> ptr = std::make_shared<int>(40);
    std::cout << "Value: " << *ptr << std::endl; // 输出 40
    std::cout << "Use count inside function: " << ptr.use_count() << std::endl; // 输出 1
    // ptr 超出作用域,内存自动释放
}

int main() 
{
    createAndDestroy();
    // 到这里,createAndDestroy 函数中的 shared_ptr 已经超出作用域,内存被释放
    return 0;
}

4. 避免循环引用 

shared_ptr 可能导致循环引用,从而造成内存泄漏。使用 std::weak_ptr 可以打破循环引用: 

#include <iostream>
#include <memory>

struct Node 
{
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环引用
    ~Node() {
        std::cout << "Node destroyed" << std::endl;
    }
};

int main() 
{
    auto node1 = std::make_shared<Node>();
    auto node2 = std::make_shared<Node>();
    
    node1->next = node2;
    node2->prev = node1;

    std::cout << "Use count of node1: " << node1.use_count() << std::endl; // 输出 1
    std::cout << "Use count of node2: " << node2.use_count() << std::endl; // 输出 1

    return 0; // node1 和 node2 超出作用域,内存被释放
}

 总结

 std::shared_ptr 是 C++ 中一种非常有用的智能指针,它提供共享所有权的内存管理功能,使得多个指针可以安全地共享同一个对象,并在最后一个指针销毁时自动释放资源。通过使用 shared_ptr,可以简化内存管理,减少内存泄漏的风险,并使代码更加安全和易于维护。


💧weak_ptr 详解 💧

std::weak_ptr 是 C++11 引入的智能指针类型,用于解决 std::shared_ptr循环引用问题。它提供了一种不参与对象引用计数的弱引用,这意味着它不会影响所管理对象的生命周期。weak_ptr 只能从 shared_ptr 或另一个 weak_ptr 构造,并且只能提升(lock)shared_ptr 以访问对象。下面详细讲解 weak_ptr 的用法和用途。 

避免循环引用 

  • 循环引用是指当一个对象或者一个单元格内的公式直接或间接地引用了自己或者另一个对象。这样会导致内存泄漏或者计算错误。也就是说,如果两个shared_ptr指针互相引用,那么它们的引用计数永远不会为零,也就无法释放内存。
struct Node
{
    std::shared_ptr<Node> _next;
    std::shared_ptr<Node> _prev;
    
    ~Node()
    {
        cout << "~Node" << endl;
    }
};
int main()
{
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);
    // 循环引用
    n1->_next = n2;
    n2->_prev = n1;
    
    return 0;
}

图例解析如下: 

其中,两个赋值语句造成了两个结点对象n1和n2循环引用: 

n1 和 n2 两个 Node 对象通过 _next 和 _prev 成员变量相互引用,导致它们的引用计数永远不为零,从而无法被销毁。 

死循环:资源只有在引用计数为1时才能被销毁。左边资源只有当右边的_prev释放以后引用计数才为0,而右边资源只有当左边的_next释放以后引用计数才为0。 

解决这个问题的一种方法是使用 std::weak_ptr 来代替其中任意一个方向上的 std::shared_ptr。 

std::weak_ptr 是一种智能指针,它用来解决 std::shared_ptr 循环引用的问题。它不会增加所指向对象的引用计数,因此不会影响对象的销毁。 

对于上面的例子,造成问题的本质是引用计数永不为0,那么只要将其中一个智能指针改为weak_ptr即可: 

struct Node
{
    std::shared_ptr<Node> _next;
    std::weak_ptr<Node> _prev; // 将_prev用weak_ptr管理
    
    ~Node()
    {
        cout << "~Node" << endl;
    }
};


与shared_ptr的关系

  • weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期。也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
  • 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造,它的构造和析构不会引起引用记数的增加或减少。弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

也就是说,weak_ptr是为了弥补shared_ptr循环引用而生的,它没有RAII的特性,不直接管理资源,只是shared_ptr的跟班,这也是weak_ptr支持使用shared_ptr构造的原因。

在使用上,weak_ptr支持指针所有的操作。它不是一个功能型的智能指针,而是辅助型,它的使命是解决shared_ptr造成的循环引用问题。

与 shared_ptr 不同,weak_ptr 不能直接访问所指向的对象。要访问对象,需要先调用 lock() 方法将其转换为 shared_ptr。如果所指向的对象已经被销毁,则 lock() 方法返回空指针。 

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(100);
    std::weak_ptr<int> weakPtr = sharedPtr;

    if (auto lockedPtr = weakPtr.lock()) { // 尝试提升为 shared_ptr
        std::cout << "Value: " << *lockedPtr << std::endl; // 输出 100
    } else {
        std::cout << "Pointer is expired" << std::endl;
    }

    sharedPtr.reset(); // 手动释放 shared_ptr 所管理的对象

    if (weakPtr.expired()) {
        std::cout << "Pointer is expired" << std::endl; // 输出 Pointer is expired
    }

    return 0;
}

五、智能指针常考面试题 

1. 什么是智能指针?它们的优点是什么? 

  • 智能指针是对象,它们封装了原始指针,并提供了自动内存管理功能。
  • 优点包括:自动释放资源,防止内存泄漏,减少悬挂指针,提高代码的可维护性和安全性。

2. 智能指针为什是 对象? 

  • 封装性: 智能指针封装了原始指针以及其他与内存管理相关的功能,例如引用计数(对于 shared_ptr),删除器(对于自定义删除器的智能指针),等等。

  • 构造函数和析构函数: 智能指针具有构造函数和析构函数,这些构造函数和析构函数在智能指针的生命周期内管理资源的分配和释放。例如,shared_ptr 会在对象被销毁时自动减少引用计数,并在引用计数为零时释放资源。

  • 成员函数: 智能指针提供了一些成员函数,用于访问和管理其封装的指针。例如,unique_ptr 提供 releaseresetget 等成员函数,shared_ptr 提供 use_countuniquereset 等成员函数。

  • 运算符重载: 智能指针重载了一些运算符,使其使用起来更像原始指针。例如,operator*operator-> 允许智能指针像原始指针一样使用。

#include <iostream>
#include <memory>

int main() 
{
    std::unique_ptr<int> uniquePtr = std::make_unique<int>(10); // unique_ptr 是一个对象
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(20); // shared_ptr 是一个对象
    std::weak_ptr<int> weakPtr = sharedPtr;                     // weak_ptr 是一个对象

    std::cout << "uniquePtr: " << *uniquePtr << std::endl;
    std::cout << "sharedPtr: " << *sharedPtr << std::endl;

    // 调用 shared_ptr 的成员函数 use_count()
    std::cout << "sharedPtr use count: " << sharedPtr.use_count() << std::endl;

    return 0;
}

 在这个例子中,uniquePtr, sharedPtr, 和 weakPtr 都是智能指针对象,它们封装了原始指针并提供了一些额外的功能用于资源管理。


3. C++中有哪些智能指针?它们的用途是什么? 

  • std::unique_ptr: 提供独占所有权的智能指针,适用于单一所有权场景。
  • std::shared_ptr: 提供共享所有权的智能指针,适用于多个对象共享同一资源的场景。
  • std::weak_ptr: 用于解决shared_ptr的循环引用问题,不参与引用计数。

4. 如何创建一个std::unique_ptr? 

使用std::make_unique

auto ptr = std::make_unique<int>(10);

使用构造函数: 

std::unique_ptr<int> ptr(new int(10));

5.std::shared_ptr 如何实现引用计数? 

  •  std::shared_ptr内部维护一个引用计数,当一个新的shared_ptr对象指向同一个对象时,引用计数增加;当shared_ptr对象被销毁时,引用计数减少。引用计数为零时,释放所管理的对象。

6. 什么是std::weak_ptr?它的作用是什么? 

  • std::weak_ptr是一个不增加引用计数的智能指针,主要用于避免shared_ptr的循环引用问题。
  • weak_ptr只能从shared_ptr或另一个weak_ptr构造,可以使用lock方法提升为shared_ptr

7. 如何避免shared_ptr的循环引用? 

  •  使用std::weak_ptr打破循环引用。例如,在双向链表或树结构中,父节点使用shared_ptr指向子节点,而子节点使用weak_ptr指向父节点。

8. std::make_shared与直接使用构造函数创建shared_ptr有何不同? 

  • std::make_shared会在单个内存块中分配对象和引用计数,具有更好的性能和更少的内存碎片。
  • 使用构造函数时,引用计数和对象可能分配在不同的内存块中。

9. std::unique_ptr是否可以复制?如果不能,如何转移所有权? 

  • std::unique_ptr不支持复制,因为它提供独占所有权。
  • 可以使用std::move转移所有权:
std::unique_ptr<int> ptr1 = std::make_unique<int>(10);
std::unique_ptr<int> ptr2 = std::move(ptr1);

10.  在什么情况下选择使用智能指针而不是原始指针?

  • 智能指针适用于需要自动管理生命周期的场景,可以避免内存泄漏和悬挂指针问题。原始指针适用于简单的指针运算和需要精细控制生命周期的场景。 

六、共勉 

以下就是我对 【C++11】智能指针 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 【C++11】 的理解,请持续关注我哦!!!     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1942627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go-kratos 学习笔记(3) google buf 管理proto

google buf 管理proto&#xff0c;以及从新归档文件的目录结构 什么是 BSR&#xff1f; BSR 将 Protobuf 文件作为版本化模块进行存储和管理&#xff0c;以便个人和组织可以轻松使用和发布他们的 API。 BSR 带有可浏览的 UI、依赖项管理、API 验证、版本控制、生成的文档以及…

手把手教你CrossOver 24.0.0 for Mac 破解版安装激活2024图文教程

兔八哥爱分享要和大家分享的是一款可以让我们直接在Mac上安装和运行Windows软件和游戏的软件——CrossOver。兔八哥爱分享这次带来的是24.0.0 测试版本。 CrossOver已支持相当多的Windows应用&#xff0c;如Office、AutoCAD、Windows Media Player 9、Photoshop、Dreamweaver、…

Java并发的笔记

打算记录自己的学习情况&#xff0c;尽量不摆烂&#xff0c;另外一件事要有始有终&#xff0c;要弄完 如果多个线程处理同一个变量&#xff0c;读跟写都保证不了 2024.7.22》》》》》》》》》》》》 2.1.1volatile的实现原理 volatile不会引起线程上下文的切换和调度 一致性更…

《0基础》学习Python——第十八讲__爬虫/<1>

一、什么是爬虫 爬虫是一种网络数据抓取的技术。通过编写程序&#xff08;通常使用Python&#xff09;&#xff0c;爬虫可以自动化地访问网页&#xff0c;解析网页内容并提取出所需的数据。爬虫可以用于各种用途&#xff0c;如搜索引擎的索引&#xff0c;数据分析和挖掘&#x…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.7安全架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

软件测试---测试需求分析

课程目标 什么是软件测试需求 软件测试需求的必要性 如何对软件测试需求进行分析&#xff08;重点&#xff09; 课程补充 灰度测试&#xff08;基于功能&#xff09;&#xff1a;先发布部分功能&#xff0c;然后看用户的反馈&#xff0c;再去发布另外一部分的功能更新。 A/B测…

Qt绘制指南针(仪表盘绘制封装使用)

指南针是一种用来确定方向的工具。它由一个磁针制成&#xff0c;其一端被磁化&#xff0c;可以自由旋转。当放置在水平面上时&#xff0c;磁针会指向地球的磁北极。通过观察磁针的指向&#xff0c;我们可以确定地理北方的方向。本示例是在Qt中绘制一个指南针&#xff0c;通过继…

基于springboot+vue+uniapp的宿舍管理系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

C++学习笔记02-结构基础(问题-解答自查版)

前言 以下问题以Q&A形式记录&#xff0c;基本上都是笔者在初学一轮后&#xff0c;掌握不牢或者频繁忘记的点 Q&A的形式有助于学习过程中时刻关注自己的输入与输出关系&#xff0c;也适合做查漏补缺和复盘。 本文对读者可以用作自查&#xff0c;答案在后面&#xff0…

Git的使用教程

仓库分区 Git本地有三个工作区域:工作目录&#xff08;Working Directory&#xff09;,暂存区&#xff08;Stage/Index&#xff09;&#xff0c;资源库&#xff08;Repository或Git Directory&#xff09;。如果再加上远程的git仓库&#xff08;Remove Directory&#xff09;就…

SAP PP 物料主数据字段状态控制

参考 https://zhuanlan.zhihu.com/p/452823415 物料主数据里面的状态 由 1、行业 2、工厂 3、物料类型 4、事务代码 5、采购相关字段 6、客制 优先级 隐藏->显示->必输->可选

Go基础编程 - 11 - 函数(func)

接口&#xff08;interface&#xff09; 函数1. 函数定义1.1. 函数名1.2. 参数列表1.3. 返回值列表 2. 匿名函数3. 闭包、递归3.1 闭包3.1.1 函数、引用环境3.1.2 闭包的延迟绑定3.1.3 goroutine 的延迟绑定 3.2 递归函数 4. 延迟调用&#xff08;defer&#xff09;4.1 defer特…

C语言中的控制语句(三):while 和 do while 语句

文章目录 [toc] &#x1f34a;自我介绍&#x1f34a;while语句&#x1f34a;do...while循环&#x1f34a;&#x1f34a; 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello…

单调栈(随缘复习到了,顺手刷了)

也是不知道为什么突然又复习到单调栈了&#xff0c;所以顺手刷了三道题&#xff0c;总结一下 P6503 [COCI2010-2011#3] DIFERENCIJA 思路&#xff1a;这题是要求每个子区间里面的最大值和最小值的差&#xff0c;我们一开始想的必然是纯暴力呀&#xff0c;但是一看这数据&#…

主流硬派SUV齐聚尼三锅,方程豹豹5成功冲顶

随着汽车的飞速普及&#xff0c;越来越多的车主都喜欢上了越野。 而在浩瀚无垠的沙漠腹地&#xff0c;尼三锅以其独特的地理形态&#xff0c;成为了无数越野爱好者心中的圣地与试炼场。 近日&#xff0c;众多汽车博主携自己的爱车普拉多、方程豹豹5、牧马人、坦克400、坦克700、…

图书馆座位管理系统 /图书馆座位预约系统/图书馆管理系统的设计与实现

摘 要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

excel批量新建多个同类型的表格

背景引入 比如&#xff0c;一个企业有多个部门&#xff0c;现在需要按照某一个excel表模板收集各个部门的信息&#xff0c;需要创建数十个同类型表格&#xff0c;且标题要包含部门名称。 1.修改模板表格标题 在一个文件夹下面放入需要发放给各个部门的表格&#xff0c;将标题…

轨迹优化 | 基于ESDF的共轭梯度优化算法(附ROS C++/Python仿真)

目录 0 专栏介绍1 数值优化&#xff1a;共轭梯度法2 基于共轭梯度法的轨迹优化2.1 障碍约束函数2.2 曲率约束函数2.3 平滑约束函数 3 算法仿真3.1 ROS C实现3.2 Python实现 0 专栏介绍 &#x1f525;课程设计、毕业设计、创新竞赛、学术研究必备&#xff01;本专栏涉及更高阶的…

Chapter17 表面着色器——Shader入门精要学习

Chapter17 表面着色器 一、编译指令1.表面函数2.光照函数3.其他可选参数 二、两个结构体1.Input 结构体&#xff1a;数据来源2.SurfaceOutput 三、Unity背后做了什么四、表面着色器的缺点 一、编译指令 作用&#xff1a;指明该表面着色器的 表面函数 和 光照函数&#xff0c;并…

LeetCode 58.最后一个单词的长度 C++

LeetCode 58.最后一个单词的长度 C 思路&#x1f914;&#xff1a; 先解决当最后字符为空格的情况&#xff0c;如果最后字符为空格下标就往后移动&#xff0c;直到不为空格才停止&#xff0c;然后用rfind查询空格找到的就是最后一个单词的起始位置&#xff0c;最后相减就是单词…