赞
踩
在开发C++代码的时候内存管理是一个很麻烦的事情,开发人员一般都是遵守谁申请谁释放的原则
,但是实际开发过程中难免有疏忽。当存在多个模块互相传递数据时更是困难,因为不知道数据什么时候会用完,因此也很难释放从而导致内存泄漏。
智能指针是一种C++对象,它模拟传统的原始指针的行为
,但可以自动管理所指向的内存。智能指针的原理就是维护一个引用计数
,指向相同内存的智能指针之间赋值
或者拷贝
都会让内部的引用计数增加
,出了某些作用域时引用计数减少
,当引用计数等于0
时指针自动删除内存对象。
int main(int argc, char *argv[]) {
// ......
// 常用声明方式
std::shared_ptr<Student> student_ptr_1 = std::shared_ptr<Student>(new Student());
// 推荐使用方式
std::shared_ptr<Student> student_ptr_2 = std::make_shared<Student>();
}
weak_ptr和shared_ptr这两指针类型最好配套使用,weak_ptr更像是一个shared_ptr观察器
,检测智能指针是否有效的一个观察器。weak_ptr通过shared_ptr的赋值创建,而且不会引起计数器的改变。
int main(int argc, char *argv[]) {
// ......
// 赋值给弱引用
std::weak_ptr<Student> student_ptr_3 = student_ptr_2;
}
统计指向同一内存对象的智能指针的个数。
交换两个智能指针所指向的内存地址。
当前智能指针放弃内存的所有权,引起计数器减少
,同时,清空智能指针,是智能指针变为empty
状态。
判断当前智能指针变量是否是唯一所有权(use_count() = 1
)。
获取当前智能指针包含的实际类型对象地址。
int main(int argc, char *argv[]) { // ...... std::shared_ptr<Student> student_ptr = std::make_shared<Student>(); qInfo() << "[1] 引用次数:" << student_ptr.use_count(); std::shared_ptr<Student> student_copy_ptr = student_ptr; qInfo() << "[2] 引用次数:" << student_ptr.use_count(); { // 局部作用域 std::shared_ptr<Student> student_scope_ptr = student_ptr; qInfo() << "[3] 引用次数:" << student_ptr.use_count(); } qInfo() << "[4] 引用次数:" << student_ptr.use_count(); } /* [1] 引用次数: 1 [2] 引用次数: 2 [3] 引用次数: 3 [4] 引用次数: 2 */
从上面这个代码中可以看出,基于原始的对象进行多次引用后其引用的次数确实是在增加,其中有一次引用是在一个小作用域内,此时当离开这个作用域再次查看其应用次数时计数减少。
int main(int argc, char *argv[]) {
// ......
{
// 构造代带参数的对象
std::shared_ptr<Student> student_ptr = std::make_shared<Student>(1);
qInfo() << "[1] 引用次数:" << student_ptr.use_count();
}
}
/*
[1] 引用次数: 1
id 1 析构了
*/
在第一个示例中知道,在作用域范围内创建的智能指针,在离开作用域范围后就会造成计数器减少,第二个示例则说明当计数器变为0之后,智能指针就会释放掉该对象内存。
int main(int argc, char *argv[]) { // ...... { { std::shared_ptr<Student> student_ptr = std::make_shared<Student>(2); // 获取实际的 Student 对象 Student* student = dynamic_cast<Student*>(student_ptr.get()); qInfo() << "学员 ID : " << student->getId(); // 直接放弃管理权造成析构 student_ptr.reset(); qInfo() << "退出第一层作用域."; } qInfo() << "退出第二层作用域."; } } /* 学员 ID : 2 id 2 析构了 退出第一层作用域 退出第二层作用域 */
使用get()函数可以获取其管理的实际对象,但需要使用dynamic_cast
进行类型的转换。此外,使用reset()函数可以放弃堆内存的管理权限,如果放弃时只剩最后一次引用则释放内存。
创建一个shared_ptr对象,该对象与原始的shared_ptr对象共享管理的内存。
该函数可以检测指向的真实地址是否有效。
放弃对真实指针地址的控制权,并返回真实指针地址,返回的地址可以使用delete进行内存释放。
int main(int argc, char *argv[]) { // ...... std::weak_ptr<Student> watcher; // 创建一个若引用对象 std::shared_ptr<Student> student_ptr = std::make_shared<Student>(2); watcher = student_ptr; qInfo() << "[1] 引用次数:" << watcher.use_count(); if (!watcher.expired()) { qInfo() << "内存有效"; } else { qInfo() << "内存无效"; } student_ptr.reset(); qInfo() << "[2] 引用次数:" << watcher.use_count(); if (!watcher.expired()) { qInfo() << "内存有效"; } else { qInfo() << "内存无效"; } } /* [1] 引用次数: 1 内存有效 id 2 析构了 [2] 引用次数: 0 内存无效 */
int main(int argc, char *argv[]) {
// 常用声明方式
QSharedPointer<Student> student = QSharedPointer<Student>(new Student(3));
// 推荐使用方式
QSharedPointer<Student> student_create = QSharedPointer<Student>::create(1);
}
int main(int argc, char *argv[]) {
// ......
// 直接赋值
QWeakPointer<Student> watcher = student_create;
// 通过 api 赋值
QWeakPointer<Student> watcher_api = student_create.toWeakRef();
}
在使用上无论哪个库中智能指针的使用都是大同小异。
int main(int argc, char *argv[]) { // ...... { QSharedPointer<Student> student = QSharedPointer<Student>::create(1); { QSharedPointer<Student> student_copy = student; } qInfo() << "离开范围"; } QWeakPointer<Student> watcher; QSharedPointer<Student> student_ptr = QSharedPointer<Student>::create(2); watcher = student_ptr; student_ptr.clear(); if (watcher.isNull()) { qInfo() << "无效内存"; } else { qInfo() << "有效内存"; } } /* 离开范围 "id 1 析构了" "id 2 析构了" 无效内存 */
class Student // 继承 std::enable_shared_from_this : public std::enable_shared_from_this<Student> { public: Student(int _id); ~Student(); private: int id_; }; int main(int argc, char *argv[]) { // ...... std::shared_ptr<Student> student_ptr = std::make_shared<Student>(1); qInfo() << student_ptr.use_count(); { // 使用 shared_from_this() 返回自身的引用 std::shared_ptr<Student> student_copy_ptr = student_ptr->shared_from_this(); qInfo() << student_ptr.use_count(); } } /* 1 2 */
class Student // 继承 QEnableSharedFromThis : public QEnableSharedFromThis<Student> { public: Student(int _id); ~Student(); private: int id_; }; int main(int argc, char *argv[]) { // ...... QSharedPointer<Student> student_ptr = QSharedPointer<Student>::create(1); { // 使用 sharedFromThis() 返回自身的引用 QSharedPointer<Student> student_copy_ptr = student_ptr->sharedFromThis(); } }
当有一段内存被释放两次就造成了二次析构问题,在使用智能指针时这种问题比较常见。
"裸指针"
同时指给两个智能指针int main(int argc, char *argv[]) {
// ......
Student* source_ptr = new Student(1);
std::shared_ptr<Student> student_ptr_1 = std::shared_ptr<Student>(source_ptr);
std::shared_ptr<Student> student_ptr_2 = std::shared_ptr<Student>(source_ptr);
student_ptr_1.reset();
student_ptr_2.reset(); // 崩溃
}
一般内存无效时的崩溃报错会有_CtrlsValidHeadPointer(block)
函数的提示,它是用来检测某段内存是否有效的。
int main(int argc, char *argv[]) {
// ......
Student source_ptr(1); // 受堆栈控制
std::shared_ptr<Student> student_ptr
= std::shared_ptr<Student>(&source_ptr); // 受智能指针控制
student_ptr.reset();
}
对于解决二次析构的方式最好就是使用推荐的make_shared方式创建
,而不是使用"裸指针"进行赋值
。如果是必须这么做的话,那一定要明确创建的"裸指针"
是不会被其他机制或者对象控制并被析构才行。
使用智能指针在进行数据的清理时调用的是delete
直接删除了,如果管理的是一段数组内存,此时就需要在创建智能指针的时候定制一个删除器
来控制释放这段内存的方式。
int main(int argc, char *argv[]) { // ...... char * new_array = new char[10]; std::shared_ptr<char> new_array_ptr = std::shared_ptr<char>(new_array, [](char * array) { delete [] array; qInfo() << "new 方式创建数组已析构"; }); char * malloc_array = (char*) malloc(10); std::shared_ptr<char> malloc_array_ptr = std::shared_ptr<char>(malloc_array, [](char * array) { free(array); qInfo() << "malloc 方式创建数组已析构"; }); char ** dimension_array = new char*[10]; for (int row = 0; row < 10; ++row) { for (int col = 0; col < 10; ++col) { dimension_array[row] = new char[col]; } } std::shared_ptr<char*> dimension_array_ptr = std::shared_ptr<char*>(dimension_array, [](char ** array) { for(int row = 0; row < 10; ++row) delete [] array[row]; delete [] array; qInfo() << "二维数组已析构"; }); new_array_ptr.reset(); malloc_array_ptr.reset(); dimension_array_ptr.reset(); } /* new 方式创建数组已析构 malloc 方式创建数组已析构 二维数组已析构 */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。