智能指標的那些事,智能指標那些事

來源:互聯網
上載者:User

智能指標的那些事,智能指標那些事

C++不像Java,C#語言,它沒有記憶體回收機制,但是它提供了強大而靈活的管理機制,使得開發人員自己避免記憶體泄露。可以通過new 獲得記憶體或建立對象,一定使用delete來釋放,這樣就避免記憶體泄露。同時也可以將分配和使用用類封裝,從而保證沒有記憶體泄露。

#include <iostream>

using namespace std;

 

#include <stdio.h>

#include <string.h>

 

class simpleClass

{

private:

char *m_buf;

size_t m_size;

public:

simpleClass(size_t n = 1)

{

m_buf = new char[n];

m_size = n;

}

~simpleClass()

{

printf("%d is deleted at %xd \n", m_size, m_buf);

delete[] m_buf;

}

char * GetBuf()

{

return m_buf;

}

};

 

void foo()

{

simpleClass  a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

}

 

int main()

{

foo();

printf("exit main()...\n");

return 0;

}

這個程式中,對char類型的記憶體配置封裝在類simpleClass中,通過聲明對象,並給所需記憶體的大小,調用GetBuf擷取響應記憶體,這段記憶體在simpleClass的對象退出時會調用解構函式自動釋放。但是還是存在不完美的地方,如果在foo()函數中增加一條賦值語句

void foo()

{

simpleClass  a(10);

simpleClass b = a;

char *p = a.GetBuf(); // 增加的語句

 

strcpy(p, "Hello");

printf("%s\n", p);

}

 

 

在實現simpleClass時,並沒有實現拷貝建構函式,因此編譯器會構造一個預設的拷貝建構函式,執行位拷貝(bit copy)操作,即對象a的內容一個個位元組的拷貝到對象b中,因此對象a中的m_buf和拷貝後對象b中的m_buf指向的是同一個地址的記憶體。當a和b都銷毀時,m_buf中的內容被銷毀兩次。解決方案:

(1)禁止拷貝構造,將拷貝建構函式聲明為私人的。

(2)使用引用計數,對使用記憶體維護一個計數器。當有指標指向這塊記憶體,計數器加1,當指向這塊記憶體的指標銷毀時,計數器減1,只有當計數器減為0時,表示沒有指標指向這塊記憶體,這塊記憶體才可以被釋放。

#include <iostream>

using namespace std;

 

#include <stdio.h>

#include <string.h>

 

class simpleClass

{

private:

char *m_buf;

size_t m_size;

int *m_count;

public:

simpleClass(size_t n = 1)

{

m_buf = new char[n];

m_size = n;

 

m_count = new int; // 為m_count分配空間 

*m_count = 1;

printf("m_count is:%d\n", *m_count);

}

simpleClass(const simpleClass& s)

{

m_size = s.m_size;

m_buf = s.m_buf;

m_count = s.m_count;

(*m_count)++;

printf("m_count is:%d\n", *m_count);

}

~simpleClass()

{

(*m_count)--;

printf("m_count is:%d\n", *m_count);

if (0 == *m_count)

{

printf("%d is deleted at %d\n", m_size, m_buf);

delete[] m_buf;

delete m_count;

}

}

char * GetBuf()

{

return m_buf;

}

};

 

void foo()

{

simpleClass a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

 

simpleClass b = a;

printf("b=%s \n", b.GetBuf());

}

 

int main()

{

foo();

printf("exit main()...\n");

return 0;

}

 

分析:當a被構造時,m_count初始化為1,當執行b=a時,count增加為2。a 和 b指向同一塊記憶體,儲存的內容都是“hello”。當退出foo()時,b首先被銷毀,m_count減1,但是m_count的值仍是大於0的,所以記憶體沒有釋放。當a銷毀時,m_count的值減為0,才釋放對應的記憶體。

如何在foo()函數裡面添加兩句代碼

void foo()

{

simpleClass a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

simpleClass b = a;

 

// 添加代碼

simpleClass c(20);

c = a;

printf("b= %s,c= %s\n", b.GetBuf(), c.GetBuf());

}

 

 

在通過拷貝構造建立b之後,聲明了一個c對象,申請的記憶體大小是20位元組。然後a賦值給c,此時c指向了a的記憶體。而c原來指向的記憶體則無指標指向,因此被釋放。但是程式沒有處理,造成記憶體泄露。解決方案,使用運算子多載。

simpleClass& operator=(const simpleClass& s)

{

// 判斷當前對象的 m_buf 是否和 s.m_buf 是否指向相同的地址 

if (m_buf == s.m_buf)

return *this;

 

// 如果不是,當前對象引用計數減1 

(*m_count)--;

 

// 如果引用計數為0,釋放該對象指向的空間 

if (*m_count == 0)

{

printf("%d is deleted at %xd\n", m_size, m_buf);

delete[] m_buf;

delete m_count;

}

// 當前對象指標 指向新的地址空間 

m_buf = s.m_buf;

m_size = s.m_size;

m_count = s.m_count;

(*m_count)++;// 這也是為什麼引用計數用指標的原因??

}

 

總算是沒有錯誤了,但是這個智能指標還不完整,並沒有實現真正指標那樣的操作,如運算子*,->都沒有重載。更多詳細內容參考《C++ Primer》 400頁。

推薦書籍:

《C++ Primer》(第5版)

*****************************分割線************************************

如果喜歡,幫忙點贊,推薦給好友

添加方式,在公眾號

搜尋:c語言每日一問

或者:Love_gcc123

或者長按二維碼圖片,就可以添加


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.