《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
- 使用模板
#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)
◼ 一般将模板的声明和实现统一放到一个.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)
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添加时被覆盖
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++
};