智能指针是 C++11 引入的一项重要特性,它可以帮助我们管理动态分配的内存,自动释放内存,避免内存泄漏和悬空指针的问题。智能指针有三种常用类型:std::unique_ptr
、std::shared_ptr
和 std::weak_ptr
。
为了帮助你熟悉智能指针的使用,下面是一些练习题,涵盖了智能指针的基本用法以及一些常见的应用场景。
练习 1:使用 std::unique_ptr
任务:创建一个 std::unique_ptr
,用于管理一个动态分配的 int
类型的内存,并打印其值。
#include <iostream>
#include <memory>int main() {// 创建一个unique_ptr来管理动态分配的内存// 请补充代码,使用 std::make_unique 创建一个 unique_ptrreturn 0;
}
提示:使用 std::make_unique
来创建 std::unique_ptr
,并访问其管理的对象。
练习 2:std::unique_ptr
的转移所有权
任务:展示如何通过 std::move
转移 std::unique_ptr
的所有权。
#include <iostream>
#include <memory>void print_value(std::unique_ptr<int> ptr) {std::cout << "Value: " << *ptr << std::endl;
}int main() {std::unique_ptr<int> ptr1 = std::make_unique<int>(10);// 转移所有权到 ptr2std::unique_ptr<int> ptr2 = std::move(ptr1);// 确保 ptr1 已经不再拥有资源,尝试解引用 ptr1 会导致错误if (ptr1 == nullptr) {std::cout << "ptr1 is now null." << std::endl;}// 打印 ptr2 中的值print_value(std::move(ptr2));return 0;
}
提示:使用 std::move
来转移 std::unique_ptr
的所有权。转移后,ptr1
会变为空指针,无法再访问其管理的资源。
练习 3:使用 std::shared_ptr
任务:使用 std::shared_ptr
来管理一个动态分配的 int
类型的内存,并展示多个 shared_ptr
指向同一内存时,引用计数的变化。
#include <iostream>
#include <memory>int main() {std::shared_ptr<int> ptr1 = std::make_shared<int>(20);// 创建 ptr2,并共享 ptr1 管理的内存std::shared_ptr<int> ptr2 = ptr1;std::cout << "ptr1's value: " << *ptr1 << std::endl;std::cout << "ptr2's value: " << *ptr2 << std::endl;// 输出引用计数std::cout << "Reference count: " << ptr1.use_count() << std::endl;return 0;
}
提示:std::shared_ptr
允许多个指针共享同一块内存,它会自动管理引用计数。当引用计数为 0 时,内存会自动释放。
练习 4:std::shared_ptr
的循环引用问题
任务:通过一个简单的类和 std::shared_ptr
,模拟一个循环引用的情况,并展示为什么会导致内存泄漏。
#include <iostream>
#include <memory>class A;
class B;class A {
public:std::shared_ptr<B> b;~A() { std::cout << "A destroyed!" << std::endl; }
};class B {
public:std::shared_ptr<A> a;~B() { std::cout << "B destroyed!" << std::endl; }
};int main() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b = b;b->a = a;// 在这里,a 和 b 的引用计数会永远不为 0,导致内存无法释放return 0;
}
提示:循环引用导致两个 shared_ptr
永远无法释放内存,因为它们互相引用,引用计数始终大于 0。可以通过使用 std::weak_ptr
来解决这个问题。
练习 5:使用 std::weak_ptr
打破循环引用
任务:修改上一题的代码,使用 std::weak_ptr
来解决循环引用的问题。
#include <iostream>
#include <memory>class A;
class B;class A {
public:std::shared_ptr<B> b;~A() { std::cout << "A destroyed!" << std::endl; }
};class B {
public:std::weak_ptr<A> a; // 使用 weak_ptr 打破循环引用~B() { std::cout << "B destroyed!" << std::endl; }
};int main() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b = b;b->a = a; // 使用 weak_ptr 避免循环引用return 0;
}
提示:使用 std::weak_ptr
来避免循环引用问题,weak_ptr
不增加引用计数,因此不会导致资源无法释放。
练习 6:std::shared_ptr
和自定义删除器
任务:使用 std::shared_ptr
创建一个自定义删除器,删除时打印一条消息。
#include <iostream>
#include <memory>void custom_deleter(int* ptr) {std::cout << "Deleting pointer: " << ptr << std::endl;delete ptr;
}int main() {// 创建 shared_ptr 时传入自定义删除器std::shared_ptr<int> ptr = std::shared_ptr<int>(new int(10), custom_deleter);std::cout << "Value: " << *ptr << std::endl;return 0;
}
提示:std::shared_ptr
允许传入自定义的删除器,用来控制资源的释放方式。在删除 shared_ptr
时,删除器会被调用。
练习 7:std::unique_ptr
和数组
任务:使用 std::unique_ptr
来管理动态分配的数组。
#include <iostream>
#include <memory>int main() {// 创建一个unique_ptr来管理动态分配的数组std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);// 给数组赋值并打印for (int i = 0; i < 5; ++i) {arr[i] = i * 10;}for (int i = 0; i < 5; ++i) {std::cout << arr[i] << " ";}return 0;
}
提示:std::unique_ptr
可以用于管理数组,创建时使用 std::make_unique<T[]>
来分配动态数组。
练习 8:std::shared_ptr
与自定义类型
任务:创建一个自定义类并使用 std::shared_ptr
来管理它的实例。
#include <iostream>
#include <memory>class MyClass {
public:MyClass() { std::cout << "MyClass Constructor" << std::endl; }~MyClass() { std::cout << "MyClass Destructor" << std::endl; }
};int main() {std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>();std::cout << "Shared pointer in scope" << std::endl;return 0;
}
提示:std::shared_ptr
可以用于管理自定义类型的对象。使用 std::make_shared
创建并管理对象实例。
这些练习将帮助你理解和掌握 C++ 中智能指针的使用,特别是 std::unique_ptr
和 std::shared_ptr
的基本概念、引用计数、资源管理,以及如何避免常见的内存管理问题(如循环引用)。