何時須要重載賦值操做符?
編譯器是否提供默認的賦值操做符?ios
- 編譯器爲每一個類默認重載了賦值操做符
- 默認的賦值操做符僅完成淺拷貝
- 當須要進行深拷貝時必須重載賦值操做符
- 賦值操做符與拷貝構造函數有相同的存在乎義
test_1.cpp面試
#include <iostream> #include <string> using namespace std; class Test { int* m_pointer; public: Test() { m_pointer = NULL; } Test(int i) { m_pointer = new int(i); } void print() { cout << "m_pointer = " << hex << m_pointer << endl; } ~Test() { delete m_pointer; } }; int main() { Test t1 = 1; Test t2; t2 = t1; t1.print(); t2.print(); return 0; }
輸出:【運行時發生內存錯誤】 m_pointer = 0x942f008 m_pointer = 0x942f008 *** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0942f008 *** ...... 已放棄 現象: t1.m_pointer 與 t2.m_pointer 指向同一片內存空間
修正: test_2.cpp編程
#include <iostream> #include <string> using namespace std; class Test { int* m_pointer; public: Test() { m_pointer = NULL; } Test(int i) { m_pointer = new int(i); } Test(const Test& obj) { m_pointer = new int(*obj.m_pointer); } Test& operator = (const Test& obj) { if( this != &obj ) { delete m_pointer; m_pointer = new int(*obj.m_pointer); } return *this; } void print() { cout << "m_pointer = " << hex << m_pointer << endl; } ~Test() { delete m_pointer; } }; int main() { Test t1 = 1; Test t2; t2 = t1; t1.print(); t2.print(); return 0; }
輸出: m_pointer = 0x83ac008 m_pointer = 0x83ac018 現象: t1.m_pointer 與 t2.m_pointer 指向不一樣的內存空間
重載賦值操做符,必然須要實現深拷貝!數組
Test& operator = (const Test& obj) { if( this != &obj ) { // ... } return *this; }
IntArray.happ
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; IntArray(int len); IntArray(const IntArray& obj); bool construct(); public: static IntArray* NewInstance(int length); int length(); bool get(int index, int& value); bool set(int index, int value); int& operator [] (int index); IntArray& operator = (const IntArray& obj); IntArray& self(); ~IntArray(); }; #endif
IntArray.cpp函數
#include "IntArray.h" IntArray::IntArray(int len) { m_length = len; } bool IntArray::construct() { bool ret = true; m_pointer = new int[m_length]; if( m_pointer ) { for(int i=0; i<m_length; i++) { m_pointer[i] = 0; } } else { ret = false; } return ret; } IntArray* IntArray::NewInstance(int length) { IntArray* ret = new IntArray(length); if( !(ret && (ret->construct())) ) { delete ret; ret = 0; } return ret; } int IntArray::length() { return m_length; } bool IntArray::get(int index, int& value) { bool ret = (index >= 0) && (index < length()); if( ret ) { value = m_pointer[index]; } return ret; } bool IntArray::set(int index, int value) { bool ret = (index >= 0) && (index < length()); if( ret ) { m_pointer[index] = value; } return ret; } int& IntArray::operator [] (int index) { return m_pointer[index]; } IntArray& IntArray::operator = (const IntArray& obj) { if( this != &obj ) { int* pointer = new int[obj.m_length]; if( pointer ) { for(int i=0; i<obj.m_length; i++) { pointer[i] = obj.m_pointer[i]; } m_length = obj.m_length; delete m_pointer; m_pointer = pointer; } } return *this; } IntArray& IntArray::self() { return *this; } IntArray::~IntArray() { delete[] m_pointer; }
main.cpp優化
#include <iostream> #include "IntArray.h" using namespace std; int main() { IntArray* a = IntArray::NewInstance(5); IntArray* b = IntArray::NewInstance(10); if( a && b ) { IntArray& array = a->self(); IntArray& brray = b->self(); cout << "array.length() = " << array.length() << endl; cout << "brray.length() = " << brray.length() << endl; array = brray; cout << "array.length() = " << array.length() << endl; cout << "brray.length() = " << brray.length() << endl; } delete a; delete b; return 0; }
輸出: array.length() = 5 brray.length() = 10 array.length() = 10 brray.length() = 10
class T { };
==>this
class T { public: T(); T(const T&); T& operator = (const T&); ~T(); };
- 下面的代碼輸出什麼?爲何?
void code() { string s = "12345"; const char* p = s.c_str(); cout << p << endl; s.append("abcde"); cout << p << endl; }
#include <iostream> #include <string> using namespace std; int main() { string s = "12345"; const char* p = s.c_str(); cout << p << endl; s.append("abcde"); cout << p << endl; // p 成爲了野指針! return 0; }
輸出: 12345 12345
【string 對象內部維護了一個指向數據(位於堆空間中)的 char* 指針,這個指針可能在運行的過程當中發生改變】spa
s = "12345"; ==> s 對象在堆空間申請內存並將字符串"12345"拷貝進入,同時內部指針指向這一空間 (0xFF112233)
p = s.c_str(); ==> p 指針指向保存數據的內存空間 (0xFF112233)
s.append("abcde"); ==> s 對象從新在堆空間申請內存,並把兩部分字符串拷貝進入,原有的堆空間被釋放(0xFF112233),內部指針指向新的內存空間 (0xFF445566)指針
- 下面的程序輸出什麼?爲何?
void code() { const char* p = "12345"; string s = ""; s.reserve(10); for(int i=0; i<5; i++) { s[i] = p[i]; } cout << "s.length : " << s.length() << endl; cout << "s.empty : " << s.empty() << endl; cout << "s : " << s << endl; }
#include <iostream> #include <string> using namespace std; int main() { const char* p = "12345"; string s = ""; s.reserve(10); // 將 s 的容量調整爲 10 for(int i=0; i<5; i++) // 不要使用 C 語言中的方式操做 C++ 中的字符串 { s[i] = p[i]; } cout << "s.length : " << s.length() << endl; cout << "s.empty : " << s.empty() << endl; cout << "s : " << s << endl; return 0; }
輸出: s.length : 0 s.empty : 1 s :
for 循環前:
m_cstr 指向10個字節的堆空間(0xFF010203)
m_length 字符長度爲 0 (空串)
for 循環後:
m_cstr 指向 10 個字節的堆空間前 5 個字節發生改變(0xFF010203)
m_length 字符長度爲 0 (空串)
警告:
在 C++ 中儘可能只採用 C++ 編程思想,而不採用混合方式開發,不然可能產生意想不到的問題
- 在須要進行深拷貝的時候必須重載賦值操做符
- 賦值操做符和拷貝構造函數有同等重要的意義
- string 類經過一個數據空間保存字符數據
- string 類經過一個成員變量保存當前字符串的長度
- C++ 開發時儘可能避開 C 語言中慣用的編程思想
以上內容參考狄泰軟件學院系列課程,請你們保護原創!