日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

关于C++拷贝控制

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

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

C++ Primer中并沒有給出Folder類的實現(xiàn)。在對Message及Folder類的復現(xiàn)過程中,出現(xiàn)了一個問題,導致了嚴重錯誤。

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

Message類:

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類:

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();
};

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

所有成員函數(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;
}

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

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;
}

運行結(jié)果:

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

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

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

class Folder
{
/*其他Folder的聲明不變*/ /*加入Folder的析構(gòu)函數(shù),以及一個工具函數(shù),對于將要銷毀的Folder,這個工具函數(shù)負責刪除該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();
}

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

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

總結(jié)

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

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