原文鏈接:C++ - 強引用和弱引用 - c++kuzhon - 博客園 https://www.cnblogs.com/kuzhon/articles/5648807.html
C++ - 強引用和弱引用_等等... 大雄,有啦!-CSDN博客 https://blog.csdn.net/gykimo/article/details/8728735
原來,我認為“為什么會有引用計數這樣的技術”是為了內存自動回收和節省內存,但是讀完下面的幾節后,內存自動回收是一個原因,但是節省內存並不是真正的原因,真正的原因是有些對象如果被復制在現實中是不合事實的。
為什么有引用計數
C++中存在兩種語義:值語義(value sematics)和對象語義(object sematic),對象語義也可以叫做引用語義(reference sematics)。
值語義,指的是對象的拷貝與原對象無關,就像拷貝int一樣,C++的常用類型數據等都是值語義。
對象語義,指的是面向對象意義下的對象,是禁止拷貝的。
在設計一個類的時候該類是否可以被拷貝(即具備拷貝構造函數),取決於拷貝后的語義是否成立,比如一個Thread類,拷貝后系統中並不會啟動另外一個線程,所以拷貝是禁止的。同樣類似於Employee雇員類也是。
這么設計起碼有兩個好處:
1. 語義合理,有些對象復制是不符合常理的
2. 節省內存
強引用
當對象被創建時,計數為1;每創建一個變量引用該對象時,該對象的計數就增加1;當上述變量銷毀時,對象的計數減1,當計數為0時,這個對象也就被析構了。
強引用計數在很多種情況下都是可以正常工作的,但是也有不湊效的時候,當出現循環引用時,就會出現嚴重的問題,以至於出現內存泄露,如下代碼:
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
class parent;
class children;
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;
class parent
{
public:
~parent() { std::cout <<"destroying parent\n"; }
public:
children_ptr children;
};
class children
{
public:
~children() { std::cout <<"destroying children\n"; }
public:
parent_ptr parent;
};
void test()
{
parent_ptr father(new parent());
children_ptr son(new children);
father->children = son;
son->parent = father;
}
void main()
{
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}
運行該程序可以看到,即使退出了test函數后,由於parent和children對象互相引用,它們的引用計數都是1,不能自動釋放,並且此時這兩個對象再無法訪問到。這就引起了c++中那臭名昭著的內存泄漏。
一般來講,解除這種循環引用有下面有三種可行的方法:
1. 當只剩下最后一個引用的時候需要手動打破循環引用釋放對象。
2. 當parent的生存期超過children的生存期的時候,children改為使用一個普通指針指向parent。
3. 使用弱引用的智能指針打破這種循環引用。
雖然這三種方法都可行,但方法1和方法2都需要程序員手動控制,麻煩且容易出錯。下面就介紹弱引用
弱引用
namespace boost {
template<typename T> class weak_ptr {
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r);
weak_ptr(const weak_ptr& r);
~weak_ptr();
T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}
則當t被銷毀時,ptr 被自動置為無效。使用方法如下:
if ( shard_ptr<T> safePtr = ptr.lock() ) safePtr->Fun();
class children
{
public:
~children() { std::cout <<"destroying children\n"; }
public:
boost::weak_ptr<parent> parent;
};
最后值得一提的是,雖然通過弱引用指針可以有效的解除循環引用,但這種方式必須在程序員能預見會出現循環引用的情況下才能使用,也可以是說弱引用僅僅是一種編譯期的解決方案,如果程序在運行過程中出現了循環引用,還是會造成內存泄漏的。因此,不要認為只要使用了智能指針便能杜絕內存泄漏。畢竟,對於C++來說,由於沒有垃圾回收機制,內存泄漏對每一個程序員來說都是一個非常頭痛的問題。
智能指針一個很重要的概念是“所有權”,所有權意味着當這個智能指針被銷毀的時候,它指向的內存(或其它資源)也要一並銷毀。這技術可以利用智能指針的生命周期,來自動地處理程序員自己分配的內存,避免顯示地調用delete,是自動資源管理的一種重要實現方式。
為什么要引入“弱引用”指針呢?弱引用指針就是沒有“所有權”的指針。有時候我只是想找個指向這塊內存的指針,但我不想把這塊內存的生命周期與這個指針關聯。這種情況下,弱引用指針就代表“我指向這東西,但這東西什么時候釋放不關我事兒……”
有些地方為了方便,直接用原始指針(raw pointer)來表示弱引用。然后用這種原始指針,其弱引用的含義不夠明確,萬一別人寫個delete xxxx,你就被坑了……而且弱引用指針還有其它一些方便你正確使用它的好處。
發布於 2014-11-30
作者:旺旺
鏈接:https://www.zhihu.com/question/26851369/answer/34271911
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
有兩個好處
這兩者普通指針都是做不到的。
1、對象被析構了,weakptr會自動等於nullptr
2、weakptr可以還原成sharedptr而不會讓引用計數錯亂
c++ - When is std::weak_ptr useful?
緩存。想用時候可以用,但不想因此阻止資源被釋放。
