C++忘了学,学了忘,可是从未做过真正的项目,那为什么还会重复开头的场景呢?
由于一名Android工程师,C++是必备的技术,每一个产品,仔细去分析的时分都能发现很多模块都能够运用C++开发,从安全性、功能等方面都有协助。
之前关于C/C++的影响便是指针难学,概念多,还有各种内存问题,着实很烦。
智能指针,用于办理动态分配内存,以协助防止内存走漏和办理资源的生命周期。智能指针是与原始指针不同的,它们能够主动地盯梢目标的所有权,当目标不再需求时,主动开释内存。 就问你怕不怕。
智能指针分类
在C++ 规范库中,有四个只能指针 即
- std::auto_ptr
- std::unique_ptr
- std::shared_ptr
- std::weak_ptr
其间std::auto_ptr在C++ 98 规范中就存在,现在已经废弃,所以不做学习。
std::unique_ptr
std::unique_ptr
是C++11引进的独占所有权智能指针。它答应一个目标拥有另一个目标的仅有所有权,当std::unique_ptr
超出效果域时,它会主动开释目标的内存。
#include <memory>
std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);
主要特点:
- 仅有所有权:每个
std::unique_ptr
实例拥有目标的仅有所有权。 - 高效且安全:没有额定的引证计数开支,适用于大多数状况。
- 可移动语义:能够运用
std::move
转移所有权。 - 不能同享:不能复制或同享所有权,因而不会导致资源走漏
std::shared_ptr
std::shared_ptr
是C++11引进的同享所有权智能指针。它答应多个std::shared_ptr
实例同享同一个目标的所有权,当最后一个std::shared_ptr
超出效果域时,目标的内存将被开释。
#include <memory>
std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(42);
std::shared_ptr<int> sharedPtr2 = sharedPtr1; // 同享所有权
主要特点:
- 同享所有权:多个
std::shared_ptr
能够同享同一个目标的所有权。 - 引证计数:内部保护引证计数,主动开释目标。
- 适用于循环引证:能够与
std::weak_ptr
一同用于处理循环引证问题。 - 有一定开支:引证计数可能引进一些额定的开支。
std::weak_ptr
std::weak_ptr
也是C++11引进的,用于处理std::shared_ptr
可能导致的循环引证问题。std::weak_ptr
答应你调查std::shared_ptr
办理的目标,但不拥有它。
#include <memory>
std::shared_ptr<int> sharedPtr = std::make_shared<int>(42);
std::weak_ptr<int> weakPtr = sharedPtr; // 弱引证
主要特点:
- 弱引证:不拥有目标,仅用于调查
std::shared_ptr
。 - 防止循环引证:能够用于打破
std::shared_ptr
之间的循环引证,以防止内存走漏。
运用推荐
一般推荐运用std::unique_ptr
来办理独占所有权的资源,运用std::shared_ptr
和std::weak_ptr
来办理同享所有权的资源和处理循环引证问题。防止运用已弃用的std::auto_ptr
。
shared_str — week_ptr
weak_ptr这个智能指针是用来辅佐shared_ptr作业的
在上面智能指针的介绍中,shared_ptr 是同享所有权智能指针,它的中心点在于同享,存在引证计数逻辑,在下面这种场景中,可能导致循环引证问题
#include <memory>
class A;
class B;
class A {
public:
// 运用同享所有权智能指针
std::shared_ptr<B> b_ptr;
A() {
std::cout << "A constructor" << std::endl;
}
~A() {
std::cout << "A destructor" << std::endl;
}
};
class B {
public:
std::shared_ptr<A> a_ptr;
B() {
std::cout << "B constructor" << std::endl;
}
~B() {
std::cout << "B destructor" << std::endl;
}
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
// 构成循环引证
a->b_ptr = b;
b->a_ptr = a;
return 0;
}
在上面的示例中,类 A
和 B
分别包括一个 std::shared_ptr
,它们相互引证对方。当 a
和 b
的引证计数都不再为零时,它们的析构函数永远不会被调用,导致内存走漏。
为了处理这个问题,能够运用 std::weak_ptr
来打破循环引证。std::weak_ptr
答应你调查 std::shared_ptr
办理的目标,但不拥有它,因而不会增加引证计数
以下是运用 std::weak_ptr
处理循环引证问题的代码
#include <memory>
#include <iostream>
class A;
class B;
class A {
public:
// 运用 weak_ptr 代替 shared_ptr
std::weak_ptr<B> b_weak_ptr;
A() {
std::cout << "A constructor" << std::endl;
}
~A() {
std::cout << "A destructor" << std::endl;
}
};
class B {
public:
// 运用 weak_ptr 代替 shared_ptr
std::weak_ptr<A> a_weak_ptr;
B() {
std::cout << "B constructor" << std::endl;
}
~B() {
std::cout << "B destructor" << std::endl;
}
};
int main() {
// 创建 shared_ptr
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
// 运用 weak_ptr
a->b_weak_ptr = b;
b->a_weak_ptr = a;
// 当 a 和 b 的引证计数为零时,它们的析构函数会被调用,不会出现内存走漏
return 0;
}
std::weak_ptr
用于代替了 std::shared_ptr
,并且在 main
函数的末尾显式地开释了 a
和 b
的 shared_ptr
,使它们的引证计数削减
关于 Java语言型选手,这个疑虑要消除
std::shared_ptr
和Java中的GC Roots(废物收集根节点)的确有一些相似之处,当看到引证计数这四个字时,肯定会想到GC roots 的算法。
咱们来对比一下:
相似之处:
-
引证计数:
std::shared_ptr
和GC Roots 都与引证计数相关。std::shared_ptr
运用引证计数来盯梢有多少个智能指针实例引证了同一目标。GC Roots 也类似,它们是根节点,盯梢哪些目标被引证,哪些不再被引证。 -
生命周期办理:
std::shared_ptr
和GC Roots 都用于办理目标的生命周期。std::shared_ptr
经过引证计数来确保目标在没有引证时被开释,GC Roots 确保被引证的目标不会被废物收集器收回。 -
循环引证问题:
std::shared_ptr
和GC Roots 都能够处理循环引证问题。在std::shared_ptr
中,运用std::weak_ptr
来打破循环引证,GC Roots 能够辨认不再可达的目标并开释它们。
不同之处
-
语言和环境:
std::shared_ptr
是C++中的一个智能指针,用于办理动态分配的内存。GC Roots 是Java虚拟机中的概念,用于废物收回。它们存在于不同的编程语言和执行环境中。 -
废物收集方式:GC Roots 一般与废物收回算法(如标记-清除、分代废物收回)一同运用,以辨认和收集不再可达的目标。而
std::shared_ptr
运用引证计数来盯梢目标的引证状况,不需求像废物收回器那样扫描整个目标图。 -
功能和开支:由于不同的废物收回算法和环境要求,GC Roots 可能会引进一些功能开支,而
std::shared_ptr
的开支一般较小。
总结
在C++编程中,智能指针是一种强壮的工具,可用于办理内存和资源,确保安全和高效的资源办理。std::shared_ptr
和std::unique_ptr
是现代C++中常用的智能指针类型,分别用于同享和独占资源所有权,在今后的开发中能够放心大胆的运用。