c++ 拷贝构造函数_禁止拷贝构造,禁止bug
禁止拷貝構造,禁止bug
一、前言
首先,我先講講為什么會寫這篇文章;這個也是翻閱自己之前博客,當時看開源代碼的時候,總是很奇怪,為什么有的代碼中會會出現類似于Epoll( const Epoll& ) = delete;這樣的代碼產生,當時大概查閱了一下資料,只是說這個代碼的意思是將默認的拷貝構造函數禁止了,但是并沒有了解到為什么這樣做,直到前幾天思考了這個問題,覺得有必要寫下來,并且分享一下,也為自己做一個筆記,在今后的開發過程中,多留一個心眼,少踩一個坑~
二、拷貝構造函數
我們先來認識下什么是拷貝構造函數;
拷貝構造函數是一個特殊的構造函數,一般只有一個參數,這個參數一般是用const修飾的,對自己類的一個引用。
在編寫程序的時候,如果我們沒有編寫拷貝構造函數,那么編譯器會為我們自動生成一個拷貝構造函數。
下面看一下最簡單的構造函數使用吧(使用默認構造函數)
#include?using?namespace?std;class?CTest{public:????CTest(){}????~CTest(){}};int?main(){????CTest?A;????CTest?B?=?A;????return?0;}我們自己編寫一個簡單的拷貝構造函數:
#include?using?namespace?std;class?CTest{public:????CTest(){}????CTest(const?CTest&?C)????{????????cout<結果:
什么情況下會調用拷貝構造函數?
主要有以下幾方面:
- 對象以值作為函數參數傳遞 代碼演示: #include using namespace std;class CTest
{public:
CTest(int num)
{
nNum = num;
}
CTest(const CTest& C)
{
nNum = C.nNum;cout<<"call me CTest"<<endl;
}int getNum()
{return this->nNum;
}
~CTest(){}private:int nNum;
};void g_Fun(CTest C)
{cout<<"C:nNum"<endl;
}int main()
{
CTest A(10);
g_Fun(A);return 0;
} 結果: 具體對象構造過程: g_Fun 函數會產生臨時變量C(void g_Fun(CTest C))調用拷貝構造函數賦值Cg_Fun函數執行完畢后,會析構掉臨時變量 - 一個對象初始化另外一個對象 例子:(開始的案例) int main()
{
CTest A;
CTest B = A;return 0;
}
還有一種說法是當對象作為值的形式作為函數的額返回值
比如:
#include?using?namespace?std;class?CTest{public:????CTest(int?num)????{????????nNum?=?num;????}????CTest(const?CTest&?C)????{????????nNum?=?C.nNum;????????cout<nNum;????}????~CTest(){}private:????int?nNum;};CTest?g_fun(){????CTest?tmp(0);????return?tmp;}int?main(){????g_fun();????return?0;}這種形式的調用拷貝構造函數,根據編譯器不同就會被優化,經過測試windows 環境下vs調式是會調用拷貝構造函數的,但是在linux下g++編譯后,就進行了優化,直接通過堆棧返回對象,少調用一次拷貝構造函數。
三、禁用拷貝構造函數
上面我們回顧了拷貝構造函數的概念以及會調用拷貝構造函數的場景,感覺并沒有異常發生,一切都函數那么友好,那么順利,無論使用我們編寫的構造函數還是使用默認拷貝構造函數,那么我們看一個例子,也許大家會有一些思考:
看下這個案例:
#include?using?namespace?std;class?CTest{public:????CTest(string?arg):name(arg),pStr(new?char[10])????{????}????~CTest()????{????????delete?pStr;????}private:????string?name;????char?*pStr;};int?main(){????CTest?C("test");????CTest?A?=?C;????return?0;}先不要看答案,大家可以思考下~
我們可以編譯運行下,會出現什么現象:
root@iZuf67on1pthsuih96udyfZ:~/GDB/20201014# g++ -std=c++11 CopyConstruct.cpp root@iZuf67on1pthsuih96udyfZ:~/GDB/20201014# ./a.out *** Error in `./a.out': double free or corruption (fasttop): 0x000000000136ac20 ***======= Backtrace: =========/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fb1f13b97e5]/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fb1f13c237a]/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fb1f13c653c]./a.out[0x400caa]./a.out[0x400b7a]/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fb1f1362830]./a.out[0x400a19]======= Memory map: ========00400000-00401000 r-xp 00000000 fd:01 131851 /root/GDB/20201014/a.out00601000-00602000 r--p 00001000 fd:01 131851 /root/GDB/20201014/a.out00602000-00603000 rw-p 00002000 fd:01 131851 /root/GDB/20201014/a.out01359000-0138b000 rw-p 00000000 00:00 0 [heap]7fb1ec000000-7fb1ec021000 rw-p 00000000 00:00 0 7fb1ec021000-7fb1f0000000 ---p 00000000 00:00 0 7fb1f1039000-7fb1f1141000 r-xp 00000000 fd:01 925468 /lib/x86_64-linux-gnu/libm-2.23.so7fb1f1141000-7fb1f1340000 ---p 00108000 fd:01 925468 /lib/x86_64-linux-gnu/libm-2.23.so7fb1f1340000-7fb1f1341000 r--p 00107000 fd:01 925468 /lib/x86_64-linux-gnu/libm-2.23.so7fb1f1341000-7fb1f1342000 rw-p 00108000 fd:01 925468 /lib/x86_64-linux-gnu/libm-2.23.so7fb1f1342000-7fb1f1502000 r-xp 00000000 fd:01 925465 /lib/x86_64-linux-gnu/libc-2.23.so7fb1f1502000-7fb1f1702000 ---p 001c0000 fd:01 925465 /lib/x86_64-linux-gnu/libc-2.23.so7fb1f1702000-7fb1f1706000 r--p 001c0000 fd:01 925465 /lib/x86_64-linux-gnu/libc-2.23.so7fb1f1706000-7fb1f1708000 rw-p 001c4000 fd:01 925465 /lib/x86_64-linux-gnu/libc-2.23.so7fb1f1708000-7fb1f170c000 rw-p 00000000 00:00 0 7fb1f170c000-7fb1f1722000 r-xp 00000000 fd:01 918031 /lib/x86_64-linux-gnu/libgcc_s.so.17fb1f1722000-7fb1f1921000 ---p 00016000 fd:01 918031 /lib/x86_64-linux-gnu/libgcc_s.so.17fb1f1921000-7fb1f1922000 rw-p 00015000 fd:01 918031 /lib/x86_64-linux-gnu/libgcc_s.so.17fb1f1922000-7fb1f1a94000 r-xp 00000000 fd:01 265161 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.217fb1f1a94000-7fb1f1c94000 ---p 00172000 fd:01 265161 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.217fb1f1c94000-7fb1f1c9e000 r--p 00172000 fd:01 265161 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.217fb1f1c9e000-7fb1f1ca0000 rw-p 0017c000 fd:01 265161 /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.217fb1f1ca0000-7fb1f1ca4000 rw-p 00000000 00:00 0 7fb1f1ca4000-7fb1f1cca000 r-xp 00000000 fd:01 925451 /lib/x86_64-linux-gnu/ld-2.23.so7fb1f1eb3000-7fb1f1eb9000 rw-p 00000000 00:00 0 7fb1f1ec8000-7fb1f1ec9000 rw-p 00000000 00:00 0 7fb1f1ec9000-7fb1f1eca000 r--p 00025000 fd:01 925451 /lib/x86_64-linux-gnu/ld-2.23.so7fb1f1eca000-7fb1f1ecb000 rw-p 00026000 fd:01 925451 /lib/x86_64-linux-gnu/ld-2.23.so7fb1f1ecb000-7fb1f1ecc000 rw-p 00000000 00:00 0 7ffdd4d0a000-7ffdd4d2b000 rw-p 00000000 00:00 0 [stack]7ffdd4def000-7ffdd4df2000 r--p 00000000 00:00 0 [vvar]7ffdd4df2000-7ffdd4df4000 r-xp 00000000 00:00 0 [vdso]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]Abortedroot@iZuf67on1pthsuih96udyfZ:~/GDB/20201014#發現運行的直接崩潰了~
就我們獲取下core文件,然后看下堆棧信息:
root@iZuf67on1pthsuih96udyfZ:~/GDB/20201014#?gdb?a.out?core?(gdb)?bt#0??0x00007ffff74aa428?in?__GI_raise?(sig=sig@entry=6)?at?../sysdeps/unix/sysv/linux/raise.c:54#1??0x00007ffff74ac02a?in?__GI_abort?()?at?abort.c:89#2??0x00007ffff74ec7ea?in?__libc_message?(do_abort=do_abort@entry=2,?fmt=fmt@entry=0x7ffff7605ed8?"***?Error?in?`%s':?%s:?0x%s?***")?at?../sysdeps/posix/libc_fatal.c:175#3??0x00007ffff74f537a?in?malloc_printerr?(ar_ptr=,?ptr=,?str=0x7ffff7605fa0?"double?free?or?corruption?(fasttop)",?action=3)?at?malloc.c:5006#4??_int_free?(av=,?p=,?have_lock=0)?at?malloc.c:3867#5??0x00007ffff74f953c?in?__GI___libc_free?(mem=)?at?malloc.c:2968#6??0x0000000000400caa?in?CTest::~CTest?(this=0x7fffffffe310,?__in_chrg=)?at?CopyConstruct.cpp:13#7??0x0000000000400b7a?in?main?()?at?CopyConstruct.cpp:23從堆棧信息大致可以得出,我們程序出現異常的是在23行,并且在析構的時候發生了異常;
所以得出結論:
不是所有的默認拷貝構造函數都是安全的,在我們不需要拷貝構造函數的時候,我們可以把默認拷貝構造函數禁止使用,這樣就不會出現因為默認拷貝構造函數導致難以尋找的BUG!!!
四、如何禁止拷貝構造函數
禁止拷貝構造構造函數有兩種方式,一般我們使用第一種,是最簡單的,也是C++11的新特性~
- 使用delete關鍵字 我們可以直接使用delete關鍵字進行禁止默認拷貝構造函數 public:
Epoll( const Epoll& ) = delete; - 使用NonCopyable 基類 class NonCopyable
{protected:
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
NonCopyable() = default;
~NonCopyable() = default;
}; 我們可以使用: class A : public NonCopyable {};
使用以上兩種方式,我們可以禁止默認拷貝函數,如果需要拷貝構造函數,我們可以自己進行編寫程序;
禁止默認拷貝構造,禁止BUG!
往期精彩匯總
GDB 多線程之旅
肝!動態規劃
C++使用鎖注意事項
嘔心瀝血的遞歸
muduo源碼剖析學習總結
windows程序崩潰調試終極武器
總結
以上是生活随笔為你收集整理的c++ 拷贝构造函数_禁止拷贝构造,禁止bug的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zigbee看门狗综合实验_2.5KW风
- 下一篇: python简单语句-7. 简单语句