無參構造函數面試
- 沒有參數的構造函數
拷貝構造函數編程
- 參數爲 const class_name& 的構造函數
無參構造函數segmentfault
- 當類中沒有定義構造函數時,編譯器提供一個無參構造函數,而且函數體爲空
拷貝構造函數數組
- 當類中沒有定義拷貝構造函數時,編譯器默認提供一個拷貝構造函數,簡單的進行成員變量的值複製
Test_1.cpp網絡
#include <stdio.h> class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } }; int main() { Test t1; Test t2; printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ()); printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ()); return 0; }
第一次輸出: t1.i = 134514464, t1.j = -1081276856 t2.i = 2528036, t2.j = 2527220 第二次輸出: t1.i = 134514464, t1.j = -1081626328 t2.i = 3298084, t2.j = 3297268 第三次輸出: t1.i = 134514464, t1.j = -1080045768 t2.i = 9716516, t2.j = 9715700 分析: 類中沒有定義構造函數,編譯器提供一個無參構造函數,函數體爲空,沒法對成員變量初始值。打印棧空間中的隨機值。 編譯器在類中未發現構造函數,會在類最後添加無參構造函數: Test() { }
test_2.cpp函數
#include <stdio.h> class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } }; int main() { Test t1; Test t2 = t1; // 注意這裏! printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ()); printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ()); return 0; }
第一次輸出: t1.i = 134514480, t1.j = -1081214552 t2.i = 134514480, t2.j = -1081214552 第二次輸出: t1.i = 134514480, t1.j = -1082006488 t2.i = 134514480, t2.j = -1082006488 第三次輸出: t1.i = 134514480, t1.j = -1077369784 t2.i = 134514480, t2.j = -1077369784 分析:類中沒有定義拷貝構造函數,編譯器提供一個拷貝構造函數,簡單的進行成員變量的值複製。 t1.i == t2.i t1.i == t2.i 編譯器在類中未發現拷貝構造函數,會在類最後添加簡單的拷貝構造函數: Test(const Test& t) { i = t.i; j = t.j; }
class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } };
<==>spa
class Test { private: int i; int j; public: int getI() { return i; } int getJ() { return j; } Test() // 無參構造函數,函數體爲空 { } Test(const Test& t) // 拷貝構造函數,進行簡單的成員變量值複製 { i = t.i; j = t.j; } };
class T { };
==>code
class T { public: T(); T(const T&); T& operator = (const T&); ~T(); };
問: T 中包含什麼?
答: 無參構造函數、拷貝構造函數、析構函數、賦值操做符重載函數。對象
- 兼容 C 語言的初始化方式
- 初始化行爲符合預期的邏輯
淺拷貝blog
- 拷貝後對象的物理狀態相同(兩對象佔用的內存空間中,每一個字節的值相等)
深拷貝
- 拷貝後對象的邏輯狀態相同
編譯器提供的拷貝構造函數只進行淺拷貝!
淺拷貝的現象:test_1.cpp
#include <stdio.h> class Test { private: int i; int j; int* p; public: int getI() { return i; } int getJ() { return j; } int* getP() { return p; } Test(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; } }; int main() { Test t1(3); Test t2 = t1; // ==> Test t2(t1); printf("t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf("t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP(), *t2.getP()); return 0; }
輸出: t1.i = 1, t1.j = 2, t1.p = 0x950c008, *t1.p = 3 t2.i = 1, t2.j = 2, t2.p = 0x950c008, *t2.p = 3 分析: 類中沒有定義拷貝構造函數,編譯器提供一個拷貝構造函數,簡單的進行成員變量的複製。 t1.p = 0x950c008,t2.p = 0x950c008 指向同一內存空間,而沒有爲 t2.p 從新分配空間,不是指望結果。
淺拷貝的問題: test_2.cpp
#include <stdio.h> class Test { private: int i; int j; int* p; public: int getI() { return i; } int getJ() { return j; } int* getP() { return p; } Test(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; } }; int main() { Test t1(3); Test t2 = t1; // ==> Test t2(t1); printf("t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf("t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP(), *t2.getP()); t1.free(); // 注意這裏! t2.free(); // 注意這裏! return 0; }
輸出: 內存錯誤
深拷貝實例: test_3.cpp
#include <stdio.h> class Test { private: int i; int j; int* p; public: int getI() { return i; } int getJ() { return j; } int* getP() { return p; } Test(int v) { i = 1; j = 2; p = new int; *p = v; } Test(const Test& t) { i = t.i; j = t.j; p = new int; // 深拷貝,從新申請內存空間 *p = *t.p; } void free() { delete p; } }; int main() { Test t1(3); Test t2 = t1; // ==> Test t2(t1); printf("t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf("t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP(), *t2.getP()); t1.free(); t2.free(); return 0; }
輸出: t1.i = 1, t1.j = 2, t1.p = 0x927b008, *t1.p = 3 t2.i = 1, t2.j = 2, t2.p = 0x927b018, *t2.p = 3 分析: t1.p = 0x927b008, t2.p = 0x927b018 指向不一樣的內存空間。指向的內存空間中的值相同。
對象中有成員指代了系統中的資源
- 成員指向了動態內存空間
- 成員打開了外存中的文件
- 成員使用了系統中的網絡端口
- 。。。。。
淺拷貝只進行簡單的成員變量值複製,因此發生 t2.m_pointer=t1.m_pointer;
兩對象成員變量指向同一片內存空間而沒有發生新的內存申請,在 free 時,對同一片內存空間釋放兩次,致使運行時內存出錯。
- 自定義拷貝構造函數,必須須要實現深拷貝!!
IntArray.h
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; public: IntArray(int len); IntArray(const IntArray& obj); int length(); bool get(int index, int& value); bool set(int index, int value); void free(); }; #endif
IntArray.cpp
#include "IntArray.h" IntArray::IntArray(int len) { m_pointer = new int[len]; for(int i=0; i<len; i++) { m_pointer[i] = 0; } m_length = len; } IntArray::IntArray(const IntArray& obj) { m_length = obj.m_length; m_pointer = new int[obj.m_length]; for(int i=0; i<obj.m_length; i++) { m_pointer[i] = obj.m_pointer[i]; } } 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; } void IntArray::free() { delete[] m_pointer; }
main.cpp
#include <stdio.h> #include "IntArray.h" int main() { IntArray a(5); for(int i=0; i<a.length(); i++) { a.set(i, i+1); } for(int i=0; i<a.length(); i++) { int value = 0; if( a.get(i, value) ) { printf("a.[%d] = %d\n", i, value); } } IntArray b = a; for(int i=0; i<b.length(); i++) { int value = 0; if( b.get(i, value) ) { printf("b.[%d] = %d\n", i, value); } } a.free(); b.free(); return 0; }
輸出: a.[0] = 1 a.[1] = 2 a.[2] = 3 a.[3] = 4 a.[4] = 5 b.[0] = 1 b.[1] = 2 b.[2] = 3 b.[3] = 4 b.[4] = 5
- C++ 編譯器會默認提供構造函數
- 無參構造函數用於定義對象的默認初始狀態
- 拷貝構造函數在建立對象時拷貝對象的狀態
對象的拷貝有淺拷貝和深拷貝兩種方式
- 淺拷貝使得對象的物理狀態相同
- 深拷貝使得對象的邏輯狀態相同
以上內容參考狄泰軟件學院系列課程,請你們保護原創!