C++之泛型編程(模板)

1.模板綜述

背景

  • 有時候許多函數或子程序的邏輯結構是一樣的,只是要處理的數據類型不一樣
  • 有時候多個類具有相同邏輯的成員函數和成員變量,只是成員變量的數據類型以及成員函數的參數類型不一樣
  • 模板就是解決數據類型不一致造成代碼冗余的一種機制,本質上就是數據類型參數化,用一種邏輯結構抽象出多種數據類型對應的函數或者類

2.函數模板

2.1基礎語法

示例代碼

#include <iostream>
using namespace std;template <typename T>//模板說明
T myFunc(T size)//函數實現
{cout << "size:" <<size<< endl;return size;
}int main(void)
{myFunc(8);//自動推導調用myFunc<float>((float)12.9);//顯示調用cout << "Hello!" << endl;//system("pause");return 0;
}
  • 模板說明里面的類屬參數在函數定義里面一定要使用,普通類型可以不使用
  • 可以使用多個類型參數進行模板說明
#include <iostream>
using namespace std;template <typename T1, typename T2>//模板說明,兩個類型參數
T2 mySort(T1 * array,T2 length)
{T2 i = 0, j = 0;T1 tmp = array[0];for (i = 0; i < length; i++){for (j = i + 1; j < length; j++){if (array[i] < array[j]){tmp = array[i];array[i] = array[j];array[j] = tmp;}}}return i;
}template <typename T1,typename T2>//模板說明
T2 printArray(T1 *array,T2 length)//函數實現
{T2 i = 0;for (i = 0; i < length; i++){cout << (int)array[i]<<""<< endl;}cout << endl;return i;
}int main(void)
{char myArray[10] = {1,3,87,54,98,37,33,63,89,2};int size = sizeof(myArray) / (*myArray);mySort(myArray, size);printArray(myArray, size);cout << "Hello!" << endl;//system("pause");return 0;
}

2.2函數模板遇上函數重載

結論:
函數模板不允許自動類型轉化
普通函數能夠進行自動類型轉換


調用規則
1 函數模板可以像普通函數一樣被重載
2 C++編譯器優先考慮普通函數
3 如果函數模板可以產生一個更好的匹配,那么選擇模板
4 可以通過空模板實參列表的語法限定編譯器只通過模板匹配

/*
函數模板不允許自動類型轉化
普通函數能夠進行自動類型轉換
*//*1 函數模板可以像普通函數一樣被重載2 C++編譯器優先考慮普通函數3 如果函數模板可以產生一個更好的匹配,那么選擇模板4 可以通過空模板實參列表的語法限定編譯器只通過模板匹配
*/#include "iostream"
using namespace std;int Max(int a, int b)
{cout<<"int Max(int a, int b)"<<endl;return a > b ? a : b;
}template<typename T>
T Max(T a, T b)
{cout<<"T Max(T a, T b)"<<endl;return a > b ? a : b;
}template<typename T>
T Max(T a, T b, T c)
{cout<<"T Max(T a, T b, T c)"<<endl;return Max(Max(a, b), c);
}void main()
{int a = 1;int b = 2;cout<<Max(a, b)<<endl; //當函數模板和普通函數都符合調用時,優先選擇普通函數cout<<Max<>(a, b)<<endl; //若顯示使用函數模板,則使用<> 類型列表cout<<Max(3.0, 4.0)<<endl; //如果 函數模板產生更好的匹配 使用函數模板cout<<Max(5.0, 6.0, 7.0)<<endl; //重載cout<<Max('a', 100)<<endl;  //調用普通函數 可以隱式類型轉換 system("pause");return ;
}

2.3函數模板實現機制

編譯器介紹

  • gcc(GNU C Compiler)編譯器的作者是RichardStallman,也是GNU項目的奠基者。
  • 什么是gcc:gcc是GNU Compiler Collection的縮寫。最初是作為C語言的編譯器(GNU C Compiler),現在已經支持多種語言了,如C、C++、Java、Pascal、Ada、COBOL語言等。
  • gcc支持多種硬件平臺,甚至對Don Knuth 設計的 MMIX 這類不常見的計算機都提供了完善的支持

gcc主要特征

1)gcc是一個可移植的編譯器,支持多種硬件平臺
2)gcc不僅僅是個本地編譯器,它還能跨平臺交叉編譯。
3)gcc有多種語言前端,用于解析不同的語言。
4)gcc是按模塊化設計的,可以加入新語言和新CPU架構的支持
5)gcc是自由軟件

gcc編譯過程

  • 預處理(Pre-Processing)
  • 編譯(Compiling)
  • 匯編(Assembling)
  • 鏈接(Linking)
Gcc *.c –o 1exe (總的編譯步驟)
Gcc –E 1.c –o 1.i  //宏定義 宏展開
Gcc –S 1.i –o 1.s 
Gcc –c 1.s –o 1.o  
Gcc 1.o –o 1exe

結論:gcc編譯工具是一個工具鏈。。。。

GCC常用編譯選項

選項作用
-o產生目標(.i、.s、.o、可執行文件等)
-c通知gcc取消鏈接步驟,即編譯源碼并在最后生成目標文件
-E只運行C預編譯器
-S告訴編譯器產生匯編語言文件后停止編譯,產生的匯編語言文件擴展名為.s
-Wall使gcc對源文件的代碼有問題的地方發出警告
-Idir將dir目錄加入搜索頭文件的目錄路徑
-Ldir將dir目錄加入搜索庫的目錄路徑
-llib鏈接lib庫
-g在目標文件中嵌入調試信息,以便gdb之類的調試程序調試

GCC常用編譯步驟

1.gcc -E hello.c -o hello.i(預處理)
2.gcc -S hello.i -o hello.s(編譯)
3.gcc -c hello.s -o hello.o(匯編)
4.gcc hello.o -o hello(鏈接)
以上四個步驟,可合成一個步驟
gcc hello.c -o hello(直接編譯鏈接成可執行目標文件)
gcc -c hello.c或gcc -c hello.c -o hello.o(編譯生成可重定位目標文件)

Gcc編譯多個文件

hello_1.h
hello_1.c
main.c

一次性編譯

gcc  hello_1.c main.c –o newhello

獨立編譯

gcc -Wall -c main.c -o main.o
gcc -Wall -c hello_1.c -o hello_fn.o
gcc -Wall main.o hello_1.o -o newhello

反匯編觀察

  • 編譯器并不是把函數模板處理成能夠處理任意類的函數
  • 編譯器從函數模板通過具體類型產生不同的函數
  • 編譯器會對函數模板進行兩次編譯
    • 在聲明的地方對模板代碼本身進行編譯;
    • 在調用的地方對參數替換后的代碼進行編譯。

3.類模板

類模板與函數模板的定義和使用類似,我們已經進行了介紹。 有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同,如下面語句聲明了一個類:

類模板用于實現類所需數據的類型參數化


類模板在表示如數組、表、圖等數據結構顯得特別重要,
這些數據結構的表示和算法不受所包含的元素類型的影響


使用類模板聲明對象的時候要顯示指定形式參數的具體類型,以便C++編譯器給對象分配具體的內存

3.1普通類模板的語法

單個類的語法

#include <iostream>
using namespace std;template <typename T>
class A
{
public:A(int a = 0){this->a = a;}void printA(){cout << a << endl;}
protected:
private:T a;
};int main(void)
{A<int> a1;a1.printA();A<int> a2(19);a2.printA();cout<<"Hello!"<<endl;return 0;
}

模板類作函數參數

#include <iostream>
using namespace std;template <typename T>
class A
{
public:A(int a = 0){this->a = a;}void printA(){cout << a << endl;}
protected:
private:T a;
};//類模板 做函數參數//參數 ,C++編譯器 要求具體的類 所以所 要 A<int> &a 
void UseA(A<int> &a)
{a.printA();
}int main(void)
{A<int> a1;UseA(a1);A<int> a2(19);UseA(a2);cout<<"Hello!"<<endl;return 0;
}

3.2繼承中的類模板語法

模板類派生時, 需要具體化模板類. C++編譯器需要知道 父類的數據類型具體是什么樣子的。要知道父類所占的內存大小是多少。只有數據類型固定下來,才知道如何分配內存。

模板類派生時, 需要具體化模板類. C++編譯器需要知道 父類的數據類型具體是什么樣子的。要知道父類所占的內存大小是多少。只有數據類型固定下來,才知道如何分配內存。


從模板類派生普通類

#include <iostream>
using namespace std;template <typename T>
class A
{
public:A(int a = 0){this->a = a;}void printA(){cout << a << endl;}
protected:T a;
private:};class B:public A<int>
{
public:B(int a = 10, int b = 20) : A<int>(a){this->b = b;}void printB(){cout << "a:" << a << " b: " << b << endl;}
protected:private:int b;
};int main(void)
{B  b1(1, 2);b1.printB();cout<<"Hello!"<<endl;return 0;
}

從模板類派生模板類

#include <iostream>
using namespace std;template <typename T>
class A
{
public:A(int a = 0){this->a = a;}void printA(){cout << a << endl;}
protected:T a;
private:};template <typename T>class C :public A<T>
{
public:C(T a, T c) :A<T>(a){this->c = c;}void printC(){cout << "a:" << a << "c:" << c << endl;}
private:T c;
protected:
};int main(void)
{C<int> c1(1, 2);c1.printC();cout<<"Hello!"<<endl;return 0;
}

3.2類模板知識體系梳理

類模板函數全部寫在類的內部

#include <iostream>
using namespace std;template <typename T>
class Complex
{friend Complex MySub(Complex c1, Complex c2){Complex tmp(c1.a-c2.a,c1.b-c2.b);return tmp;}friend ostream & operator<<(ostream & out, Complex & c){out << c.a << " + " << c.b << "i" << endl;return out;}
public:Complex(T a, T b){this->a = a;this->b = b;}Complex operator+(Complex & c2){Complex tmp(a + c2.a,b + c2.b);return tmp;}void printCom(){cout << a << " + " << b << "i" << endl;}
protected:
private:T a;T b;
};int main(void)
{//需要把模板類 進行具體化以后  才能定義對象  C++編譯器要分配內存Complex<int>    c1(1, 2);Complex<int>    c2(3, 4);Complex<int> c3 = c1 + c2;//c3.printCom();cout << c3 << endl;//濫用友元函數{Complex<int> c4 = MySub(c1, c2);cout << c4 << endl;}cout<<"Hello!"<<endl;return 0;
}

類模板函數全寫在類的外部,但在同一個cpp中

如果一個模板類具有友元函數,且該友元函數的形參包含模板類對象,則需要進行類模板和友元函數的前置聲明。
需要注意的是:

  1. 模板類和模板類里面的友元函數需要進行前置聲明
  2. 友元函數在前置聲明的時候,函數名后面不指定具體的數據類型,但是在模板類里面聲明的時候,需要在函數名后面緊跟具體的數據類型,比如<T>
  3. 模板類的成員函數在類的外面實現的時候,需要注意參數列表,函數名(類作用域)以及返回類型是否需要強行指定具體的數據類型,以便編譯器確定分配內存
  4. 友元函數在類的外部實現的時候,不需要管函數名前面的類作用域以及函數名后面的具體數據類型,只需要注意參數列表以及返回值是否需要指定數據類型即可
  5. 所有函數在類的外部實現的時候,其函數名后面都不用管具體的數據類型。
  6. 友元函數調用的時候,需要在函數名后面緊跟具體數據類型。言外之意,前置聲明和外部實現的格式一樣,類的內部聲明和調用的時候,函數名后面都要具體的數據類型。
#include <iostream>
using namespace std;template <typename T>//類的前置聲明
class Complex;template <typename T>//友元函數的前置聲明
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2);//友元函數前置聲明時不需要在函數名后面指定具體數據類型template <typename T>
ostream& operator<<(ostream& out, Complex<T>& c);//友元函數前置聲明時不需要在函數名后面指定具體數據類型template <typename T>
class Complex
{friend Complex<T> MySub<T>(Complex<T>& c1, Complex<T>& c2); //在模板類里面聲明友元函數的時候,需要在函數名后面緊跟具體的數據類型,比如<T>friend ostream & operator<<<T>(ostream & out, Complex<T> & c);//在模板類里面聲明友元函數的時候,需要在函數名后面緊跟具體的數據類型,比如<T>/*friend ostream & operator<<(ostream & out, Complex<T> & c);如果operator<<后面沒有<T>則會報錯,所以模板類里面存在友元函數的時候,除了進行友元函數的前置聲明意外,還需要在模板類里面進行函數名數據類型具體化,即指定具體的typename參數類型緊跟在函數名后面。*/
public:Complex(T a, T b);Complex operator+(Complex & c2);void printCom();
protected:
private:T a;T b;
};/*
模板類的成員函數在類的外面實現的時候,
需要注意參數列表,函數名(類作用域)以及返回類型是否需要強行指定具體的數據類型,
以便編譯器確定分配內存
*/
template <typename T>
Complex<T>::Complex(T a, T b)
{this->a = a;this->b = b;
}template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> & c2)
{Complex<T> tmp(a + c2.a, b + c2.b);return tmp;
}template <typename T>
void Complex<T>::printCom()
{cout << a << " + " << b << "i" << endl;
}/*
友元函數在類的外部實現的時候,
不需要管函數名前面的類作用域以及函數名后面的具體數據類型,
只需要注意參數列表以及返回值是否需要指定數據類型即可
*/
template <typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2)
{Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);return tmp;
}template <typename T>
ostream & operator<<(ostream & out, Complex<T> & c)
{out << c.a << " + " << c.b << "i" << endl;return out;
}int main(void)
{//需要把模板類 進行具體化以后  才能定義對象  C++編譯器要分配內存Complex<int>    c1(1, 2);Complex<int>    c2(3, 4);Complex<int> c3 = c1 + c2;//c3.printCom();cout << c3 << endl;//濫用友元函數{Complex<int> c4 = MySub<int>(c1, c2);//友元函數調用的時候,需要在函數名后面緊跟具體數據類型cout << c4 << endl;}cout<<"Hello!"<<endl;return 0;
}

結論:不要濫用友元函數,一般友元函數只適用于重載<<或者>>操作符。

類模板函數全寫在類的外部,但在不同的.h和cpp中

由于模板的實現機制在本質上是兩次編譯,所以如果只在主程序里面包含頭文件(類模板的聲明),編譯器不會自動尋找cpp文件里面的成員函數和友元函數的函數體。所以會出現找不到某個函數體的錯誤。只能是包含實現函數體的cpp文件,而cpp文件又包含了h文件,所以實質上是包含了類模板的聲明以及類模板函數的實現,故業界都是將這兩部分(.h和.cpp)寫在同一個文件中,叫做hpp文件,只需要在提供的開源庫里面包含該hpp文件,即可使用類模板。

  • 傳統類的頭文件(類模板聲明部分)
#pragma  once#include <iostream>
using namespace std;template <typename T>
class Complex
{friend ostream & operator<< <T> (ostream &out, Complex &c3);public:Complex(T a, T b);void printCom();Complex operator+ (Complex &c2);private:T   a;T   b;
};
  • 傳統類的實現部分(具體類模板函數的實現部分)
#include <iostream>
using namespace std;
#include "complex.h"//構造函數的實現 寫在了類的外部
template <typename T>
Complex<T>::Complex(T a, T b)
{this->a = a;this->b = b;
}template <typename T>
void Complex<T>::printCom()
{cout << "a:" << a << " b: " << b << endl;
}template <typename T>
Complex<T>  Complex<T>::operator+ (Complex<T> &c2)
{Complex tmp(a + c2.a, b + c2.b);return tmp;
}template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c3)
{out << c3.a << " + " << c3.b << "i" << endl;return out;
}
  • 測試程序(包含hpp文件)
#include <iostream>
using namespace std;
#include "complex.cpp"void main()
{//需要把模板類 進行具體化以后  才能定義對象  C++編譯器要分配內存Complex<int>    c1(1, 2);Complex<int>    c2(3, 4);Complex<int> c3 = c1 + c2;cout << c3 << endl;cout << "hello..." << endl;return;
}

3.3類模板中的static關鍵字

  • 類模板—>實例化—>模板類
    每一個模板類有自己的類模板數據成員,該模板類的所有對象共享一個static數據成員
  • 和非模板類的static數據成員一樣,模板類的static數據成員也應該在文件范圍定義和初始化
  • 每個模板類有自己的類模板的static數據成員副本
/*編譯器并不是把函數模板處理成能夠處理任意類的函數編譯器從函數模板通過具體類型產生不同的函數編譯器會對函數模板進行兩次編譯在聲明的地方對模板代碼本身進行編譯;在調用的地方對參數替換后的代碼進行編譯。
*/#include <iostream>
using namespace std;template <typename T>
class AA
{
public:static T m_a;
protected:
private:
};template <typename T>
T AA<T>::m_a  = 0;class AA1
{
public:static int m_a;
protected:
private:
};int AA1::m_a  = 0;class AA2
{
public:static char m_a;
protected:
private:
};
char AA2::m_a  = 0;void main()
{AA<int> a1, a2, a3;a1.m_a = 10;a2.m_a ++;a3.m_a ++;cout << AA<int>::m_a << endl;AA<char> b1, b2, b3;b1.m_a = 'a';b2.m_a ++;b2.m_a ++ ;cout << AA<char>::m_a << endl;//m_a 應該是 每一種類型的類 使用自己的m_acout<<"hello..."<<endl;system("pause");return ;
}

3.4類模板小結

類模板的聲明

1.先寫出一個實際的類。由于其語義明確,含義清楚,一般不會出錯。
2.將此類中準備改變的類型名(如int要改變為float或char)改用一個自己指定的虛擬類型名(如上例中的numtype)。
3.在類聲明前面加入一行,格式為(class和typename作用一樣):
template <class虛擬類型參數>
如:

 template <class numtype> //注意本行末尾無分號class Compare{…}; //類體

4.用類模板定義對象時用以下形式:
類模板名<實際類型名> 對象名;
類模板名<實際類型名> 對象名(實參表列);
如:

 Compare<int> cmp;Compare<int> cmp(3,7);

5.如果在類模板外定義成員函數,應寫成類模板形式:
template <class 虛擬類型參數>
函數類型 類模板名<虛擬類型參數>::成員函數名(函數形參表列) {…}
6.類模板的類型參數可以有一個或多個,每個類型前面都必須加class或者typename,如:

template <class T1,class T2>
class someclass
{…};

在定義對象時分別代入實際的類型名,如:
someclass<int,double> obj;
7.和使用類一樣,使用類模板時要注意其作用域,只能在其有效作用域內用它定義對象。
8.模板可以有層次,一個類模板可以作為基類,派生出派生模板類。

4.模板在工程中的應用

綜述

1.模板是C++中類型參數化的多態工具,提供函數模板和類模板
2.模板定義從模板說明開始,類屬參數必須在模板實現中至少使用一次
3.同一個類屬參數可以用于多類模板
4.類屬參數可用于函數形參,返回類型以及聲明函數中的變量
5.模板由編譯器根據實際的數據類型實例化,生成實際的可執行代碼,從而得到模板函數和模板類
6.函數模板可以進行重載
7.類模板可以進行派生繼承


工程中用到的容器
所有容器提供的都是值(value)語意,而非引用(reference)語意。容器執行插入元素的操作時,內部實施拷貝動作。所以STL容器內存儲的元素必須能夠被拷貝(必須提供拷貝構造函數)。


案例
設計一個數組模板類( MyVector ),完成對int、char、Teacher類型元素的管理。

  • 類模板定義
  • 構造函數
  • 拷貝構造函數
  • 重載操作符<< [] =操作符
  • 從數組模板中進行派生

使用基礎數據類型以及一般的自定義類對象作為容器元素

  • 容器/數組類模板頭文件
#pragma once        // 保證頭文件只被編譯一次。#include <iostream>
using namespace std;template <typename T>
class MyVector
{
public:MyVector(int size = 0);//構造函數MyVector(const MyVector & obj);//拷貝構造函數~MyVector();//析構函數T & operator[](int index);MyVector & operator=(MyVector obj);int getLen();friend ostream & operator<< <T>(ostream & out, const MyVector<T> & obj);
protected:
private:int m_len;T *m_space;
};
  • 容器/數組類模板實現文件
#include "myvector.h"template <typename T>
T & MyVector<T>::operator[](int index)
{return m_space[index];
}template <typename T>
MyVector<T>::MyVector(int size = 0)//構造函數
{m_space = new T[size];m_len = size;
}template <typename T>
MyVector<T>::MyVector(const MyVector & obj)//拷貝構造函數
{m_len = obj.m_len;m_space = new T[m_len];for (int i = 0; i < m_len; i++){m_space[i] = obj.m_space[i];}}template <typename T>
MyVector<T>::~MyVector()//析構函數
{if (m_space != NULL){delete[]m_space;m_len = 0;m_space = NULL;}
}template <typename T>
MyVector<T> & MyVector<T>::operator=(MyVector<T> obj)
{/*1.釋放舊內存*/if (m_space != NULL){delete[]m_space;m_space = NULL;m_len = 0;}/*2.重新分配內存*/m_space = new T[obj.m_len];m_len = obj.m_len;/*3.拷貝數據*/for (int i = 0; i < m_len; i++){m_space[i] = obj[i];}/*4.返回左值本身*/return  *this;
}template <typename T>
int MyVector<T>::getLen()
{return m_len;
}template <typename T>
ostream & operator<<(ostream & out, const MyVector<T> & obj)
{for (int i = 0; i < obj.m_len; i++){cout << obj.m_space[i] << " ";}cout << endl;return out;
}
  • 容器/數組類測試文件
#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;class Teacher
{
public:Teacher(){age = 22;strcpy(name, "Goopher");}Teacher(int age, char *name){this->age = age;strcpy(this->name,name);}void printTeacher(){cout << name << "," << age << endl;}
protected:
private:int age;char name[32];
};class Test1
{
public:
protected:
private:int a;
};class Test2
{
public:
protected:
private:int a;static int b;//static成員存儲在全局區,不占類對象的內存模型
};class Test3
{
public:virtual void hello()//虛函數使得類對象具有VPTR指針,多占用一個指針的大小{}
protected:
private:int a;static int b;
};class Test4
{
public:virtual void hello(){}virtual void hello01() = 0;//多個虛函數只對應一個VPTR指針(一個虛函數表)
protected:
private:int a;static int b;
};class Test5
{
public:virtual void hello(){}virtual void hello01() = 0;void printTest()//普通成員函數也不占據類對象的內存模型{}
protected:
private:int a;static int b;
};class Test6
{
public:virtual void hello(){}//virtual void hello01() = 0;void printTest(){}void printTest01()//多個普通成員函數也不占據類對象的內存模型{}
protected:
private:int a;static int b;
};
int main(void)
{   /*初始化v1容器中的沒一個對象并在初始化的時候逐個打印*/MyVector<int> v1(10);for (int i = 0; i < v1.getLen(); i++){v1[i] = i + 1;cout << v1[i] << " ";}cout << endl;/*初始化v2容器中的沒一個對象并在初始化以后使用重載<<的方式打印*/MyVector<int> v2 = v1;for (int i = 0; i < v2.getLen(); i++){v2[i] = i*2 + 1;}cout <<v2<< endl;/*使用類對象設置容器并打印*/Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");MyVector<Teacher> v3(4);v3[0] = t1;v3[1] = t2;v3[2] = t3;v3[3] = t4;for (int i = 0; i < v3.getLen(); i++){Teacher tmp = v3[i];tmp.printTeacher();}cout<<"Hello!"<<endl;cout << sizeof(Test1)<<endl;cout << sizeof(Test2) << endl;cout << sizeof(Test3) << endl;cout << sizeof(Test4) << endl;cout << sizeof(Test5) << endl;cout << sizeof(Test6) << endl;Test6 t6;cout << sizeof(t6) << endl;system("pause");return 0;
}

優化后的Teacher類對象作為容器存儲元素(類模板頭文件和實現文件不變)

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;//1  優化Teacher類, 屬性變成 char *panme, 購置函數里面 分配內存
//2  優化Teacher類,析構函數 釋放panme指向的內存空間
//3  優化Teacher類,避免淺拷貝 重載= 重寫拷貝構造函數 
//4  優化Teacher類,在Teacher增加 << 
//5  在模板數組類中,存int char Teacher Teacher*(指針類型)//=====>stl 容器的概念 
class Teacher
{
public:Teacher(){age = 22;name = new char[1];strcpy(name, "");}Teacher(int age, char *name){this->age = age;this->name = new char[strlen(name) + 1];strcpy(this->name,name);}Teacher(const Teacher &obj){age = obj.age;name = new char[strlen(obj.name) + 1];strcpy(name,obj.name);}~Teacher(){if (name != NULL){delete[]name;name = NULL;age = 22;}}void printTeacher(){cout << name << "," << age << endl;}friend ostream & operator<<(ostream & out, Teacher &obj);Teacher & operator=(const Teacher & obj){if (name != NULL){delete[]name;name = NULL;age = 22;}name = new char[strlen(obj.name) + 1];strcpy(name, obj.name);age = obj.age;return *this;}
protected:
private:int age;char *name;
};ostream & operator<<(ostream & out, Teacher &obj)
{out << obj.name << "," << obj.age << endl;return out;
}
int main(void)
{   /*使用類對象設置容器并打印*/Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");MyVector<Teacher> v3(4);v3[0] = t1;v3[1] = t2;v3[2] = t3;v3[3] = t4;for (int i = 0; i < v3.getLen(); i++){Teacher tmp = v3[i];tmp.printTeacher();}cout<<"Hello!"<<endl;system("pause");return 0;
}

指針作為容器元素存儲

#define _CRT_SECURE_NO_WARNINGS#include <iostream>
#include "myvector.cpp"//注意包含的是cpp文件
using namespace std;//1  優化Teacher類, 屬性變成 char *panme, 購置函數里面 分配內存
//2  優化Teacher類,析構函數 釋放panme指向的內存空間
//3  優化Teacher類,避免淺拷貝 重載= 重寫拷貝構造函數 
//4  優化Teacher類,在Teacher增加 << 
//5  在模板數組類中,存int char Teacher Teacher*(指針類型)//=====>stl 容器的概念 
class Teacher
{
public:Teacher(){age = 22;name = new char[1];strcpy(name, "");}Teacher(int age, char *name){this->age = age;this->name = new char[strlen(name) + 1];strcpy(this->name,name);}Teacher(const Teacher &obj){age = obj.age;name = new char[strlen(obj.name) + 1];strcpy(name,obj.name);}~Teacher(){if (name != NULL){delete[]name;name = NULL;age = 22;}}void printTeacher(){cout << name << "," << age << endl;}friend ostream & operator<<(ostream & out, Teacher &obj);Teacher & operator=(const Teacher & obj){if (name != NULL){delete[]name;name = NULL;age = 22;}name = new char[strlen(obj.name) + 1];strcpy(name, obj.name);age = obj.age;return *this;}
protected:
private:int age;char *name;
};ostream & operator<<(ostream & out, Teacher &obj)
{out << obj.name << "," << obj.age << endl;return out;
}
int main(void)
{   /*使用類對象設置容器并打印*/Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");MyVector<Teacher*> v3(4);v3[0] = &t1;v3[1] = &t2;v3[2] = &t3;v3[3] = &t4;for (int i = 0; i < v3.getLen(); i++){Teacher *tmp = v3[i];tmp->printTeacher();}cout << t1;cout<<"Hello!"<<endl;system("pause");return 0;
}

總結:類模板實現了數據結構(具體數據類型)和算法的分離,真正的實現了泛型編程。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448363.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448363.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448363.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Base64轉PDF、PDF轉IMG(使用pdfbox插件)

--添加依賴 <!-- https://mvnrepository.com/artifact/org.apache.pdfbox/pdfbox --><dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.12</version></dependency&…

const的用法,特別是用在函數后面

原文出處&#xff1a;http://blog.csdn.net/zcf1002797280/article/details/7816977

圖解 Linux 安裝 JDK1.8 、配置環境變量

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 到官網下載 JDK 1.8 https://www.oracle.com/technetwork/java/javase/downloads/index.html 2. 用 rz 命令把 jdk-8u191-linux-x6…

剎車

定義 剎車就是可以減慢車速的機械制動裝置&#xff0c;又名減速器。簡單來說&#xff0c;汽車剎車踏板在方向盤下面&#xff0c;踩住剎車踏板&#xff0c;則使剎車杠桿聯動受壓并傳至到剎車鼓上的剎車片卡住剎車輪盤&#xff0c;使汽車減速或停止運行。作用 目的是減速&a…

【原創】Performanced C++ 經驗規則 第五條:再談重載、覆蓋和隱藏

第五條&#xff1a;再談重載、覆蓋和隱藏 在C中&#xff0c;無論在類作用域內還是外&#xff0c;兩個&#xff08;或多個&#xff09;同名的函數&#xff0c;可能且僅可能是以下三種關系&#xff1a;重載&#xff08;Overload&#xff09;、覆蓋&#xff08;Override&#xff0…

C++之純虛函數和抽象類

純虛函數和抽象類 1.基本概念 2.案例 #include <iostream> using namespace std;////面向抽象類編程(面向一套預先定義好的接口編程)//解耦合 ....模塊的劃分class Figure //抽象類 { public://閱讀一個統一的界面(接口),讓子類使用,讓子類必須去實現virtual void get…

解決: -bash: $‘\302\240docker‘: command not found

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 我只是運行 一條很簡單的啟動容器的命令&#xff0c;多次執行都報錯&#xff0c;報錯如題&#xff1a; -bash: $\302\240docker: comma…

換擋/掛檔

定義 換擋/掛檔是指變速器&#xff0c;用于轉變發動機曲軸的轉矩及轉速&#xff0c;以適應汽車在起步、加速、行駛以及克服各種道路阻礙等不同行駛條件下對驅動車輪牽引力及車速不同要求的需要。作用 使汽車能以非常低的穩定車速行駛&#xff0c;而這種低的轉速只靠內然…

sql:無法解決 equal to 操作中 Chinese_PRC_CI_AS 和 Chinese_Taiwan_Stroke_CI_AS 之間的排序規則沖突。...

--無法解決 equal to 操作中 "Chinese_PRC_CI_AS" 和 "Chinese_Taiwan_Stroke_CI_AS" 之間的排序規則沖突。 CREATE VIEW View_VipBranchStaffBranchList AS select VipBranchStaff.*,geovindu_branch.B_Name,VipExamCountry.ExamCountryName from VipBran…

【汽車取證篇】GA-T 1998-2022《汽車車載電子數據提取技術規范》(附下載)

【汽車取證篇】GA-T 1998-2022《汽車車載電子數據提取技術規范》&#xff08;附下載&#xff09; GA-T 1998-2022《汽車車載電子數據提取技術規范》標準—【蘇小沐】 總結 公眾號回復關鍵詞【汽車取證】自動獲取資源合集&#xff0c;如鏈接失效請留言&#xff0c;便于…

解決: Client does not support authentication protocol requested by server; consider upgrading MySQL

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 在服務器上把 mysql 裝好后&#xff0c;運行起來。 2. navicat 死活連接不上&#xff0c;在網上查說是要改數據庫賬號、密碼什么的&…

C++之STL理論基礎

1.基本概念 STL&#xff08;Standard Template Library&#xff0c;標準模板庫)是惠普實驗室開發的一系列軟件的統稱。雖然主要出現在C中&#xff0c;但在被引入C之前該技術就已經存在了很長的一段時間。 STL的從廣義上講分為三部分&#xff1a;algorithm&#xff08;算法&am…

方向盤

定義 方向盤是汽車、輪船、飛機等的操縱行駛方向的輪狀裝置。 構成 一般由骨架和發泡組合起來就是最簡單的方向盤了&#xff0c;而方向盤上都會有和主駕駛氣囊對應的安裝卡扣或螺釘孔&#xff0c;其下方一般會有多功能開關模塊。作用 方向盤不僅可以控制車輛的方向…

數據庫范式俗話

1NF&#xff1a;一個table中的列是不可再分的&#xff08;即列的原子性&#xff09; 2NF&#xff1a;一個table中的行是可以唯一標示的&#xff0c;&#xff08;即table中的行是不可以 重復的&#xff09; 3NF&#xff1a;一個table中的列不依賴于另一個table中的非主鍵列 4NF&…

STL之string類型

1.String概念 string是STL的字符串類型&#xff0c;通常用來表示字符串。而在使用string之前&#xff0c;字符串通常是用char*表示的。 string和char*的區別&#xff1a; string是一個類, char*是一個指向字符的指針。 string封裝了char*&#xff0c;管理這個字符串&#x…

解決maven打包報錯:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、報錯經歷&#xff1a; 今天使用eclipse通過maven install打war包的時候&#xff0c;出現了下圖所示的錯誤 二、問題分析&#xff1a…

離合器

離合器的定義 汽車離合器位于發動機和變速箱之間的飛輪殼內&#xff0c;用螺釘將離合器總成固定在飛輪的后平面上&#xff0c;離合器的輸出軸就是變速箱的輸入軸。在汽車行駛過程中&#xff0c;駕駛員可根據需要踩下或松開離合器踏板&#xff0c;使發動機與變速箱暫時分離和…

Python 刪除滿足條件的某些行

數據&#xff1a; data 字段&#xff1a;col 要刪除的內容是 col False 的行 # 方案一 data1 data[~data[col] False] # ~ 取反# 方案二 保留 data[已采] ! False ind data[col] ! False data2 data.loc[ind,]# 方案三 去掉 data[已采] True ind2 data[col] False…

STL之Vector

1.簡介 vector是將元素置于一個動態數組中加以管理的容器。可以隨機存取元素&#xff08;支持索引值直接存取&#xff0c;用[]操作符或at()方法&#xff0c;還支持迭代器方式存取&#xff09;。   vector尾部添加或移除元素非常快速。但是在中部或頭部插入元素或移除元素比…

解決 : Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 執行 maven install 命令報錯如題&#xff1a; Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:comp…