日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

关于C++拷贝控制

發(fā)布時(shí)間:2023/10/11 综合教程 79 老码农
生活随笔 收集整理的這篇文章主要介紹了 关于C++拷贝控制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

通常來(lái)說(shuō),對(duì)于類(lèi)內(nèi)動(dòng)態(tài)分配資源的類(lèi)需要進(jìn)行拷貝控制:要在拷貝構(gòu)造函數(shù)、拷貝賦值運(yùn)算符、析構(gòu)函數(shù)中實(shí)現(xiàn)安全高效的操作來(lái)管理內(nèi)存。但是資源管理并不是一個(gè)類(lèi)需要定義自己的拷貝控制成員的唯一原因。C++ Primer 第5版 中給出了一個(gè)Message類(lèi)與Folder類(lèi)的例子,分別表示電子郵件消息和消息目錄。每個(gè)Message可以出現(xiàn)在多個(gè)Folder中,但是,任意給定的Message的內(nèi)容只有一個(gè)副本。如果一條Message的內(nèi)容被改變,我們從任意的Folder中看到的該Message都是改變后的版本。為了記錄Message位于哪些Folder中,每個(gè)Message都用一個(gè)set保存所在的Folder的指針,同樣的,每個(gè)Folder都用一個(gè)set保存它包含的Message的指針。二者的設(shè)計(jì)如下圖所示:

C++ Primer中并沒(méi)有給出Folder類(lèi)的實(shí)現(xiàn)。在對(duì)Message及Folder類(lèi)的復(fù)現(xiàn)過(guò)程中,出現(xiàn)了一個(gè)問(wèn)題,導(dǎo)致了嚴(yán)重錯(cuò)誤。

Message及Folder類(lèi)的初步設(shè)計(jì)如下:

Message類(lèi):

class Message
{
friend class Folder;
private:
string contents;
set<Folder*> folders; //功能函數(shù):在本消息的folders列表中加入/刪除新文件夾指針f
void addFolder(Folder* f);
void remFolder(Folder* f); //功能函數(shù):在本消息folders列表中的所有Folder中刪除指向此消息的指針
void remove_from_folders(); public:
string getContents();
set<Folder*> getFolders(); //構(gòu)造函數(shù)與拷貝控制
Message(const string& s = " ") :contents(s) {};
~Message(); //接口:將本消息存入給定文件夾f
void save(Folder& f);
//接口:將本消息在給定文件夾中刪除
void remove(Folder& f);
};

Folder類(lèi):

class Folder
{
friend class Message;
private:
set<Message*> messages; //功能函數(shù):將給定消息的指針添加到本文件夾的messages中
void addMsg(Message* m);
//功能函數(shù):將給定消息的指針在本文件夾中的messages中刪除
void remMsg(Message* m); public:
set<Message*> getMessages();
};

這兩個(gè)類(lèi)有對(duì)稱(chēng)的功能函數(shù):Message.addFolder(Folder* f)與Folder.addMsg(Message* m),以及Message.remFolder(Folder* f)與Folder.remMsg(Message* m),用來(lái)實(shí)現(xiàn)Message的保存以及拷貝控制操作等。

所有成員函數(shù)的實(shí)現(xiàn)如下:

string Message::getContents()
{
return contents;
}
set<Folder*> Message::getFolders()
{
return folders;
} void Message::addFolder(Folder* f)
{
this->folders.insert(f);
}
void Message::remFolder(Folder* f)
{
this->folders.erase(f);
} //接口:將本消息存入給定文件夾f
void Message::save(Folder& f)
{
this->addFolder(&f);
f.addMsg(this);
}
//接口:將本消息在給定文件夾中刪除
void Message::remove(Folder& f)
{
this->remFolder(&f);
f.remMsg(this);
} void Message::remove_from_folders()
{
for (auto f : folders)
{
f->remMsg(this);
}
} Message::~Message()
{
remove_from_folders();
} /*Folder的成員函數(shù)*/
//功能函數(shù):將給定消息的指針添加到本文件夾的messages中
void Folder::addMsg(Message* m)
{
messages.insert(m);
}
//功能函數(shù):將給定消息的指針在本文件夾中的messages中刪除
void Folder::remMsg(Message* m)
{
messages.erase(m);
} set<Message*> Folder::getMessages()
{
return messages;
}

在這個(gè)實(shí)現(xiàn)版本的代碼測(cè)試中,出現(xiàn)了這樣一個(gè)問(wèn)題:程序會(huì)有運(yùn)行時(shí)錯(cuò)誤,主函數(shù)的返回值不為0。測(cè)試代碼如下:

void test()
{
Message m1("Hello,"), m2("World"), m3("!");
Folder f1, f2;
m1.save(f1); m1.save(f2);
m2.save(f2);
m3.save(f2);
m2.remove(f2);
} int main()
{
test();
system("pause");
return 0;
}

運(yùn)行結(jié)果:

經(jīng)調(diào)試排查原因之后,找到了問(wèn)題所在:試圖對(duì)已經(jīng)被的銷(xiāo)毀對(duì)象的指針進(jìn)行解引用。該bug和“函數(shù)返回指向局部變量的指針”所導(dǎo)致的問(wèn)題類(lèi)似。我們?yōu)镸essage類(lèi)定義了析構(gòu)函數(shù):

Message::~Message()
{
remove_from_folders();
}

這個(gè)析構(gòu)函數(shù)的實(shí)現(xiàn)與C++ Primer上的實(shí)現(xiàn)完全一致。該析構(gòu)函數(shù)意圖在于當(dāng)一個(gè)Message被銷(xiāo)毀時(shí),應(yīng)該清除它的folders中的所有指向它的指針。這看上去合理,可是在這里卻導(dǎo)致了內(nèi)存錯(cuò)誤。原因在于,remove_from_folders()操作會(huì)訪問(wèn)該Message所在的所有Folder的指針,而若這些Folder的銷(xiāo)毀在該Message的銷(xiāo)毀之前進(jìn)行,則操作會(huì)試圖通過(guò)指針解引用,來(lái)訪問(wèn)已被銷(xiāo)毀的Folder對(duì)象。這會(huì)導(dǎo)致嚴(yán)重的運(yùn)行時(shí)錯(cuò)誤。在本例中,局部變量Folder f1的創(chuàng)建在m1之后,將m1加入f1,test()函數(shù)結(jié)束時(shí),按照局部變量的銷(xiāo)毀順序,會(huì)先銷(xiāo)毀后創(chuàng)建的對(duì)象f1,于是,m1的析構(gòu)函數(shù)會(huì)試圖解引用已被銷(xiāo)毀對(duì)象f1的指針。出現(xiàn)這個(gè)問(wèn)題,是因?yàn)樵趯?shí)現(xiàn)的時(shí)候沒(méi)有按照C++ Primer上的設(shè)計(jì)正確地實(shí)現(xiàn)Folder的析構(gòu)函數(shù)。我們按照如下實(shí)現(xiàn)Folder的析構(gòu)函數(shù):

class Folder
{
/*其他Folder的聲明不變*/ /*加入Folder的析構(gòu)函數(shù),以及一個(gè)工具函數(shù),對(duì)于將要銷(xiāo)毀的Folder,這個(gè)工具函數(shù)負(fù)責(zé)刪除該Folder中所有Message指向它的指針*/
private:
void remove_from_messages();
public:
~Folder();
}; void Folder::remove_from_messages()
{
for (auto m : messages)
m->remFolder(this);
} Folder::~Folder()
{
remove_from_messages();
}

此時(shí),F(xiàn)older的析構(gòu)函數(shù)在Folder被銷(xiāo)毀時(shí)可以正確地刪除所有Message中指向自身的指針,就避免了對(duì)已經(jīng)銷(xiāo)毀的對(duì)象進(jìn)行解引用的操作。反過(guò)來(lái),若先定義的是f1,后定義的是m1,在m1先銷(xiāo)毀時(shí),m1的析構(gòu)函數(shù)也可以正確地刪除所有Folder中指向m1的指針。所以,無(wú)論Folder先被銷(xiāo)毀,還是Message先被銷(xiāo)毀,都能夠正確地執(zhí)行析構(gòu)操作。使用與上面同樣的test()函數(shù)進(jìn)行測(cè)試,程序可以正常地退出了:

這個(gè)例子也給了我們又一次提醒:在C++中,指針與拷貝控制、內(nèi)存管理一定要萬(wàn)分小心謹(jǐn)慎,一點(diǎn)小的差錯(cuò)也可能導(dǎo)致程序的災(zāi)難。

總結(jié)

以上是生活随笔為你收集整理的关于C++拷贝控制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。