动态内存的基本功能和使用
動(dòng)態(tài)內(nèi)存的基本功能和使用
- 基本知識(shí)
- 動(dòng)態(tài)內(nèi)存
- new和delete
- shared_ptr
- StrBlob類(lèi)與StrBlobPtr類(lèi)
- 示例代碼
- 程序1
- 程序2
- 程序3
本文是本人大一期間在校學(xué)習(xí)C++課程時(shí)所撰寫(xiě)的實(shí)驗(yàn)報(bào)告的摘錄,由于剛上大學(xué),剛接觸計(jì)算機(jī)編程方面的相關(guān)知識(shí),故可能會(huì)有很多不足甚至錯(cuò)誤的地方,還請(qǐng)各位看官指出及糾正。
本文所涉及到的“教材”指:
電子工業(yè)出版社《C++ Primary中文版(第5版)》
如需轉(zhuǎn)載或引用請(qǐng)標(biāo)明出處。
本文涉及到的三個(gè)程序通過(guò)自定義了一個(gè)結(jié)構(gòu)體Foo、類(lèi)型StrBlob、指向其的類(lèi)型StrBlobPtr以及定義其他一些操作這些類(lèi)或結(jié)構(gòu)體的函數(shù),來(lái)演示了如何使用動(dòng)態(tài)內(nèi)存及其特點(diǎn),并展示了shared_ptr的用法和特點(diǎn)。下面是一些相關(guān)的知識(shí)點(diǎn)。
基本知識(shí)
動(dòng)態(tài)內(nèi)存
除了自動(dòng)和static對(duì)象外,C++還支持動(dòng)態(tài)分配對(duì)象。動(dòng)態(tài)分配的對(duì)象的生存期與它們?cè)谀睦锝▌?chuàng)是無(wú)關(guān)的,只有當(dāng)顯示地被釋放時(shí),這些對(duì)象才會(huì)銷(xiāo)毀。也就是說(shuō),動(dòng)態(tài)對(duì)象的生存期由程序來(lái)控制,當(dāng)動(dòng)態(tài)對(duì)象不再使用時(shí),我們的代碼必須顯示地銷(xiāo)毀它們。
new和delete
在C++中,動(dòng)態(tài)內(nèi)存的管理是通過(guò)一對(duì)運(yùn)算符來(lái)完成的:new,在動(dòng)態(tài)內(nèi)存中為對(duì)象分配空間并返回一個(gè)指向該對(duì)象的指針,我們可以選擇對(duì)對(duì)象進(jìn)行初始化;delete,接受一個(gè)動(dòng)態(tài)對(duì)象的指針,銷(xiāo)毀該對(duì)象,并釋放與之關(guān)聯(lián)的內(nèi)存。
然而想通過(guò)使用這兩個(gè)關(guān)鍵字管理動(dòng)態(tài)內(nèi)存很容易出問(wèn)題,因?yàn)槲覀兒苋菀拙屯酸尫艃?nèi)存,這種情況下就會(huì)產(chǎn)生內(nèi)存泄漏;有時(shí)在尚有指針應(yīng)用內(nèi)存的情況下我們就釋放了它,這時(shí)就會(huì)產(chǎn)生引用非法內(nèi)存的指針。
為了更安全地使用動(dòng)態(tài)內(nèi)存,新的標(biāo)準(zhǔn)庫(kù)提供了幾種智能指針類(lèi)型來(lái)管理動(dòng)態(tài)對(duì)象。下面主要介紹shared_ptr。
shared_ptr
shared_ptr類(lèi)似于vector,智能指針也是模板,因此建創(chuàng)智能指針時(shí)須告知編譯器該指針可以指向的類(lèi)型。例如,我們可以像這樣去建創(chuàng)智能指針:
shared_ptr<string> p1; //p1可以指向string shared_ptr<vector<string>> p2; //p2可以指向由string組成的vector智能指針的用法和普通的指針類(lèi)似,都可以通過(guò)解引用智能指針?lè)祷厮赶虻哪繕?biāo),都可以通過(guò)->運(yùn)算符返回它指向目標(biāo)的成員等。
shared_ptr最大的特點(diǎn)是,可以認(rèn)為每個(gè)shared_ptr都有一個(gè)關(guān)聯(lián)的計(jì)數(shù)器,通常也稱(chēng)其為應(yīng)用計(jì)數(shù)。無(wú)論何時(shí)我們拷貝一個(gè)shared_ptr時(shí),計(jì)數(shù)器都會(huì)遞增。
例如,當(dāng)引用一個(gè)shared_ptr初始化另一個(gè)shared_ptr,或?qū)⑺鳛閰?shù)傳遞給一個(gè)函數(shù)以及作為函數(shù)的返回值時(shí),它關(guān)聯(lián)的計(jì)數(shù)器就會(huì)遞增。當(dāng)我們給shared_ptr賦予一個(gè)新值或是shared_ptr被銷(xiāo)毀(例如一個(gè)局部的shared_ptr離開(kāi)其作用域)時(shí),計(jì)數(shù)器會(huì)遞減。一旦一個(gè)shared_ptr的計(jì)數(shù)器變?yōu)?,它就會(huì)自動(dòng)銷(xiāo)毀自己所管理的對(duì)象,并釋放它們占用的內(nèi)存。而這一特性使得正確、安全地使用動(dòng)態(tài)內(nèi)存變得非常容易。
具體例子詳見(jiàn)代碼注釋部分。
StrBlob類(lèi)與StrBlobPtr類(lèi)
這兩個(gè)類(lèi)是自定義的類(lèi),用于演示如何使用動(dòng)態(tài)內(nèi)存。其中StrBlob類(lèi)是對(duì)象本身,StrBlobPtr類(lèi)是用于管理對(duì)象的類(lèi)似于指針的類(lèi)型。其中的一些操作函數(shù)的實(shí)現(xiàn)方法詳見(jiàn)代碼注釋部分。
示例代碼
程序1
① Foo.h
#ifndef FOO_H //如果沒(méi)有定義FOO_H,則進(jìn)行以下定義 #define FOO_H#include <iostream>typedef int T; //定義新的int類(lèi)型T struct Foo { //默認(rèn)情況下Foo的成員是公共的Foo(T t): val(t) { } //重載,使Foo(t)相當(dāng)于令Foo成員val的值為tT val; //Foo只有一個(gè)成員val };//定義輸出函數(shù),接受一個(gè)輸出流和一個(gè)Foo類(lèi)型數(shù)據(jù)的引用為實(shí)參 std::ostream& print(std::ostream &os, const Foo &f) {os << f.val; //通過(guò)輸出流os輸出f的成員valreturn os; //返回os }#endif //定義結(jié)束② allocPtr.cpp
#include <vector> using std::vector;#include <string> using std::string;#include <iostream> using std::istream; using std::ostream; using std::cin; using std::cout; using std::endl; //包含有Foo類(lèi)型相關(guān)信息的頭文件 #include "Foo.h"//factory函數(shù)返回一個(gè)指向動(dòng)態(tài)分配內(nèi)存對(duì)象的指針,所指類(lèi)型為Foo //作用為生成一個(gè)動(dòng)態(tài)分配內(nèi)存的對(duì)象 Foo* factory(T arg) {//恰當(dāng)?shù)靥幚韰?shù)arg//通過(guò)new關(guān)鍵字,動(dòng)態(tài)分配一塊內(nèi)存大小為Foo的大小的對(duì)象,用arg為參數(shù)初始化該對(duì)象//同時(shí)調(diào)用方負(fù)責(zé)釋放此內(nèi)存return new Foo(arg); }//use_factory函數(shù)返回一個(gè)指向動(dòng)態(tài)分配內(nèi)存對(duì)象的指針,所指類(lèi)型為Foo //作用為對(duì)其中的數(shù)據(jù)進(jìn)行操作 Foo* use_factory(T arg) {Foo *p = factory(arg); //通過(guò)調(diào)用factory函數(shù),動(dòng)態(tài)分配Foo所需的空間,用指針p指向print(cout, *p); //用自定義的輸出函數(shù)將p指向的對(duì)象的內(nèi)容(val)輸出到cout cout << endl; //結(jié)束一行//返回p以便被調(diào)用方使用return p; //調(diào)用方必須記得釋放動(dòng)態(tài)分配的內(nèi)存 } int main() {T arg;while (cin >> arg) { //有正確輸入使執(zhí)行循環(huán)Foo *p = use_factory(arg); //用指針p指向use_factory函數(shù)的返回值delete p; //使用之后釋放p所指向的動(dòng)態(tài)內(nèi)存}system("pause");return 0; }運(yùn)行結(jié)果:
依次輸入11 22 33 55 88 -99 00 z[enter],窗口顯示:
程序2
① Foo.h
同上。
② allocSP.cpp
運(yùn)行結(jié)果同程序1
程序3
① StrBlob.h
//如果沒(méi)有定義STRBLOB_H,則進(jìn)行以下定義 #ifndef STRBLOB_H #define STRBLOB_H #include <vector> #include <string> #include <memory> //使用shared_ptr等 #include <stdexcept> //使用一些異常類(lèi)class StrBlobPtr; //該聲明需要在StrBlob中進(jìn)行友元聲明class StrBlob {friend class StrBlobPtr; //聲明StrBlobPtr為友元類(lèi) public: //公開(kāi)成員typedef std::vector<std::string>::size_type size_type; //定義簡(jiǎn)寫(xiě)的size_typeStrBlob() : data(new std::vector<std::string>()) { } //重載構(gòu)造,使StrBlob()相當(dāng)于用使用new動(dòng)態(tài)分配內(nèi)存大小為vector<std::string>構(gòu)造的data StrBlob(const std::string*, const std::string*); //之前的C++11沒(méi)有initializer_list,我們將定義一個(gè)構(gòu)造函數(shù)//它的指針指向一個(gè)數(shù)組//定義關(guān)于size的操作size_type size() const { return data->size(); } //size函數(shù)返回成員data的大小bool empty() const { return data->empty(); } //empty函數(shù)判斷data是否為空//定義增加、刪除元素的操作void push_back(const std::string &t) { data->push_back(t); } //在成員data中添加類(lèi)型為string的元素tvoid pop_back();//定義元素訪(fǎng)問(wèn)的操作std::string& front(); //返回data的頭元素std::string& back(); //返回data的尾元素//下面是關(guān)于StrBlobPtr的接口StrBlobPtr begin();StrBlobPtr end(); private: //私有成員 std::shared_ptr<std::vector<std::string> > data; //data為指向存放string的vector的shared_ptr指針 void check(size_type i, const std::string &msg) const; //檢查函數(shù),如果data[i]不存在則拋出異常msg };//使用內(nèi)聯(lián)關(guān)鍵字重載構(gòu)造函數(shù) inline StrBlob::StrBlob(const std::string *beg, const std::string *end): data(new std::vector<std::string>(beg, end)) { }//StrBlobPtr在嘗試訪(fǎng)問(wèn)不存在的元素時(shí)拋出異常 class StrBlobPtr {friend bool eq(const StrBlobPtr&, const StrBlobPtr&); //聲明友元函數(shù)eq public: //公開(kāi)成員StrBlobPtr(): curr(0) { } //重載構(gòu)造,使StrBlobPtr()相當(dāng)于curr(0)StrBlobPtr(StrBlob &a, size_t sz = 0): wptr(a.data), curr(sz) { } //同為重載構(gòu)造std::string& deref() const; //成員函數(shù)derefStrBlobPtr& incr(); //成員函數(shù)incr()StrBlobPtr& decr(); //成員函數(shù)decr() private://如果檢查成功的話(huà),check函數(shù)返回一個(gè)指向該vector的shared_ptr指針std::shared_ptr<std::vector<std::string>> check(std::size_t, const std::string&) const;//成員wptr的類(lèi)型為指向vector<string>的weak_ptr指針,意味著其指向的對(duì)象可能被釋放 std::weak_ptr<std::vector<std::string> > wptr;std::size_t curr; //成員curr用于指示當(dāng)前位置 };inline std::string& StrBlobPtr::deref() const {//檢查該位置是否超出vector的范圍std::shared_ptr<std::vector<std::string>> p = check(curr, "dereference past end"); return (*p)[curr]; //(*p)是該指針指向的vector }inline std::shared_ptr<std::vector<std::string>> StrBlobPtr::check(std::size_t i, const std::string &msg) const {//檢查該向量vector是否存在std::shared_ptr<std::vector<std::string> > ret = wptr.lock(); //如果不存在,則拋出異常if (!ret)throw std::runtime_error("unbound StrBlobPtr");//如果超出范圍,則拋出另一個(gè)異常 if (i >= ret->size()) throw std::out_of_range(msg);return ret; //否則返回一個(gè)指向vector的shared_ptr指針 }//返回對(duì)遞增對(duì)象的引用 inline StrBlobPtr& StrBlobPtr::incr() {//如果curr早就超過(guò)了容器的范圍,則不能遞增check(curr, "increment past end of StrBlobPtr"); //檢查curr的值是否合法++curr; //增加currreturn *this; //返回當(dāng)前對(duì)象 }inline StrBlobPtr& StrBlobPtr::decr() {//如果curr的值為0,則遞減它會(huì)產(chǎn)生一個(gè)無(wú)效的目標(biāo)--curr; //向前移動(dòng)一個(gè)元素check(-1, "decrement past begin of StrBlobPtr"); //如果curr的值為-1, 則拋出異常 return *this; //返回當(dāng)前對(duì)象 }//返回StrBlob中的頭元素和尾元素 inline StrBlobPtr StrBlob::begin() {return StrBlobPtr(*this); //返回頭元素 }inline StrBlobPtr StrBlob::end() {StrBlobPtr ret = StrBlobPtr(*this, data->size()); //返回尾元素return ret; }//關(guān)于StrBlobPtr中相同元素的操作 inline bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) {std::shared_ptr<std::vector<std::string>> l = lhs.wptr.lock(), r = rhs.wptr.lock();//如果這兩個(gè)vector是同一個(gè)的話(huà)if (l == r) //如果它們同時(shí)指向NULL或同時(shí)指向同一個(gè)元素的話(huà),說(shuō)明它們是相等的 return (!r || lhs.curr == rhs.curr);elsereturn false; //如果它們指向不同的vector,說(shuō)明它們不相等 }//上面函數(shù)的相反 inline bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs) {return !eq(lhs, rhs); } #endif //結(jié)束定義② useBlob.cpp
#include <iostream> using std::cout; using std::endl;#include <string> using std::string;//包含有StrBolb類(lèi)型相關(guān)信息的頭文件 #include "StrBlob.h"int main() {StrBlob b1; //定義一個(gè)StrBlob類(lèi)型b1{//劃分出一個(gè)新的塊string temp[] = { "a", "an", "the" }; //定義臨時(shí)的string數(shù)組temp,用于存儲(chǔ)數(shù)據(jù)StrBlob b2(temp, temp + sizeof(temp) / sizeof(*temp)); //在塊中定義另一個(gè)StrBlob類(lèi)型b2,用temp中的元素進(jìn)行初始化,b2的計(jì)數(shù)器加一b1 = b2; //將b2賦值給b1,遞增b2內(nèi)置的計(jì)數(shù)器,此時(shí)b2計(jì)數(shù)為2b2.push_back("about"); //向b2中添加元素"about"cout << b2.size() << endl; //輸出b2的大小} //離開(kāi)b2的作用域,b2內(nèi)置的計(jì)數(shù)器減一,此時(shí)b2計(jì)數(shù)為1,不為0,因此對(duì)象沒(méi)有被銷(xiāo)毀cout << b1.size() << endl; //輸出b1的大小,若一樣,則說(shuō)明b1和b2是同一個(gè)vector//當(dāng)StrBlobPtr類(lèi)型的指針it不指向b1的尾元素時(shí)執(zhí)行循環(huán)for (StrBlobPtr it = b1.begin(); neq(it, b1.end()); it.incr())cout << it.deref() << endl; //輸出it指向的元素,然后遞增itsystem("pause");return 0; }運(yùn)行結(jié)果:
說(shuō)明b1和b2指向的是同一個(gè)vector。
總結(jié)
以上是生活随笔為你收集整理的动态内存的基本功能和使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Kali Linux ver2020.4
- 下一篇: 共享智能指针编程实验