要求:
设计一个数组模板类(MyArray),完成对不同类型元素的管理
操作步骤
设计头文件
在 qtcreate下先创建03_code的项目,然后右键点击03_code添加新文件,点击头文件,点击Choose
命名为 myarry.hpp,其中 .hpp是头文件和源文件结合的一种,一般头文件 .h 是定义类模板,而.cpp 实现类模板的公共方法操作,但是这种方式存在问题,所以为了方便我们操作,C++可以直接使用 .hpp 实现定义类模板和实现它的公共操作方法
设计模板类
#ifndef MYARRY_HPP
#define MYARRY_HPP
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class MyArry{
template <typename T1>
friend ostream & operator<<(ostream &out,MyArry<T1> ob);
private:
T *arry; //保存数组地址
int size; //大小
int capacity; //容量
public:
//无参构造
MyArry();
//有参构造
MyArry(int capacity);
//拷贝构造
MyArry(MyArry &ob);
//析构函数
~MyArry();
//重载运算符,完成深拷贝
MyArry & operator=(MyArry &ob);
//输入函数
void pushBack(T elem);
};
#endif // MYARRY_HPP
公共方法实现
无参构造
//无参构造 成员变量初始化
template<class T>
MyArry<T>::MyArry()
{
arry = NULL;
size = 0;
capacity = 0;
}
有参构造
//有参构造 成员变量赋值
template<class T>
MyArry<T>::MyArry(int capacity)
{
this->capacity = capacity;
arry = new T[this->capacity];
size = 0;
//清空数组
memset(arry,0,sizeof(T)*capacity);
}
拷贝构造
拷贝构造本质是构造函数
在上面的代码中,旧对象给新对象初始化就会调用拷贝构造函数
目的是为了在结束的时候新对象也会调用析构函数释放掉新创建的堆区空间,而不会造成新旧对象前后释放指针变量指向的同一块堆区空间,造成多次释放
//拷贝构造
template<class T>
MyArry<T>::MyArry(MyArry &ob)
{
//ob没有空间,拷贝的空间也没有
if (ob.arry == NULL){
arry = NULL;
size = 0;
capacity = 0;
}
//ob有空间,所以即将拷贝的也有空间
else{
capacity = ob.capacity;
size = ob.size;
//申请堆区数组空间
arry = new T[capacity];
//将旧堆区空间的值复制到新堆区空间
memcpy(arry,ob.arry,sizeof(T)*capacity);
}
}
析构函数
//析构函数,释放堆区空间
template<class T>
MyArry<T>::~MyArry()
{
if(arry != NULL){ //若堆区空间为空就不用析构
delete [] arry;
}
}
重载运算符,完成 “=”深拷贝
返回值是引用类型是因为可以链式操作完成 “=”深拷贝,即 ........ob3 = ob2 = ob1 = ob,返回值依然是一个 MyArry<T> 类型,MyArry<T> 必不可少,因为它模板类名 MyArry 和 T 一起完整的描述了是一个如何的返回值
//深拷贝的时候完成赋值重载运算符 ob1 = ob2
template<class T>
MyArry<T> &MyArry<T>::operator=(MyArry &ob)
{
//判断 this->arry是否存在空间
if(this->arry != NULL){
delete [] arry;
arry = NULL;
}
capacity = ob.capacity;
size = ob.size;
//申请数组堆区空间
arry = new T[capacity];
//将旧堆区空间的值复制到新堆区空间
memset(arry,0,sizeof(T)*capacity);
//将旧堆区空间的值复制到新堆区空间
memcpy(arry,ob.arry,sizeof(T)*capacity);
//链式完成赋值重载运算符,返回左边运算符的引用
return *this;
}
插入函数
//输入函数
template<class T>
void MyArry<T>::pushBack(T elem)
{
if (size == capacity){ //满
//扩展容量
capacity = (capacity == 0?1:2*capacity);
//根据容量申请空间
T *temp = new T[capacity];
//无参构造没有给空间的代价,因为无参构造中 size == capacity
if(arry != NULL){
//将旧空间的内容拷贝到新空间
memcpy(temp,arry,sizeof(T)*size);
//释放旧空间
delete [] arry;
}
arry = temp;
}
arry[size] = elem;
size++;
return;
}
全局输出函数
注意:类模板的T和函数模板的T1不能重名
//全局函数重载
//注意:类模板的T和函数模板的T1不能重名
template <typename T1>
ostream & operator<<(ostream &out,MyArry<T1> ob){
int i;
for(i = 0; i<ob.size;i++){
out<<ob.arry[i]<<endl;
}
return out;
}
代码实现
MyArry.hpp头文件中的代码实现
#ifndef MYARRY_HPP
#define MYARRY_HPP
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class MyArry{
template <typename T1>
friend ostream & operator<<(ostream &out,MyArry<T1> ob);
private:
T *arry; //保存数组地址
int size; //大小
int capacity; //容量
public:
//无参构造
MyArry();
//有参构造
MyArry(int capacity);
//拷贝构造
MyArry(MyArry &ob);
//析构函数
~MyArry();
//重载运算符,完成深拷贝
MyArry & operator=(MyArry &ob);
//插入函数
void pushBack(T elem);
};
#endif // MYARRY_HPP
//无参构造 初始化
template<class T>
MyArry<T>::MyArry()
{
arry = NULL;
size = 0;
capacity = 0;
}
//有参构造 成员变量赋值
template<class T>
MyArry<T>::MyArry(int capacity)
{
this->capacity = capacity;
arry = new T[this->capacity];
size = 0;
//清空数组
memset(arry,0,sizeof(T)*capacity);
}
//拷贝构造
template<class T>
MyArry<T>::MyArry(MyArry &ob)
{
//ob没有空间,拷贝的空间也没有
if (ob.arry == NULL){
arry = NULL;
size = 0;
capacity = 0;
}
//ob有空间,所以即将拷贝的也有空间
else{
capacity = ob.capacity;
size = ob.size;
//申请堆区数组空间
arry = new T[capacity];
//将旧堆区空间的值复制到新堆区空间
memcpy(arry,ob.arry,sizeof(T)*capacity);
}
}
//析构函数
template<class T>
MyArry<T>::~MyArry()
{
if(arry != NULL){ //若堆区空间为空就不用析构
delete [] arry;
}
}
//深拷贝的时候完成赋值重载运算符 ob1 = ob2
template<class T>
MyArry<T> &MyArry<T>::operator=(MyArry &ob)
{
//判断 this->arry是否存在空间
if(this->arry != NULL){
delete [] arry;
arry = NULL;
}
capacity = ob.capacity;
size = ob.size;
//申请数组堆区空间
arry = new T[capacity];
//将旧堆区空间的值复制到新堆区空间
memset(arry,0,sizeof(T)*capacity);
//将旧堆区空间的值复制到新堆区空间
memcpy(arry,ob.arry,sizeof(T)*capacity);
//链式完成赋值重载运算符,返回左边运算符的引用
return *this;
}
//输入函数
template<class T>
void MyArry<T>::pushBack(T elem)
{
if (size == capacity){ //满
//扩展容量
capacity = (capacity == 0?1:2*capacity);
//根据容量申请空间
T *temp = new T[capacity];
//无参构造没有给空间的代价,因为无参构造中 size == capacity
if(arry != NULL){
//将旧空间的内容拷贝到新空间
memcpy(temp,arry,sizeof(T)*size);
//释放旧空间
delete [] arry;
}
arry = temp;
}
arry[size] = elem;
size++;
return;
}
//全局函数重载
//注意:类模板的T和函数模板的T1不能重名
template <typename T1>
ostream & operator<<(ostream &out,MyArry<T1> ob){
int i;
for(i = 0; i<ob.size;i++){
out<<ob.arry[i]<<endl;
}
return out;
}
main.cpp中的测试代码实现如下
#include <iostream>
#include "myarry.hpp"
using namespace std;
int main(int argc, char *argv[])
{
MyArry<int> ob(5);
ob.pushBack(10);
ob.pushBack(20);
ob.pushBack(30);
ob.pushBack(40);
ob.pushBack(50);
cout<<ob<<endl;
cout<<"------------------------------------"<<endl;
MyArry<char> ob1(5);
ob1.pushBack('a');
ob1.pushBack('b');
ob1.pushBack('c');
ob1.pushBack('d');
ob1.pushBack('e');
cout<<ob1<<endl;
return 0;
}
代码测试结果
在main.cpp中测试自定义类型
#include <iostream>
#include "myarry.hpp"
using namespace std;
class Person{
private:
int num;
string name;
int score;
public:
Person(){};
Person(int num,string name,int score){
this->num = num;
this->name = name;
this->score = score;
}
};
int main(int argc, char *argv[])
{
MyArry<int> ob(5);
ob.pushBack(10);
ob.pushBack(20);
ob.pushBack(30);
ob.pushBack(40);
ob.pushBack(50);
cout<<ob<<endl;
cout<<"------------------------------------"<<endl;
MyArry<char> ob1(5);
ob1.pushBack('a');
ob1.pushBack('b');
ob1.pushBack('c');
ob1.pushBack('d');
ob1.pushBack('e');
cout<<ob1<<endl;
cout<<"------------------------------------"<<endl;
MyArry<Person> ob3;
ob3.pushBack(Person(123,"ml",99.9));
ob3.pushBack(Person(456,"ll",100.0));
ob3.pushBack(Person(789,"ll",100.0));
ob3.pushBack(Person(101,"ll",100.0));
ob3.pushBack(Person(111,"ll",100.0));
cout<<ob3S<<endl;
return 0;
}
遍历出错,原因 Person 找不到输出运算符
解决方法是让 Person 重写上面的输出函数,并且要设置该函数为 Person 类的友元函数,便于访问 Person 的私有属性
#include <iostream>
#include "myarry.hpp"
using namespace std;
class Person{
friend ostream & operator<<(ostream &out,Person ob);
private:
int num;
string name;
int score;
public:
Person(){};
Person(int num,string name,int score){
this->num = num;
this->name = name;
this->score = score;
}
};
//设置为 Person 类的友元,便于访问 Person 的私有属性
ostream & operator<<(ostream &out,Person ob){
out<<ob.num<<" "<<ob.name<<" "<<ob.score<<endl;
return out;
}
int main(int argc, char *argv[])
{
MyArry<int> ob(5);
ob.pushBack(10);
ob.pushBack(20);
ob.pushBack(30);
ob.pushBack(40);
ob.pushBack(50);
cout<<ob<<endl;
cout<<"------------------------------------"<<endl;
MyArry<char> ob1(5);
ob1.pushBack('a');
ob1.pushBack('b');
ob1.pushBack('c');
ob1.pushBack('d');
ob1.pushBack('e');
cout<<ob1<<endl;
cout<<"------------------------------------"<<endl;
MyArry<Person> ob3;
ob3.pushBack(Person(123,"ml",99.9));
ob3.pushBack(Person(456,"ll",100.0));
ob3.pushBack(Person(789,"ll",100.0));
ob3.pushBack(Person(101,"ll",100.0));
ob3.pushBack(Person(111,"ll",100.0));
cout<<ob3<<endl;
return 0;
}
打印成功