欢迎关注本公众号,专注面试题拆解
分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取,免费分享。 欢迎V:fb964919126
智能指针和裸指针混用容易出现哪些问题?
C++中智能指针和裸指针混用,如果处理的不够仔细,会引入一些问题,这里主要说几种常见易错的问题:
第一种情况:双重释放
代码示例:
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:
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:智能指针交互,用裸指针做临时变量
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电子书资料赠送
精彩文章合集
专题推荐