面试题:智能指针和裸指针混用容易出现哪些问题?

旅行   2024-09-25 11:55   广东  

欢迎关注本公众号,专注面试题拆解

分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取,免费分享。 欢迎V:fb964919126


智能指针和裸指针混用容易出现哪些问题?

C++中智能指针和裸指针混用,如果处理的不够仔细,会引入一些问题,这里主要说几种常见易错的问题:

第一种情况:双重释放

代码示例:

#include <memory>
class MyClass {public: MyClass() { /* 构造函数 */ } ~MyClass() { /* 析构函数 */ }};
int main() { MyClass* rawPtr = new MyClass(); // 创建裸指针 std::unique_ptr<MyClass> smartPtr(rawPtr); // 智能指针接管裸指针
// 裸指针尝试释放智能指针管理的对象 delete rawPtr; // 双重释放错误,因为智能指针也会尝试释放
// 智能指针离开作用域,对象被释放 return 0;}

在这段代码中,rawPtr 指向一个动态分配的对象。然后,将 rawPtr 作为构造函数参数传递给了 std::unique_ptr<MyClass>,这意味着 smartPtr 现在拥有这个对象的所有权,并会在其离开作用域时自动删除该对象

然而,在这段代码中,又显式地调用了 delete rawPtr,那么会导致双重释放。在VS2022上运行,程序崩溃

第二种情况:悬挂指针,野指针

代码示例1:

#include <memory>#include <iostream>
class MyClass {public: MyClass() { /* 构造函数 */ } ~MyClass() { std::cout << "析构函数被调用" << std::endl; }
void someMethod() { std::cout << " k = " << k << std::endl; }
private: int k = 0;};
int main() { std::unique_ptr<MyClass> smartPtr(new MyClass()); // 创建智能指针 MyClass* rawPtr = smartPtr.get(); // 获取智能指针管理的对象的裸指针
// 智能指针离开作用域,对象被释放 { std::unique_ptr<MyClass> anotherSmartPtr = std::move(smartPtr); }
// 裸指针变成了悬挂指针 rawPtr->someMethod(); // 访问悬挂指针,导致未定义行为 return 0;}

在这个示例中:

smartPtr 是一个 std::unique_ptr,用于管理 MyClass 类型的对象。

rawPtr 是一个指向 smartPtr 管理的对象的裸指针。

使用 std::move 将 smartPtr 的所有权移动到 anotherSmartPtr。

当 anotherSmartPtr 离开作用域时,它会自动释放所管理的对象。

在这个过程中,rawPtr 指向的对象已经被 anotherSmartPtr 释放,因此 rawPtr 成为一个悬挂指针。如果随后通过 rawPtr 访问对象的方法,这里不一定会发生崩溃,但是会导致未定义的行为。在VS2022上运行,程序没有崩溃,但是预期值没有打印出来。

代码示例2:智能指针交互,用裸指针做临时变量

#include <iostream>#include <memory>
class MyClass {public: MyClass(int value) : value_(value) { std::cout << "MyClass " << value << " constructed\n"; } ~MyClass() { std::cout << "MyClass " << value_ << " destructed\n"; } void display() { std::cout << "MyClass " << value_ << " displayed\n"; }private: int value_;};
int main() { std::shared_ptr<MyClass> smartPtr1(new MyClass(1)); std::shared_ptr<MyClass> smartPtr2(new MyClass(2)); std::cout << "交换前" << std::endl; smartPtr1->display(); smartPtr2->display(); std::cout << "******************" << std::endl; { // 获取裸指针 MyClass* pTemp = smartPtr1.get(); smartPtr1 = smartPtr2; smartPtr2.reset(pTemp); } std::cout << "******************" << std::endl; std::cout << "交换后" << std::endl; smartPtr1->display(); smartPtr2->display();
return 0;}

上面代码目的是smartPtr1和smartPtr2要实现交换,但是实际上因为中间的临时变量使用了裸指针,没有达到预期的效果

MyClass* pTemp = smartPtr1.get();这句代码执行完,smartPtr1的内存引用计数还是1,当执行smartPtr1 = smartPtr2这句代码的时候,smartPtr1的引用计数会被置为0然后调用析构,导致之前指向的内存资源其实已经释放,这个时候pTemp已经是野指针了,后面再操作就是错误了。

VS2022实际运行结果如下:

可以看到smartPtr1在中途已经调用了析构函数,它原来指向的资源已经被释放。

总结:

量避免将裸指针和智能指针混用。

end



CppPlayer 



关注,回复【电子书】珍藏CPP电子书资料赠送

精彩文章合集

专题推荐

【专辑】计算机网络真题拆解
【专辑】大厂最新真题
【专辑】C/C++面试真题拆解

CppPlayer
一个专注面试题拆解的公众号
 最新文章