面试题(18)|C/C++基础(10):智能指针

《C++ Primer》

《Effect c++》第三章

C++内存回收功能怎么实现

说说 C++ 中智能指针和指针的区别是什么?

标准回答

1. 智能指针

如果在程序中使用 new 从堆(自由存储区)分配内存,等到不需要时,应使用 delete 将其释放。C++ 引入了智能指针 auto_ptr,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL)表明,需要有更精致的机制。

基于程序员的编程体验和 BOOST 库提供的解决方案,C++11 摒弃了 auto_ptr,并新增了三种智能指针:unique_ptr、shared_ptr 和 weak_ptr。所有新增的智能指针都能与 STL 容器和移动语义协同工作。

2. 指针

C 语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。

3. 智能指针和普通指针的区别

智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制,区别是它负责自动释放所指的对象,这样的一层封装机制的目的是为了使得智能指针可以方便的管理一个对象的生命期。

指针是一种数据类型,用于保存内存地址,而智能指针是类模板。

智能指针weak、share内部实现

华为面试系列:智能指针

智能指针优缺点。

如何理解智能指针? - 知乎

请你回答一下智能指针有没有内存泄露的情况

得分点 shared_ptr、循环引用、weak_ptr

标准回答

智能指针有内存泄露的情况:

当两个类对象中各自有一个 shared_ptr 指向对方时,会造成循环引用,使引用计数失效,从而导致内存泄露。

为了解决循环引用导致的内存泄漏,引入了弱指针weak_ptr,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。

weak_ptr 如何解决 shared_ptr 的循环引用问题?

得分点 引用计数

标准回答

weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,它指向一个由 shared_ptr 管理的对象而不影响所指对象的生命周期,也就是将一个 weak_ptr 绑定到一个 shared_ptr 不会改变 shared_ptr 的引用计数,依此特性可以解决 shared_ptr 的循环引用问题。

weak_ptr 没有解引用 * 和获取指针 -> 运算符,它只能通过 lock 成员函数去获取对应的 shared_ptr 智能指针对象,从而获取对应的地址和内容。

不论是否有 weak_ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。

智能指针是否线程安全?

这里的智能指针单指shared_ptr

shared_ptr对象提供与内置类型相同级别的线程安全性:

1.同一个shared_ptr对象可以被多个线程同时读;

2.不同的shared_ptr对象可以被多个线程同时修改,即使这些shared_ptr对象管理着同一个对象的指针.

3.任何其他并发访问的结果都是无定义的.

shared_ptr线程安全性分析****

shared_ptr源代码?

以下四行代码的区别是什么?const char * arr = "..._c++校招面试题目合集_牛客网

智能指针代码实践深度思考

思考一:std::move

std::unique_ptr<SCH_ITEM> repeatItem( static_cast<SCH_ITEM*>( aItem->Clone() ) );

        // Clone() preserves the flags, we want 'em cleared.
        repeatItem->ClearFlags();

        m_items_to_repeat.emplace_back( std::move( repeatItem ) );//这段代码中的std::move是什么作用

在这段代码中,std::move 的作用是将 std::unique_ptr 对象 repeatItem 的所有权转移给 m_items_to_repeat 容器。由于 std::unique_ptr 是独占所有权的智能指针,因此在将其转移时,源对象将不再拥有指向对象的所有权。

具体来说,std::move 是一个类型转换函数,它将一个左值转换为一个右值引用。对于 std::unique_ptr 对象,通过将其转换为右值引用,可以将其所有权转移给其他对象,而不是进行拷贝或赋值操作。

在这个例子中,std::moverepeatItem 转换为右值引用,并将其传递给 m_items_to_repeat.emplace_back() 函数。由于 emplace_back() 函数使用移动语义将对象插入容器中,因此 repeatItem 的所有权被转移给了容器。这意味着,在函数执行完毕后,指向 SCH_ITEM 对象的唯一所有权现在属于容器中的元素。

思考二:shared_ptr如何实现参数传递

现在有个智能指针std::shared_ptr<wxRibbonPage> page,其中一个函数A(wxRibbonPang*),我该如何将page传给函数A

如果您想将 std::shared_ptr<wxRibbonPage> 对象 page 传递给函数 A,则可以使用 std::shared_ptrget() 成员函数获取指向原始对象的裸指针,并将其传递给函数 A。例如:

void A(wxRibbonPage* pagePtr) 
{ 
// ... 
} 
std::shared_ptr<wxRibbonPage> page = std::make_shared<wxRibbonPage>(m_ribbonBar, wxID_ANY, _("Place"), wxNullBitmap); 
A(page.get());

请注意,由于 std::shared_ptr 具有共享所有权的特性,因此在将 std::shared_ptr 对象传递给函数 A 时,对象的所有权仍然由 std::shared_ptr 管理。因此,只要存在任何一个 std::shared_ptr 指向对象,对象就不会被销毁。如果您确定在函数 A 中不需要继续使用对象,可以将裸指针包装在一个新的 std::shared_ptr 中,并将其传递给函数 A,以确保对象在函数执行完毕后被正确销毁。

std::shared_ptr 的 get() 成员函数与std::move()获取shared_ptr的区别,get()函数有什么危害

std::shared_ptrget() 成员函数和 std::move() 函数都可以用于获取指向 std::shared_ptr 对象管理的原始对象的裸指针。但是,它们的作用是不同的。

std::shared_ptrget() 成员函数返回一个指向原始对象的裸指针,但是它不会影响 std::shared_ptr 对象本身。也就是说,即使您使用 get() 获取了裸指针,仍然需要确保至少有一个 std::shared_ptr 对象指向原始对象,否则对象可能在不该被销毁的时候被释放,导致程序崩溃或其他问题。

相比之下,std::move() 函数会将 std::shared_ptr 对象的所有权转移给其他对象,从而确保在对象不再需要时正确销毁。由于 std::shared_ptr 具有共享所有权的特性,因此只要存在任何一个 std::shared_ptr 指向对象,对象就不会被销毁。因此,通过使用 std::move() 函数将对象的所有权转移给其他对象,可以确保在不再需要对象时正确销毁它。

使用 get() 函数获取裸指针的危害在于,如果您不小心在没有任何 std::shared_ptr 对象指向原始对象时释放了裸指针,程序就会崩溃或出现其他问题。因此,在使用 get() 函数获取裸指针时,请确保至少有一个 std::shared_ptr 对象指向原始对象,并且在不再需要对象时正确销毁它。

所以上面的代码使用std::move会更好:

void A(wxRibbonPage* pagePtr) 
{ 
// ... 
} 
std::shared_ptr<wxRibbonPage> page = std::make_shared<wxRibbonPage>(m_ribbonBar, wxID_ANY, _("Place"), wxNullBitmap); 
A(std::move(page));

现在有个智能指针std::unique_ptr<wxRibbonPage> page,其中一个函数A(wxRibbonPang*),我该如何将page传给函数A

如果您想将 std::unique_ptr<wxRibbonPage> 对象 page 传递给函数 A,则可以使用 std::unique_ptrget() 成员函数获取指向原始对象的裸指针,并将其传递给函数 A。例如:

void A(wxRibbonPage* pagePtr)
 {
 // ... 
} 
std::unique_ptr<wxRibbonPage> page(new wxRibbonPage(m_ribbonBar, wxID_ANY, _("Place"), wxNullBitmap)); 
A(page.get());

请注意,当您将 std::unique_ptr 对象的所有权转移给函数 A 时,函数 A 将成为对象的新所有者,并且负责在不再需要对象时将其正确销毁。如果您不打算将所有权转移给函数 A,则可以考虑使用 std::shared_ptr 来管理对象的生命周期。

std::unique_ptr的release成员函数

std::unique_ptrrelease() 成员函数用于释放智能指针对所管理对象的所有权,并返回指向该对象的裸指针。调用 release() 函数后,智能指针不再拥有对所管理对象的所有权,裸指针将成为该对象的唯一所有者。

release() 函数返回的裸指针可以用于将所有权转移给其他智能指针对象,或者用于直接管理所管理对象的生命周期。但是,请注意,如果您使用 release() 函数释放了智能指针对所管理对象的所有权,并且没有将该对象的所有权转移给其他智能指针对象或手动管理其生命周期,则可能会导致内存泄漏或其他问题。

以下是一个使用 release() 函数释放智能指针对所管理对象的所有权的示例:

 

c复制

std::unique_ptr<int> ptr(new int(42)); 
// 创建一个 unique_ptr 对象,管理一个 int 类型的对象
 int* raw_ptr = ptr.release(); 
// 释放 unique_ptr 对象对所管理对象的所有权,并返回指向该对象的裸指针 
// 现在,raw_ptr 指向该对象,并成为该对象的唯一所有者 
// 在使用完该对象后,需要手动释放它 
delete raw_ptr;

请注意,在使用 release() 函数时,请确保将所管理对象的所有权转移给其他智能指针对象或手动管理其生命周期,以避免内存泄漏或其他问题。

release与get的区别

std::unique_ptrrelease()get() 成员函数都与管理的裸指针有关,但它们的作用是不同的。

  • release() 函数用于释放智能指针对所管理对象的所有权,并返回指向该对象的裸指针。调用 release() 函数后,智能指针不再拥有对所管理对象的所有权,裸指针将成为该对象的唯一所有者。这意味着您需要手动管理所管理对象的生命周期,以避免内存泄漏或其他问题。

  • get() 函数返回一个指向所管理对象的裸指针,但不释放智能指针对所管理对象的所有权。这意味着智能指针仍然拥有对所管理对象的所有权,您仍然需要使用智能指针来管理所管理对象的生命周期。

以下是一个使用 release()get() 函数的示例:

std::unique_ptr<int> ptr(new int(42)); 
// 创建一个 unique_ptr 对象,管理一个 int 类型的对象 
int* raw_ptr = ptr.release();
 // 释放 unique_ptr 对象对所管理对象的所有权,并返回指向该对象的裸指针 
// 现在,raw_ptr 指向该对象,并成为该对象的唯一所有者 
// 在使用完该对象后,需要手动释放它 
delete raw_ptr; 

std::unique_ptr<int> ptr2(new int(23));
 // 创建一个 unique_ptr 对象,管理一个 int 类型的对象 
int* raw_ptr2 = ptr2.get(); 
// 获取 unique_ptr 对象所管理对象的裸指针,但不释放智能指针对所管理对象的所有权
 // 现在,raw_ptr2 指向该对象,但智能指针仍然拥有对该对象的所有权 
// 在使用完该对象后,可以直接使用智能指针释放它
 ptr2.reset(); 
// 释放智能指针对所管理对象的所有权

请注意,在使用 release() 函数时,请确保将所管理对象的所有权转移给其他智能指针对象或手动管理其生命周期,以避免内存泄漏或其他问题。而使用 get() 函数则需要注意不要在智能指针生命周期结束后访问所管理对象的裸指针,以避免悬空指针问题。

get(),release(),std::move区别,各自使用场景,可能问题,

get()release()std::move() 都与 std::unique_ptr 类型有关,但它们的作用是不同的。

  • get() 函数返回一个指向所管理对象的裸指针,但不释放智能指针对所管理对象的所有权。这意味着智能指针仍然拥有对所管理对象的所有权,您仍然需要使用智能指针来管理所管理对象的生命周期。get() 的使用场景通常是需要将所管理对象的裸指针传递给某个函数或类的构造函数等,但不想将智能指针本身传递给该函数或构造函数。

  • release() 函数用于释放智能指针对所管理对象的所有权,并返回指向该对象的裸指针。调用 release() 函数后,智能指针不再拥有对所管理对象的所有权,裸指针将成为该对象的唯一所有者。这意味着您需要手动管理所管理对象的生命周期,以避免内存泄漏或其他问题。release() 的使用场景通常是需要将所管理对象的裸指针传递给某个函数或类的构造函数等,并将所有权转移给该函数或构造函数。

  • std::move() 函数用于将一个 std::unique_ptr 对象的所有权转移给另一个 std::unique_ptr 对象,同时将原来的 std::unique_ptr 对象置为空指针。这意味着原来的 std::unique_ptr 对象不再拥有对所管理对象的所有权,新的 std::unique_ptr 对象成为该对象的唯一所有者。std::move() 的使用场景通常是需要将一个 std::unique_ptr 对象传递给另一个函数或类的构造函数等,并将所有权转移给该函数或构造函数。

在使用这些函数时,需要注意以下问题:

  • 在使用 get() 函数时,请确保不要在智能指针生命周期结束后访问所管理对象的裸指针,以避免悬空指针问题。

  • 在使用 release() 函数时,请确保将所管理对象的所有权转移给其他智能指针对象或手动管理其生命周期,以避免内存泄漏或其他问题。

  • 在使用 std::move() 函数时,请确保原来的 std::unique_ptr 对象不再需要访问所管理对象,并且新的 std::unique_ptr 对象确实需要访问该对象,以避免悬空指针问题。

总之,这些函数各自有不同的使用场景和注意事项,请根据具体情况选择合适的函数并谨慎使用。

什么时悬空指针

悬空指针是指一个指针变量指向已经释放的内存空间,或者指向尚未分配的内存空间的情况。当您使用这样的指针时,程序可能会出现未定义的行为,例如崩溃、数据损坏等问题。

悬空指针通常是由以下原因引起的:

  • 指针变量被赋值为 nullptr 或者未初始化。

  • 指针变量指向的内存空间已经被释放,但指针变量没有被置为 nullptr

  • 指针变量指向的内存空间已经被释放,但程序继续使用该指针变量。

  • 指针变量指向的内存空间尚未分配,但程序继续使用该指针变量。

为了避免悬空指针问题,您可以采取以下措施:

  • 在使用指针变量之前,对其进行初始化或赋值操作。

  • 在释放内存空间后,将指针变量置为 nullptr

  • 避免使用已经被释放或尚未分配的内存空间。

  • 使用智能指针等 RAII 技术来管理内存资源,以避免手动管理内存资源时出现悬空指针问题。

总之,悬空指针是一个常见的程序错误,可以通过谨慎编程和使用现代 C++ 技术来避免

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haimianjie2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值