学习智能指针之前需要知道的:
- 智能指针是原始指针的封装,在头文件<memory>中,优点就是自动分配内存,不用担心潜在的内存泄漏。
- 不是所有的指针都可以封装成智能指针,很多时候原始指针更方便。
- 各指针中,最常用的还是裸指针,其次就是unique_ptr 和 shared_ptr
- weak_ptr 是 shared_ptr 的一个补充,使用较少
- auto_ptr 被弃用
智能指针只解决 独占 / 共享 所有权指针的释放、传输,没有根本上解决 C++ 内存安全问题
unique_ptr 独占指针
性质:
- 在任何给定的时刻,只能有一个指针管理内存
- 当指针超出作用域时,内存将自动释放
- 该类型指针不可以 Copy,只可以 Move
创建方式:
- 通过已有裸指针创建
- 通过 new 创建
- 通过 std::make_unique 创建
获取地址:
- 类方法 get() 获取地址
-> 与 * :
- 通过 -> 调用成员函数
- 通过 * 调用 dereferencing
智能指针的使用
测试代码:
#include <iostream>
#include <memory>
class Stu {
std::string m_name;
public:
Stu() {
this->m_name = "stuTemp";
std::cout << "无参构造" << std::endl;
}
Stu(std::string name) :m_name(name) {
std::cout << "有参构造" << std::endl;
}
~Stu() {
std::cout << "析构" << std::endl;
}
void printStuName() const {
std::cout << this->m_name << std::endl;
}
};
int main() {
/* 创建智能指针3种方法 */
// 1、原始指针创建
Stu* s_p1 = new Stu("stu1");
std::unique_ptr<Stu> u_s_p1{ s_p1 };
u_s_p1->printStuName();
// 2、new
std::unique_ptr<Stu> u_s_p2{ new Stu("stu2")};
u_s_p2->printStuName();
// 3、make_unique
std::unique_ptr<Stu> u_s_p3 = std::make_unique<Stu>("stu3");
u_s_p3->printStuName();
//...
//std::unique_ptr<int> u_i_p1 = std::make_unique<int>(10);
//std::cout << "value:" << *u_i_p1 << std::endl;
//std::cout << "address:" << u_i_p1 << std::endl;
//std::cout << "address:" << u_i_p1.get() << std::endl;
//std::cout << "address:" << u_s_p3.get() << std::endl;
std::cout << "----------------" << std::endl;
return 0;
}
运行结果:
unique_ptr 与 函数调用
传入值的方式:
- 需要使用 std::move 来转移内存拥有权
- 如果参数直接传入 std::make_unique 语句 自动转换为 move
传入引用的方式:
- 如果设置参数为 const 则不能修改指向,eg:不能 reset()
- reset() 方法为智能指针清空方法
函数返回值的方式:
- 指向一个 local object
- 可以用作链式函数
测试代码:
#include <iostream>
#include <memory>
class Stu {
std::string m_name;
public:
Stu() {
this->m_name = "stuTemp";
std::cout << "无参构造" << std::endl;
}
Stu(std::string name) :m_name(name) {
std::cout << "有参构造" << std::endl;
}
~Stu() {
std::cout << "析构" << std::endl;
}
void printStuName() const {
std::cout << this->m_name << std::endl;
}
void setStuName(std::string name) {
this->m_name = name;
}
};
void doWithStuPassValue(std::unique_ptr<Stu> s) {
s->printStuName();
}
void doWithStuPassRef(const std::unique_ptr<Stu>& s) {
s->setStuName("JJ");
s->printStuName();
//s.reset(); // 清空
}
std::unique_ptr<Stu> getUniquePtr() {
std::unique_ptr<Stu> u_s_p1 = std::make_unique<Stu>("Local");
std::cout << "address:" << u_s_p1.get() << std::endl;
return u_s_p1;
}
int main() {
// 1、passing by value
std::unique_ptr<Stu> u_s_p1 = std::make_unique<Stu>("NN");
doWithStuPassValue(std::move(u_s_p1)); // 使用 move 转移所有权
//u_s_p1->printStuName(); // 使用已移动的对象
doWithStuPassValue(std::make_unique<Stu>("MM")); // 默认转换成 move 形式
// 2、passing by reference
std::unique_ptr<Stu> u_s_p2 = std::make_unique<Stu>("KK");
doWithStuPassRef(u_s_p2);
//u_s_p2->printStuName();
//std::cout << u_s_p2 << std::endl; // 0
//std::cout << u_s_p2.get() << std::endl; // 0
//u_s_p2->printStuName();
// 3、Return by value
getUniquePtr()->printStuName();
return 0;
}
运行结果:
在函数参数方面,传入参数时,传入的是智能指针就需要使用 move 来转移内存拥有权,如果是直接传入 make_unique 语句,则会默认进行 move。
-
推荐使用的方法是将函数参数修改为 const 加 引用。 就可以直接传入智能指针。