<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文掌握 C++ 智能指針的使用

          共 5365字,需瀏覽 11分鐘

           ·

          2021-06-22 15:38

          RAII 與引用計數(shù)

          了解 Objective-C/Swift 的程序員應(yīng)該知道引用計數(shù)的概念。引用計數(shù)這種計數(shù)是為了防止內(nèi)存泄露而產(chǎn)生的。


          基本想法是對于動態(tài)分配的對象,進行引用計數(shù),每當增加一次對同一個對象的引用,那么引用對象的引用計數(shù)就會增加一次, 每刪除一次引用,引用計數(shù)就會減一,當一個對象的引用計數(shù)減為零時,就自動刪除指向的堆內(nèi)存。


          在傳統(tǒng)C++中,『記得』手動釋放資源,總不是最佳實踐。因為我們很有可能就忘記了去釋放資源而導(dǎo)致泄露所以通常的做法是對于一個對象而言,我們在構(gòu)造函數(shù)的時候申請空間,而在析構(gòu)函數(shù)(在離開作用域時調(diào)用)的時候釋放空間, 也就是我們常說的 RAII 資源獲取即初始化技術(shù)。


          凡事都有例外,我們總會有需要將對象在自由存儲上分配的需求,在傳統(tǒng) C++ 里我們只好使用 new 和 delete 去 『記得』對資源進行釋放。C++11 引入了智能指針的概念,使用了引用計數(shù)的想法,讓程序員不再需要關(guān)心手動釋放內(nèi)存


          這些智能指針就包括 std::shared_ptr std::unique_ptr std::weak_ptr使用它們需要包含頭文件<memory>


          注意:引用計數(shù)不是垃圾回收,引用計數(shù)能夠盡快收回不再被使用的對象,同時在回收的過程中也不會造成長時間的等待, 更能夠清晰明確的表明資源的生命周期。



          std::shared_ptr


          std::shared_ptr 是一種智能指針,它能夠記錄多少個 shared_ptr 共同指向一個對象,從而消除顯式的調(diào)用 delete,當引用計數(shù)變?yōu)榱愕臅r候就會將對象自動刪除。


          但還不夠,因為使用 std::shared_ptr 仍然需要使用 new 來調(diào)用,這使得代碼出現(xiàn)了某種程度上的不對稱。


          std::make_shared 就能夠用來消除顯式的使用 new,所以 std::make_shared 會分配創(chuàng)建傳入?yún)?shù)中的對象, 并返回這個對象類型的 std::shared_ptr 指針。例如:

          #include <iostream>
          #include <memory>
          void foo(std::shared_ptr<int> i)
          {
          (*i)++;
          }
          int main()
          {
          // auto pointer = new int(10); // illegal, no direct assignment
          // Constructed a std::shared_ptr
          auto pointer = std::make_shared<int>(10);
          foo(pointer);
          std::cout << *pointer << std::endl; // 11
          // The shared_ptr will be destructed before leaving the scope
          return 0;
          }

          std::shared_ptr 可以通過 get() 方法來獲取原始指針,通過 reset() 來減少一個引用計數(shù), 并通過 use_count() 來查看一個對象的引用計數(shù)。例如:

          auto pointer = std::make_shared<int>(10);
          auto pointer2 = pointer; // 引用計數(shù)+1
          auto pointer3 = pointer; // 引用計數(shù)+1
          int *p = pointer.get(); // 這樣不會增加引用計數(shù)
          std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 3
          std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 3
          std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 3

          pointer2.reset();
          std::cout << "reset pointer2:" << std::endl;
          std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 2
          std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0, pointer2 已 reset
          std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 2
          pointer3.reset();
          std::cout << "reset pointer3:" << std::endl;
          std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl; // 1
          std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl; // 0
          std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl; // 0, pointer3 已 reset


          std::unique_ptr


          std::unique_ptr 是一種獨占的智能指針,它禁止其他智能指針與其共享同一個對象,從而保證代碼的安全:

          std::unique_ptr<int> pointer = std::make_unique<int>(10); // make_unique 從 C++14 引入
          std::unique_ptr<int> pointer2 = pointer; // 非法

          make_unique 并不復(fù)雜,C++11 沒有提供 std::make_unique,可以自行實現(xiàn):

          template<typename T, typename ...Args>
          std::unique_ptr<T> make_unique( Args&& ...args ) {
          return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
          }

          至于為什么沒有提供,C++ 標準委員會主席 Herb Sutter 在他的博客中提到原因是因為『被他們忘記了』。


          既然是獨占,換句話說就是不可復(fù)制。但是,我們可以利用 std::move 將其轉(zhuǎn)移給其他的 unique_ptr,例如:

          #include <iostream>
          #include <memory>

          struct Foo {
          Foo() { std::cout << "Foo::Foo" << std::endl; }
          ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
          void foo() { std::cout << "Foo::foo" << std::endl; }
          };

          void f(const Foo &) {
          std::cout << "f(const Foo&)" << std::endl;
          }

          int main() {
          std::unique_ptr<Foo> p1(std::make_unique<Foo>());
          // p1 不空, 輸出
          if (p1) p1->foo();
          {
          std::unique_ptr<Foo> p2(std::move(p1));
          // p2 不空, 輸出
          f(*p2);
          // p2 不空, 輸出
          if(p2) p2->foo();
          // p1 為空, 無輸出
          if(p1) p1->foo();
          p1 = std::move(p2);
          // p2 為空, 無輸出
          if(p2) p2->foo();
          std::cout << "p2 被銷毀" << std::endl;
          }
          // p1 不空, 輸出
          if (p1) p1->foo();
          // Foo 的實例會在離開作用域時被銷毀
          }


          std::weak_ptr


          如果你仔細思考 std::shared_ptr 就會發(fā)現(xiàn)依然存在著資源無法釋放的問題。看下面這個例子:

          struct A;
          struct B;

          struct A {
          std::shared_ptr<B> pointer;
          ~A() {
          std::cout << "A 被銷毀" << std::endl;
          }
          };
          struct B {
          std::shared_ptr<A> pointer;
          ~B() {
          std::cout << "B 被銷毀" << std::endl;
          }
          };
          int main() {
          auto a = std::make_shared<A>();
          auto b = std::make_shared<B>();
          a->pointer = b;
          b->pointer = a;
          }

          運行結(jié)果是 A, B 都不會被銷毀,這是因為 a,b 內(nèi)部的 pointer 同時又引用了 a,b,這使得 a,b 的引用計數(shù)均變?yōu)榱?2,而離開作用域時,a,b 智能指針被析構(gòu),卻只能造成這塊區(qū)域的引用計數(shù)減一。


          這樣就導(dǎo)致了 a,b 對象指向的內(nèi)存區(qū)域引用計數(shù)不為零,而外部已經(jīng)沒有辦法找到這塊區(qū)域了,也就造成了內(nèi)存泄露,如圖 1:


          圖 1

          解決這個問題的辦法就是使用弱引用指針 std::weak_ptr,std::weak_ptr是一種弱引用(相比較而言 std::shared_ptr 就是一種強引用)。


          弱引用不會引起引用計數(shù)增加,當換用弱引用時候,最終的釋放流程如圖 2 所示:


          圖 2


          在上圖中,最后一步只剩下 B,而 B 并沒有任何智能指針引用它,因此這塊內(nèi)存資源也會被釋放。


          std::weak_ptr 沒有 * 運算符和 -> 運算符,所以不能夠?qū)Y源進行操作,它的唯一作用就是用于檢查 std::shared_ptr 是否存在,其 expired() 方法能在資源未被釋放時,會返回 false,否則返回 true。


          總結(jié)


          智能指針這種技術(shù)并不新奇,在很多語言中都是一種常見的技術(shù),現(xiàn)代 C++ 將這項技術(shù)引進,在一定程度上消除了 new/delete 的濫用,是一種更加成熟的編程范式。


          來源:https://changkun.de/modern-cpp/zh-cn/05-pointers/index.html


          推薦:

          Android FFmpeg 實現(xiàn)帶濾鏡的微信小視頻錄制功能

          全網(wǎng)最全的 Android 音視頻和 OpenGL ES 干貨,都在這了

          一文掌握 YUV 圖像的基本處理

          瀏覽 75
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天堂AV免费观看 | 网址在线 | 黄色一级片电影在线观看 | 美女啪啪 | 91在线无码精品秘 豆花 |