文章目錄
- 一、容器思想
- 1、自定義類可拷貝 - 深拷貝與淺拷貝
- 2、自定義類可拷貝 - 代碼示例
- 3、自定義類可打印 - 左移運算符重載
- 二、代碼示例
- 1、Array.h 頭文件
- 2、Array.cpp 代碼文件
- 3、Test.cpp 主函數代碼文件
- 4、執行結果
一、容器思想
1、自定義類可拷貝 - 深拷貝與淺拷貝
上一篇博客 【C++】泛型編程 ? ( 類模板示例 - 數組類模板 | 構造函數和析構函數 的 聲明與實現 | 普通成員函數 的 聲明與實現 | 外部友元函數 的 聲明與實現 ) 中 , 實現了一個 數組 類模板 , 數組 中的 數據元素 是 泛型類型 , 可以是任意類型 ;
也就是說 , 該數組可以存儲 任意類型 的數據 , 包括 自定義類對象 ;
該數組 就是一個 數據的容器 ;
數組中 每個元素 插入數據時 , 其本質是一個 拷貝操作 , 數組 的 內存空間 在 聲明實際類型 以及 創建 時 , 就已經確定了 , 向數組中插入元素 , 就是將 已有的 數據 拷貝到 已經分配好的內存中 ;
向 數據容器 ( 數組 ) 中插入的數據 , 必須可以被 拷貝 , 如果 不能被拷貝 , 就會出現插入數據失敗的問題 ;
容器 中的 類型 可拷貝 , 就是要求 容器中的 數據類型 都是 值語義 , 不是 引用語義 ,
向 容器 中插入元素 , 就是拷貝 數據內容 到容器中 , 要將真實的值拷貝進去 , 不是將 引用地址 拷貝進去 ,
就是 深拷貝 和 淺拷貝 的問題 ;
下面的示例中 , 自定義類中的成員變量 char m_name[32] 是 在定義時 , 直接分配好的 ,
如果 自定義類 中有 指針類型的成員變量 , 如 char* m_name , 涉及到 動態分配內存 , 如果沒有定義 拷貝構造函數 , 默認的 拷貝構造函數 是 淺拷貝 函數 , 直接將 指針地址 簡單拷貝 , 這就是 不可被拷貝的情況 ;
那么多個 數組元素 就會共享 相同的 堆內存 數據 , 此時就會出現問題 ;
如果遇到了上述問題 , 定義了 char* m_name 成員變量 , 涉及到 動態分配內存 , 那么 該自定義類 必須自己實現 深拷貝 的 拷貝構造函數 ;
編寫的類 , 可以存儲到 數組類模板 容器 中 , 那么 該類 必須 支持 拷貝工作 , 具體一些就是 深拷貝 工作 ;
2、自定義類可拷貝 - 代碼示例
下面簡單實現一個類 , 該類中維護了 2 個成員變量 , char m_name[32] 數組變量 和 int m_age 變量 , 這兩個 成員 都是在 創建時 就會分配內存空間 , 不存在 深拷貝問題 ;
如果 char m_name[32] 數組變量 改為 char* m_name 指針變量 , 就需要考慮 深拷貝問題了 ;
class Student
{
public:Student(){m_age = 10;strcpy(m_name, "NULL");}Student(const char* name, int age) {strcpy(this->m_name, name);this->m_age = age;}void printT() {cout << "name : " << m_name << " , age : " << m_age << endl;}private:char m_name[32];int m_age;
};
3、自定義類可打印 - 左移運算符重載
數組類模板 中 , 實現了 左移運算符 打印日志 , 如果 數組中 存儲 自定義類對象 想要通過 cout 打印出來 , 那么 該自定義類 必須 進行 左移運算符重載操作 ;
聲明 左移運算符重載函數 :
class Student
{friend ostream& operator<<(ostream& out, const Student& s);
}
實現 左移運算符重載函數 :
// 重載左移運算符實現
ostream& operator<<(ostream& out, const Student& s) {out << "name : " << s.m_name << " , age : " << s.m_age << " ; ";return out;
}
二、代碼示例
1、Array.h 頭文件
#pragma once#include "iostream"
using namespace std;template <typename T>
class Array
{// 左移 << 操作符重載// 注意 聲明時 , 需要在 函數名 和 參數列表之間 注明 泛型類型 <T>// 實現時 , 不能在 函數名 和 參數列表之間 注明 泛型類型 <T>friend ostream& operator<< <T> (ostream& out, const Array& a);public:// 有參構造函數Array(int len = 0);// 拷貝構造函數Array(const Array& array);// 析構函數~Array();public:// 數組下標 [] 操作符重載// 數組元素類型是 T 類型T& operator[](int i);// 等號 = 操作符重載Array& operator=(const Array& a);private:// 數組長度int m_length;// 指向數組數據內存 的指針// 指針類型 是 泛型類型 TT* m_space;
};
2、Array.cpp 代碼文件
#include "Array.h"// 左移 << 操作符重載
// 注意 聲明時 , 需要在 函數名 和 參數列表之間 注明 泛型類型 <T>
// 實現時 , 不能在 函數名 和 參數列表之間 注明 泛型類型 <T>
template <typename T>
ostream& operator<< (ostream& out, const Array<T>& a)
{for (int i = 0; i < a.m_length; i++){// 在一行內輸入數據, 使用空格隔開, 不換行out << a.m_space[i] << " ";}// 換行out << endl;return out;
}// 有參構造函數
template <typename T>
Array<T>::Array(int len)
{// 設置數組長度m_length = len;// 為數組在堆內存中分配內存// 注意 元素類型為 Tm_space = new T[m_length];cout << " 調用有參構造函數 " << endl;
}// 拷貝構造函數
// 這是一個深拷貝 拷貝構造函數
template <typename T>
Array<T>::Array(const Array<T>& array)
{// 設置數組長度m_length = array.m_length;// 創建數組// 注意 元素類型為 Tm_space = new T[m_length];// 為數組賦值for (int i = 0; i < m_length; i++){m_space[i] = array.m_space[i];}cout << " 調用拷貝構造函數 " << endl;
}// 析構函數
template <typename T>
Array<T>::~Array()
{if (m_space != NULL){// 釋放 new T[m_length] 分配的內存 delete[] m_space;m_space = NULL;m_length = 0;}cout << " 調用析構函數 " << endl;
}// 數組下標 [] 操作符重載
template <typename T>
T& Array<T>::operator[](int i)
{return m_space[i];
}// 等號 = 操作符重載
template <typename T>
Array<T>& Array<T>::operator=(const Array<T>& a)
{if (this->m_space != NULL){// 釋放 new int[m_length] 分配的內存 delete[] this->m_space;this->m_space = NULL;}// 設置數組長度this->m_length = a.m_length;// 創建數組this->m_space = new T[m_length];// 為數組賦值for (int i = 0; i < m_length; i++){this->m_space[i] = a.m_space[i];}cout << " 調用 等號 = 操作符重載 函數" << endl;// 返回是引用類型// 返回引用就是返回本身// 將 this 指針解引用, 即可獲取數組本身return *this;
}
3、Test.cpp 主函數代碼文件
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std; // 此處注意, 類模板 聲明與實現 分開編寫
// 由于有 二次編譯 導致 導入 .h 頭文件 類模板函數聲明 無法找到 函數實現
// 必須 導入 cpp 文件
#include "Array.cpp"class Student
{friend ostream& operator<<(ostream& out, const Student& s);
public:Student(){m_age = 10;strcpy(m_name, "NULL");}Student(const char* name, int age) {strcpy(this->m_name, name);this->m_age = age;}void printT() {cout << "name : " << m_name << " , age : " << m_age << endl;}private:char m_name[32];int m_age;
};// 重載左移運算符實現
ostream& operator<<(ostream& out, const Student& s) {out << "name : " << s.m_name << " , age : " << s.m_age << " ; ";return out;
}int main() {// 驗證 有參構造函數Array<Student> array(3);Student s0("Tom", 18), s1("Jerry", 12), s2("Jack", 16);array[0] = s0;array[1] = s1;array[2] = s2;// 遍歷數組 打印數組元素for (int i = 0; i < 3; i++) {array[i].printT();}cout << array << endl;// 控制臺暫停 , 按任意鍵繼續向后執行system("pause");return 0;
}
4、執行結果
執行結果 :
調用有參構造函數
name : Tom , age : 18
name : Jerry , age : 12
name : Jack , age : 16
name : Tom , age : 18 ; name : Jerry , age : 12 ; name : Jack , age : 16 ;Press any key to continue . . .