014-模板、自定义动态数组

《C++文章汇总》
上一篇介绍了《013-运算符重载》,本文介绍模板和自定义动态数组。

1.模板

◼ 泛型,是一种将类型参数化以达到代码复用的技术,C++中使用模板来实现泛型
未使用模板前

class Point{
    friend ostream &operator<<(ostream &cout,const Point &point);
    int m_x;
    int m_y;
public:
    Point(int x,int y):m_x(x),m_y(y){
        
    };
    //拷贝构造函数
    Point(const Point &point){
        cout << "Point(const Point &point)" << endl;
        m_x = point.m_x;
        m_y = point.m_y;
      }
    Point operator+(const Point &point){//返回值不能写成&,会报错Non-const lvalue reference to type 'Point' cannot bind to a temporary of type 'Point' 临时变量不能作为非const引用,临时变量的修改没有意义,属于const
        return Point(m_x+point.m_x,m_y+point.m_y);
    };
};
ostream &operator<<(ostream &cout,const Point &point){
    return  cout << "(" << point.m_x << ", " << point.m_y << ")" << endl;
}
int add(int a,int b){
    return a+b;
}
double add(double a,double b){
    return a+b;
}
Point add(Point a,Point b){
    return a+b;//a.operator+(b);
}
int main(){
    cout << add(1,2) << endl;
    cout << add(2.5,2.6) << endl;
    cout << add(Point(10,20),Point(20,30)) << endl;
    getchar();
    return 0;
}
//输出
3
5.1
(30, 50)

◼ 模板的使用格式如下,编译器根据传递的参数不同,生成不同的函数,本质还是有三个函数

template

class Point{
    friend ostream &operator<<(ostream &cout,const Point &point);
    int m_x;
    int m_y;
public:
    Point(int x,int y):m_x(x),m_y(y){
        
    };
    //拷贝构造函数
    Point(const Point &point){
        cout << "Point(const Point &point)" << endl;
        m_x = point.m_x;
        m_y = point.m_y;
      }
    Point operator+(const Point &point){//返回值不能写成&,会报错Non-const lvalue reference to type 'Point' cannot bind to a temporary of type 'Point' 临时变量不能作为非const引用,临时变量的修改没有意义,属于const
        return Point(m_x+point.m_x,m_y+point.m_y);
    };
};
ostream &operator<<(ostream &cout,const Point &point){
    return  cout << "(" << point.m_x << ", " << point.m_y << ")" << endl;
}
//int add(int a,int b){
//    return a+b;
//}
//double add(double a,double b){
//    return a+b;
//}
//Point add(Point a,Point b){
//    return a+b;//a.operator+(b);
//}
template T add(T a,T b){
    return a+b;
}
int main(){
    cout << add(1,2) << endl;
    cout << add(2.5,2.6) << endl;
    cout << add(Point(10,20),Point(20,30)) << endl;
    getchar();
    return 0;
}
//输出
3
5.1
(30, 50)

cout << add(10,20) << endl;
cout << add(2.5,2.6) << endl;
cout << add(Point(10,20),Point(20,30)) << endl;
等价于,编译器自动识别类型
cout << add(1,2) << endl;//call add (010F1438h)
cout << add(2.5,2.6) << endl;//call add(010F1433h)
cout << add(Point(10,20),Point(20,30)) << endl;//call add(010F143Dh)

多参数类型

template C add(A a,B b){
    return a+b;
}

typename和class是等价的
◼ 模板没有被使用时,是不会被实例化出来的
◼ 模板的声明和实现如果分离到.h和.cpp中,会导致链接错误

  • 不使用模板,声明和实现方法,不会报错,main.cpp文件时会给add(int a,int b),add(double a,double b)产生占位符,编译完成后,将add.obj和main.obj链接后回将占位符替代为实际函数地址方便调用
#include 
int add(int a,int b);
double add(double a,double b);
#include "add.hpp"

int add(int a,int b){
    return a+b;
}
double add(double a,double b){
    return a+b;
}
//template T add(T a,T b){
//    return a+b;
//}
class Point{
    friend ostream &operator<<(ostream &cout,const Point &point);
    int m_x;
    int m_y;
public:
    Point(int x,int y):m_x(x),m_y(y){
        
    };
    //拷贝构造函数
    Point(const Point &point){
        cout << "Point(const Point &point)" << endl;
        m_x = point.m_x;
        m_y = point.m_y;
      }
    Point operator+(const Point &point){//返回值不能写成&,会报错Non-const lvalue reference to type 'Point' cannot bind to a temporary of type 'Point' 临时变量不能作为非const引用,临时变量的修改没有意义,属于const
        return Point(m_x+point.m_x,m_y+point.m_y);
    };
};
ostream &operator<<(ostream &cout,const Point &point){
    return  cout << "(" << point.m_x << ", " << point.m_y << ")" << endl;
}
int main(){
    
    cout << add(1,2) << endl;//call 函数地址
    cout << add(2.5,2.6) << endl;//call 函数地址
//    cout << add(Point(10,20),Point(20,30)) << endl;
    getchar();
    return 0;
}
//输出
3
5.1
图片.png
  • 使用模板
#include 
//int add(int a,int b);
//double add(double a,double b);
template T add(T a,T b);

#include "add.hpp"
//int add(int a,int b){
//    return a+b;
//}
//double add(double a,double b){
//    return a+b;
//}
template T add(T a,T b){
    return a+b;
}
class Point{
    friend ostream &operator<<(ostream &cout,const Point &point);
    int m_x;
    int m_y;
public:
    Point(int x,int y):m_x(x),m_y(y){
        
    };
    //拷贝构造函数
    Point(const Point &point){
        cout << "Point(const Point &point)" << endl;
        m_x = point.m_x;
        m_y = point.m_y;
      }
    Point operator+(const Point &point){//返回值不能写成&,会报错Non-const lvalue reference to type 'Point' cannot bind to a temporary of type 'Point' 临时变量不能作为非const引用,临时变量的修改没有意义,属于const
        return Point(m_x+point.m_x,m_y+point.m_y);
    };
};
ostream &operator<<(ostream &cout,const Point &point){
    return  cout << "(" << point.m_x << ", " << point.m_y << ")" << endl;
}
int main(){
    cout << add(1,2) << endl;
    cout << add(2.5,2.6) << endl;
    cout << add(Point(10,20),Point(20,30)) << endl;
    getchar();
    return 0;
}

使用模板编译报链接错误,为什么使用模板编译链接会报错?单独编译main.cpp时,生成三个函数地址占位符,单独编译add.cpp时,没有调用add(int a,int b)、add(double a,double b)和add(Point a,Point b)函数的代码,故编译时没有生成add(int a,int b)、add(double a,double b)和add(Point a,Point b)函数,泛型模板只有调用了才会生成函数,add.obj中没有函数实现代码,链接时不会有函数的实现地址替代占位符,故编译链接失败报错,不会有exe生成

Undefined symbols for architecture x86_64:
  "Point add(Point, Point)", referenced from:
      _main in main.o
  "double add(double, double)", referenced from:
      _main in main.o
  "int add(int, int)", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
图片.png

图片.png

◼ 一般将模板的声明和实现统一放到一个.hpp文件中,.hpp文件既可以放声明又可以放实现

#include 
template T add(T a,T b){
    return a+b;
}
#include "main.hpp"
#include 
#include "add.hpp"
using namespace std;
class Point{
    friend ostream &operator<<(ostream &cout,const Point &point);
    int m_x;
    int m_y;
public:
    Point(int x,int y):m_x(x),m_y(y){
        
    };
    //拷贝构造函数
    Point(const Point &point){
        cout << "Point(const Point &point)" << endl;
        m_x = point.m_x;
        m_y = point.m_y;
      }
    Point operator+(const Point &point){//返回值不能写成&,会报错Non-const lvalue reference to type 'Point' cannot bind to a temporary of type 'Point' 临时变量不能作为非const引用,临时变量的修改没有意义,属于const
        return Point(m_x+point.m_x,m_y+point.m_y);
    };
};
ostream &operator<<(ostream &cout,const Point &point){
    return  cout << "(" << point.m_x << ", " << point.m_y << ")" << endl;
}
int main(){
    
    cout << add(1,2) << endl;
    cout << add(2.5,2.6) << endl;
    cout << add(Point(10,20),Point(20,30)) << endl;
    getchar();
    return 0;
}
//输出
3
5.1
(30, 50)

2.自定义动态数组

自定义动态数组,模拟数组越界抛异常

class Array{
    //用于指向首元素
    int *m_data;
    //元素个数
    int m_size;
    //容量
    int m_capacity;
public:
    Array(int capacity=0){
        m_capacity = (capacity<=0)?10:capacity;
        m_size = 0;
        //申请堆空间
        m_data = new int[m_capacity];
    };
    ~Array(){
        if (m_data == NULL ) return;
        delete[] m_data;
        m_data = NULL;
    };
    void add(int value){
        if (m_size == m_capacity) {
            //扩容
            /**
             1.申请一块更大的新空间
             2.将旧空间的数据拷贝到新空间
             3.释放旧空间
             */
            cout << "空间不够" << endl;
            return;
        }
        m_data[m_size++] = value;
    };
    int get(int index){
        if(index < 0 || index >= m_size){
            //报错:抛异常
            throw "数组下标越界";
        }
        return m_data[index];
    };
    int size(){
        return m_size;
    }
    int operator[](int index){
        return get(index);
    }
};
int main(){
    
    Array array(3);
    array.add(10);
    array.add(20);
    array.add(30);
    array.add(40);
    array.add(50);
    
    cout << array.get(0) << endl;
    cout << array[1] << endl;
    cout << array.size() << endl;
    cout << array[5] << endl;//下标越界抛异常
    
    getchar();
    return 0;
}
//输出
空间不够
空间不够
10
20
3
libc++abi.dylib: terminating with uncaught exception of type char const*
terminating with uncaught exception of type char const*
(lldb) 
图片.png

3.自定义动态数组模板

template
class Array{
    //用于指向首元素
    Item *m_data;
    //元素个数
    int m_size;
    //容量
    int m_capacity;
public:
    Array(int capacity=0){
        m_capacity = (capacity<=0)?10:capacity;
        m_size = 0;
        //申请堆空间
        m_data = new Item[m_capacity];
    };
    ~Array(){
        if (m_data == NULL ) return;
        delete[] m_data;
        m_data = NULL;
    };
    void add(Item value){
        if (m_size == m_capacity) {
            //扩容
            /**
             1.申请一块更大的新空间
             2.将旧空间的数据拷贝到新空间
             3.释放旧空间
             */
            cout << "空间不够" << endl;
            return;
        }
        m_data[m_size++] = value;
    };
    Item get(int index){
        if(index < 0 || index >= m_size){
            //报错:抛异常
            throw "数组下标越界";
        }
        return m_data[index];
    };
    int size(){
        return m_size;
    }
    Item operator[](int index){
        return get(index);
    }
};
int main(){
    
    Array array(3);//类型必须写,否则构造函数不知道类型
    array.add(10);
    array.add(20);
    array.add(30);
    array.add(40);
    array.add(50);
    
    cout << array.get(0) << endl;
    cout << array[1] << endl;
    cout << array.size() << endl;
    
    getchar();
    return 0;
}
//输出
空间不够
空间不够
10
20
3

存放对象类型

int main(){
    Array array;
    //array.add(&Point(1, 2));//数据不安全,临时对象放入数组中,临时对象会马上销毁,马上销毁还存入地址值
    array.add(new Point(3,4));//数据不安全,堆空间的对象,没有看到delete对象被销毁
    array.add(new Car());
    
    getchar();
    return 0;
}

4.抽取模板到.hpp文件中,声明和实现分开写,易于可读和调用者阅读

I.自定义动态数组模板中添加基本数据类型int

template
class Array{
    friend ostream &operator<< <>(ostream &,const Array &);
    //用于指向首元素
    Item *m_data;
    //元素个数
    int m_size;
    //容量
    int m_capacity;
public:
    Array(int capacity=0);
    ~Array();
    void add(Item value);
    Item get(int index);
    int size();
    Item operator[](int index);
};

template
Array::Array(int capacity=0){
    m_capacity = (capacity<=0)?10:capacity;
    m_size = 0;
    //申请堆空间
    m_data = new Item[m_capacity];
};
template
Array::~Array(){
    if (m_data == NULL ) return;
    delete[] m_data;
    m_data = NULL;
};
template
void Array::add(Item value){
    if (m_size == m_capacity) {
        //扩容
        /**
         1.申请一块更大的新空间
         2.将旧空间的数据拷贝到新空间
         3.释放旧空间
         */
        cout << "空间不够" << endl;
        return;
    }
    m_data[m_size++] = value;
};
template
Item Array::get(int index){
    if(index < 0 || index >= m_size){
        //报错:抛异常
        throw "数组下标越界";
    }
    return m_data[index];
};
template
int Array::size(){
    return m_size;
}
template
Item Array::operator[](int index){
    return get(index);
}
template 
ostream &operator<< <>(ostream &cout,const Array &array){
    cout << "[";
    for(int i=0;i
#include "Array.hpp"
using namespace std;
int main(){
    Array array;//类型必须写,否则构造函数不知道类型
    array.add(10);
    array.add(20);
    array.add(30);
    array.add(40);
    array.add(50);
    
    cout << array << endl;
    getchar();
    return 0;
}
//输出
[10,20,30,40,50]

友元函数中含有泛型,声明和实现都要加上尖括号

II.自定义动态数组模板中添加对象类型Point,模板中代码m_data = new Item[m_capacity];相当于m_data = new Point[m_capacity],此时Point中需要无参的构造函数

class Point {
    friend ostream &operator<<(ostream &,const Point &);
    int m_x;
    int m_y;
public:
    Point(int x=0,int y=0):m_x(x),m_y(y){
        
    }
};
ostream &operator<<(ostream &cout,const Point &point){
    return cout << "(" << point.m_x << ", " << point.m_y << ")";
}
int main(){
    Array array;
    array.add(Point(1,2));
    array.add(Point(3,4));
    array.add(Point(5,6));
    cout << array << endl;
}
//输出
(1,2),(3,4),(5,6)

III.自定义动态数组模板中删除元素逻辑,将后面的元素往前移,size--,没有内存回收的逻辑,原先的最后一个内存单元等着下次add添加时被覆盖


图片.png
class Array{
    friend ostream &operator<< <>(ostream &,const Array &);
    //用于指向首元素
    Item *m_data;
    //元素个数
    int m_size;
    //容量
    int m_capacity;
    void checkIndex(int index);
public:
    Array(int capacity=0);
    ~Array();
    void add(Item value);
    void remove(int index);
    Item get(int index);
    int size();
    Item operator[](int index);
};
//抽取下边越界判断
template
void Array::checkIndex(int index){
    if(index < 0 || index >= m_size){
        //报错:抛异常
        throw "数组下标越界";
    }
}
template
void Array::remove(int index){
    checkIndex(index);
    //数据index后往前移,size--
};

IV.自定义动态数组中插入元素,将index位置及后面的元素值往后挪,最后的元素先往后移再移动前面的元素,将value覆盖掉本来处在index位置的值

void insert(int index,Item value);
template
void Array::insert(int index,Item value){
    checkIndex(index);
    //数据index后往后移,从最后一个元素开始往后移,倒数第二个,倒数第三个... 直到index位置的值用value覆盖,size++
};

你可能感兴趣的:(014-模板、自定义动态数组)