乐趣区

关于c++:C面试八股文什么是智能指针

某日二师兄加入 XXX 科技公司的 C ++ 工程师开发岗位第 19 面:

面试官:什么是智能指针?

二师兄:智能指针是 C ++11 引入的类模板,用于治理资源,行为相似于指针,但不须要手动申请、开释资源,所以称为智能指针。

面试官:C++11 引入了哪些智能指针?

二师兄:三种,别离是shared_ptrunique_ptr、和weak_ptr

面试官:说一说三种指针的特色及用处。

二师兄:好的。shared_ptr应用了援用计数(use count)技术,当复制个 shared_ptr 对象时,被治理的资源并没有被复制,而是减少了援用计数。当析构一个 shared_ptr 对象时,也不会间接开释被治理的的资源,而是将援用计数减一。当援用计数为 0 时,才会真正的开释资源。shared_ptr能够不便的共享资源而不用创立多个资源。

二师兄:unique_ptr则不同。unique_ptr独占资源,不能拷贝,只能挪动。挪动过后的 unique_ptr 实例不再占有资源。当 unique_ptr 被析构时,会开释所持有的资源。

二师兄:weak_ptr能够解决 shared_ptr 所持有的资源循环援用问题。weak_ptr在指向 shared_ptr 时,并不会减少 shared_ptr 的援用计数。所以 weak_ptr 并不知道 shared_ptr 所持有的资源是否曾经被开释。这就要求在应用 weak_ptr 获取 shared_ptr 时须要判断 shared_ptr 是否无效。

struct Boo;
struct Foo{std::shared_ptr<Boo> boo;};
struct Boo{std::shared_ptr<Foo> foo;};

二师兄:Foo 中有一个智能指针指向 Goo,而 Goo 中也有一根智能指针指向 Foo,这就是循环援用,咱们能够应用 weak_ptr 来解决这个文通。

Boo boo;
auto foo = boo.foo.lock();
if(foo)
{// 这里通过获取到了 foo,能够应用}else
{// 这里没有获取到,不能应用}

面试官:好的。智能指针是线程平安的吗?

二师兄:是的。抛开类型 T,智能指针是类型平安的。

面试官:为什么?

二师兄:因为智能指针底层应用的援用计数是 atomic 的原子变量,原子变量在自增自减时是线程平安的,这保障了多线程读写智能指针时是平安的。

面试官:好的。为什么尽量不要应用裸指针初始化智能指针?

二师兄:因为可能存在同一个裸指针初始了多个智能指针,在智能指针析构时会造成资源的屡次开释。

面试官:为什么不要从智能指针中返回裸指针呢?

二师兄:是因为如果返回的裸指针被开释了,智能指针持有的资源也生效了,对智能指针的操作是未定义的行为。

面试官:智能指针可能持有数组吗?

二师兄:shread_ptrunique_ptr 都能够持有数组。

面试官:那你晓得在开释资源的时候两者有什么不同吗?

二师兄:这个临时还不分明。。

面试官:能够应用动态对象初始化智能指针吗?

二师兄:让我想想。。不能够,因为动态对象的生命周期和过程一样长,而智能指针的析构的时候会导致动态资源被开释。这会导致未定义的行为。

面试官:如果须要在一个类中实现一个办法,这个办法返回这个类的 shread_ptr 实例,须要留神哪些货色?

二师兄:须要继承 std::enable_shared_from_this 类,办法返回shared_from_this()

struct Foo : public std::enable_shared_from_this<Foo>
{std::shared_ptr<Foo> get_foo()
    {return shared_from_this();
    }
};

面试官:为什么不间接返回 this 指针?

二师兄:额。。。不太分明,然而这应该是个范式。

面试官:好的,明天的面试完结了,请回去等告诉吧。

明天二师兄的体现不错,让咱们看看一些答复的不太现实的中央吧。

智能指针是线程平安的吗?

很遗憾,使用不当的时候并不是。

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>

struct Foo
{Foo(int i):i_(i){}
    void print() {std::cout << i_ << std::endl;}
    int i_;
};

int main(int argc, char const *argv[])
{
    {auto shptr = std::make_shared<Foo>(42);
        std::thread([&shptr](){std::this_thread::sleep_for(std::chrono::seconds(1));
            shptr->print();}).detach();}
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 0;
}
// g++ test.cpp -o test -lpthread
// ./test 
// Segmentation fault

当咱们向另一个线程传递智能指针的援用时,因为 use count 并没有加 1,在shptr 析构时间接销毁了治理的 Foo 实例,所以在线程中执行 shptr->print() 会引发coredump

批改起来也很简略,把 std::thread([&shptr]() 改成 std::thread([shptr]() 即可。记住,智能指针尽量不要传援用

晓得在开释资源的时候 shread_ptrunique_ptr有什么不同吗?

这里须要在 shared_ptr 结构时传入 deleter,用来销毁持有的数组,而unique_ptr 无需此操作,因为 unique_ptr 重载了unique_ptr(T[])

get_foo()办法为什么不间接返回 this 指针?

参考”为什么尽量不要应用裸指针初始化智能指针“。聪慧的小伙伴,想想如果屡次调用 get_foo() 会产生什么?

好了,明天二师兄的面试之旅到这里就完结了。感激小伙伴的急躁浏览。如果您感觉还不错,请多多反对二师兄,拜谢~

关注我,带你 21 天“精通”C++!(狗头)

退出移动版