首页 > 代码库 > 数据结构与算 5:C++ 顺序/链式存储,栈 模板类实现,编译模板类问题解决

数据结构与算 5:C++ 顺序/链式存储,栈 模板类实现,编译模板类问题解决

本文谢绝转载原文来自http://990487026.blog.51cto.com

技术分享

数据结构与算 5:C++ 顺序/链式存储,栈 模板类实现
	C++ 顺序存储模板类的实现[面试重要]
	C++ 链式存储模板类的实现[面试重要]
	C++ 链式存储模板类,载入指针
	c++ 栈的链式存储实现(模板类嵌套模板类),编译问题教训[重要]

	


C++ 顺序存储模板类的实现[面试重要]


项目文件:

chunli@http://990487026.blog.51cto.com~/c++$ tree
.
├── main.cpp
├── seqlist.cpp
└── seqlist.h
0 directories, 4 files
================================


main.cpp文件

chunli@http://990487026.blog.51cto.com~/c++$ cat main.cpp 
//C++ 构建模板类 顺序存储

#include <iostream>
#include <stdio.h>
#include <string.h>
#include "seqlist.cpp"
using namespace std;

struct Teacher
{
	char name[64];
	int age;
};

void fun()
{
	int i = 0;
	struct Teacher  tmp;
	struct Teacher  t1;  t1.age = 31;
	struct Teacher  t2;  t2.age = 32;
	struct Teacher  t3;  t3.age = 33;

	seqlist<Teacher> list(10);//创建顺序表
	list.insert(t1,0);//头插法
	list.insert(t2,0);//头插法
	list.insert(t3,0);//头插法
	//遍历链表
	for(i=0;i<list.getLen();i++)
	{
		list.get(i,tmp);
		cout <<tmp.age <<"  get\n";
	}
	
	list.clear();// 清空链表
	list.insert(t1,0);//头插法
	list.insert(t2,0);//头插法
	list.insert(t3,0);//头插法
	
	//循环删除
	while(list.getLen() > 0)
	{
		list.del(0,tmp);////头删除
		cout <<tmp.age <<" del\n";
	}
}

int main()
{
	fun();
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++$



seqlist.cpp文件

chunli@http://990487026.blog.51cto.com~/c++$ cat seqlist.cpp 
#include <stdio.h>
#include "seqlist.h"
template <typename T>
seqlist<T>::seqlist(int Capacity)
{
	this->pArray = new T[Capacity];
	this->capacity = Capacity;
	this->len = 0;
}

template <typename T>
seqlist<T>::~seqlist(void)
{
	delete [] this->pArray;
	this->pArray = NULL;
	this->capacity = 0;
	this->len = 0;
}

template <typename T>
int seqlist<T>::getLen()
{
	return this->len;
}

template <typename T>
int seqlist<T>::getCapacity()
{
	return this->capacity;
}

template <typename T>
int seqlist<T>::insert(T &t,int pos)
{
	int i = 0;
	if(pos <0)
	{
		return -1;
	}
	for(i=this->len;i>pos;i--)
	{
		pArray[i] = pArray[i-1];
	}
	pArray[i] = t;//STL元素保存时,通过复制机制实现的,你的类要可以复制才行
	this->len++;
	return 0;
}

template <typename T>
int seqlist<T>::get(int pos,T &t)
{
	if(pos <0)
	{
		return -1;
	}
	t = pArray[pos];
	return 0;
}

template <typename T>
int seqlist<T>::del(int pos,T &t)
{
	int i = 0;
	t = pArray[pos];
	for(i=pos+1;i<this->len;i++)
	{
		pArray[i-1] = pArray[i];
	}
	this->len--;
	return 0;
}

template <typename T>
int seqlist<T>::clear()//回到初始状态
{
	this->len = 0;
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++$


seqlist.h文件

chunli@http://990487026.blog.51cto.com~/c++$ cat seqlist.h
#pragma once
template <typename T>
class seqlist
{
public:
	seqlist(int Capacity);
	~seqlist(void);
	int getLen();
	int getCapacity();
	int insert(T &t,int pos);
	int get(int pos,T &t);
	int del(int pos,T &t);
	int clear();//清空链表
private:
	int len;
	int capacity;
	T *pArray;

};
chunli@http://990487026.blog.51cto.com~/c++$


编译运行:

chunli@http://990487026.blog.51cto.com~/c++$ g++ main.cpp  -Wall && ./a.out 
33  get
32  get
31  get
33 del
32 del
31 del
chunli@http://990487026.blog.51cto.com~/c++$



C++ 链式存储模板类的实现[面试重要]


项目文件:

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ tree
.
├── linklist.cpp
├── linklist.h
└── main.cpp
0 directories, 3 files


linklist.cpp 文件

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ cat linklist.cpp 
#include <stdio.h>
#include "linklist.h"  

template <typename T>
LinkList<T>::LinkList(void)
{
	this->head = new Node<T>;
	this->head->next = NULL;//初始化
	this->length  =0;//初始化
}

template <typename T>
LinkList<T>::~LinkList(void)
{
	Node<T> *tmp = NULL;
	while(this->head)
	{
		tmp = this->head->next;
		delete head;
		this->head = tmp;
	}
	this->length = 0;
	this->head  =NULL;
}


template <typename T>
int LinkList<T>::clear(void)
{
        Node<T> *tmp = NULL;
        while(this->head)
        {
                tmp = this->head->next;
                delete head;
                this->head = tmp;
        }
        this->length = 0;
        this->head = new Node<T>;
        this->head->next = NULL;//初始化
        this->length  =0;//初始化

	return 0;
}


template <typename T>
int LinkList<T>::len(void)
{
	return this->length;
}

template <typename T>
int LinkList<T>::insert(T &t,int pos)
{
	Node<T> *current = this->head;
	for(int i = 0;i<pos;i++)
	{
		current = current->next;
	}
	Node<T> *node = new Node<T>;
	if(node == NULL)
	{
		return -1;
	}
	node->t = t;//缓冲外部数据,调用拷贝构造函数
	node->next = current->next;//让新节点连接后续表
	current->next = node;
	this->length++;
	return 0;
}

template <typename T>
int LinkList<T>::get(int pos,T &t)
{
        Node<T> *current = NULL;
        current = this->head;
        for(int i = 0;i<pos;i++)
        {
                current = current->next;
        }
	t = current->next->t;//把缓存的节点给他
	return 0;
}

template <typename T>
int LinkList<T>::del(int pos,T &t)
{
        Node<T> *current = this->head;
        for(int i = 0;i<pos;i++)
        {
                current = current->next;
        }
        Node<T> *ret = current->next;//被删除的元素
	t = ret->t;////把缓存的节点给他
	current->next = ret->next;
	this->length--;
	delete ret;//释放内存
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$



linklist.h文件

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ cat linklist.h
#pragma once 
//模板类里面,在插入元素的时候,应该把每一个t保存起来
template <typename T>
struct Node
{
	T t;
	Node<T> *next;//搞成链表
};

template <typename T>
class LinkList
{
public:
	LinkList();
	~LinkList();
	int clear();
	int   len();
	int insert(T &t,int pos );
	int get (int pos,T &t);
	int del(int pos,T &t);
private:
	Node<T> *head;//指向链表的头指针
	int length;
};
chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$


main.cpp文件

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <string.h>
#include "linklist.cpp"
using namespace std;

struct Teacher
{
	char name[64];
	int age;
};

int main()
{
	int i = 0;
	struct Teacher  tmp;
	struct Teacher  t1;  t1.age = 31;
	struct Teacher  t2;  t2.age = 32;
	struct Teacher  t3;  t3.age = 33;

	LinkList<Teacher> list;//实例化 链式储存类
	list.insert(t1,0);//头插法
	list.insert(t2,0);//头插法
	list.insert(t3,0);//头插法
	//遍历链表
	for(i=0;i<list.len();i++)
	{
		list.get(i,tmp);
		cout <<tmp.age <<"  get\n";
	}
	
	list.clear();// 清空链表
	list.insert(t1,0);//头插法
	list.insert(t2,0);//头插法
	list.insert(t3,0);//头插法
	
	//循环删除
	while(list.len() > 0)
	{
		list.del(0,tmp);////头删除
		cout <<tmp.age <<" del\n";
	}
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$


编译运行:

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类
$ g++ main.cpp linklist.cpp -Wall  && ./a.out 
33  get
32  get
31  get
33 del
32 del
31 del
chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$


C++ 链式存储模板类,载入指针

linklist.cpp,linklist.h文件不变的

以下是main程序:

chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <string.h>
#include "linklist.cpp"
using namespace std;

struct Teacher
{
	char name[64];
	int age;
};

int main()
{
	int i = 0;
	struct Teacher  *tmp = NULL;
	struct Teacher  t1;  t1.age = 31;
	struct Teacher  t2;  t2.age = 32;
	struct Teacher  t3;  t3.age = 33;

	struct Teacher *p1 = &t1;
	struct Teacher *p2 = &t2;
	struct Teacher *p3 = &t3;

	LinkList<Teacher*> list;//实例化 链式储存类
	list.insert(p1,0);//头插法
	list.insert(p2,0);//头插法
	list.insert(p3,0);//头插法
	//遍历链表
	for(i=0;i<list.len();i++)
	{
		list.get(i,tmp);
		cout <<tmp->age <<"  get\n";
	}
	
	list.clear();// 清空链表
	list.insert(p1,0);//头插法
	list.insert(p2,0);//头插法
	list.insert(p3,0);//头插法
	
	//循环删除
	while(list.len() > 0)
	{
		list.del(0,tmp);////头删除
		cout <<tmp->age <<" del\n";
	}
	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$ g++ main.cpp  linklist.cpp -Wall && ./a.out 
33  get
32  get
31  get
33 del
32 del
31 del
chunli@http://990487026.blog.51cto.com~/c++/linklist_链式存储模板类$



c++ 栈的链式存储实现(模板类嵌套模板类),编译问题教训[重要]



项目文件:

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ tree
.
├── linklist.cpp
├── linklist.h
├── LinkStack.cpp
├── LinkStack.h
└── main.cpp
0 directories, 5 files



linklist.cpp 文件

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ cat linklist.cpp 
#include <stdio.h>
#include "linklist.h"  

template <typename T>
LinkList<T>::LinkList(void)
{
	this->head = new Node<T>;
	this->head->next = NULL;//初始化
	this->length  =0;//初始化
}

template <typename T>
LinkList<T>::~LinkList(void)
{
	Node<T> *tmp = NULL;
	while(this->head)
	{
		tmp = this->head->next;
		delete head;
		this->head = tmp;
	}
	this->length = 0;
	this->head  =NULL;
}


template <typename T>
int LinkList<T>::clear(void)
{
        Node<T> *tmp = NULL;
        while(this->head)
        {
                tmp = this->head->next;
                delete head;
                this->head = tmp;
        }
        this->length = 0;
        this->head = new Node<T>;
        this->head->next = NULL;//初始化
        this->length  =0;//初始化

	return 0;
}


template <typename T>
int LinkList<T>::len(void)
{
	return this->length;
}

template <typename T>
int LinkList<T>::insert(T &t,int pos)
{
	Node<T> *current = this->head;
	for(int i = 0;i<pos;i++)
	{
		current = current->next;
	}
	Node<T> *node = new Node<T>;
	if(node == NULL)
	{
		return -1;
	}
	node->t = t;//缓冲外部数据
	node->next = current->next;//让新节点连接后续表
	current->next = node;
	this->length++;
	return 0;
}

template <typename T>
int LinkList<T>::get(int pos,T &t)
{
        Node<T> *current = NULL;
        current = this->head;
        for(int i = 0;i<pos;i++)
        {
                current = current->next;
        }
	t = current->next->t;//把缓存的节点给他
	return 0;
}

template <typename T>
int LinkList<T>::del(int pos,T &t)
{
        Node<T> *current = this->head;
        for(int i = 0;i<pos;i++)
        {
                current = current->next;
        }
        Node<T> *ret = current->next;//被删除的元素
	t = ret->t;////把缓存的节点给他
	current->next = ret->next;
	this->length--;
	delete ret;//释放内存
	return 0;
}

chunli@http://990487026.blog.51cto.com~/c++/link_stack$



linklist.h 文件

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ cat linklist.h
#pragma once 
//模板类里面,在插入元素的时候,应该把每一个t保存起来
template <typename T>
struct Node
{
	T t;
	Node<T> *next;//搞成链表
};

template <typename T>
class LinkList
{
public:
	LinkList(void);
	~LinkList(void);
	int clear(void);
	int   len(void);
	int insert(T &t,int pos );
	int get (int pos,T &t);
	int del(int pos,T &t);
private:
	Node<T> *head;//指向链表的头指针
	int length;
};
chunli@http://990487026.blog.51cto.com~/c++/link_stack$


LinkStack.cpp 文件

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ cat LinkStack.cpp 
#include <stdio.h>
#include "LinkStack.h"
#include "linklist.cpp"//不要包含linklist.h,编译模板类包含linklist.cpp,不然老是报错找不到函数

template <typename T>
LinkStack<T>::LinkStack(void)
{
	this->list = new LinkList<T>;	//创建栈就相当于创建线性表
}

template <typename T>
LinkStack<T>::~LinkStack(void)
{
	clear();
	delete list;
	list = NULL;
}

template <typename T>
void LinkStack<T>::clear()
{
	list->clear();
}

template <typename T>
int LinkStack<T>::push(T &t)
{
	return list->insert(t,0);	//向栈中添加元素
}

template <typename T>
int LinkStack<T>::pop(T &t)
{
	return this->list->del(0,t);//传出元素,删除头部元素
}

template <typename T>
int LinkStack<T>::top(T &t)
{
	return this->list->get(0,t);//获取头部元素
}

template <typename T>
int LinkStack<T>::size()
{
	return this->list->len();
}

chunli@http://990487026.blog.51cto.com~/c++/link_stack$


LinkStack.h 文件

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ cat LinkStack.h
#pragma once
#include "linklist.h"

template <typename T>
class LinkStack
{
public:
	LinkStack(void);
	~LinkStack(void);
	void clear();
	int push(T &t);
	int pop(T &t);
	int top(T &t);
	int size();
private:
	LinkList<T> *list;//实例化一个链表	
};
chunli@http://990487026.blog.51cto.com~/c++/link_stack$



main.cpp 文件

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ cat main.cpp 
/*
g++ 编译模板类 ,老是报错,烦啊!!!
chunli@http://990487026.blog.51cto.com~/c++/link_stack$ g++ main.cpp LinkStack.cpp linklist.cpp 
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::LinkStack()‘:
main.cpp:(.text._ZN9LinkStackI7TeacherEC2Ev[_ZN9LinkStackI7TeacherEC5Ev]+0x20): undefined reference to `LinkList<Teacher>::LinkList()‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::~LinkStack()‘:
main.cpp:(.text._ZN9LinkStackI7TeacherED2Ev[_ZN9LinkStackI7TeacherED5Ev]+0x29): undefined reference to `LinkList<Teacher>::~LinkList()‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::push(Teacher&)‘:
main.cpp:(.text._ZN9LinkStackI7TeacherE4pushERS0_[_ZN9LinkStackI7TeacherE4pushERS0_]+0x27): undefined reference to `LinkList<Teacher>::insert(Teacher&, int)‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::top(Teacher&)‘:
main.cpp:(.text._ZN9LinkStackI7TeacherE3topERS0_[_ZN9LinkStackI7TeacherE3topERS0_]+0x24): undefined reference to `LinkList<Teacher>::get(int, Teacher&)‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::size()‘:
main.cpp:(.text._ZN9LinkStackI7TeacherE4sizeEv[_ZN9LinkStackI7TeacherE4sizeEv]+0x17): undefined reference to `LinkList<Teacher>::len()‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::pop(Teacher&)‘:
main.cpp:(.text._ZN9LinkStackI7TeacherE3popERS0_[_ZN9LinkStackI7TeacherE3popERS0_]+0x24): undefined reference to `LinkList<Teacher>::del(int, Teacher&)‘
/tmp/ccd3WOZY.o: In function `LinkStack<Teacher>::clear()‘:
main.cpp:(.text._ZN9LinkStackI7TeacherE5clearEv[_ZN9LinkStackI7TeacherE5clearEv]+0x17): undefined reference to `LinkList<Teacher>::clear()‘
collect2: error: ld returned 1 exit status
chunli@http://990487026.blog.51cto.com~/c++/link_stack$

g++ 编译模板类的两种方式
关于模板类声明与实现分离(即声明放在.h文件,实现放在.cpp文件)的测试。
最近在写模板类的时候,分开编译模板声明和模板实现老实编译不过。看提示应该是链接不到实现的函数。
原来原因是模板的具体定义要在模板参数确定了之后才能实例化。
而在模板实例化的过程中(比如在main函数中,但只包含模板声明函数)。包含实例化的.cpp文件编译成.o文件,留了类函数的入口地址等待填充。
一般,包含普通函数的.cpp文件编译成.o文件时。函数是确定的,能编译成二进制代码。然后就有函数入口地址可以让链接程序填入调用了这个函数的.o文件中。然后函数调用就成功了。
但是,对于模板类的实现。编译成.o文件时,仍然没有实例化。就是说模板类的实现函数不知道具体的模板函数是什么,不能实例化成一个真正的类型.
(比如Vector<int> a ; //参数实例化成int,vector<int>为一个类型( 和内置类型double一样了 ))。
但是没有实例化前,函数是不确定的。就是还没有编译成二进制文件。所以没有函数的入口地址提供。由于没有入口地址,链接程序在帮main函数找实例化成具体类型的模板找实现函数时找不到,就提示链接错误了。

既然知道哪里粗了问题,就可以解决问题了。解决的办法就是让实例化了模板的.cpp文件可以看到函数实现就行了。

1、直接把函数声明和函数实现全部放在同一个.h文件里面。这样引用.h文件的时候,声明和实现都直接展开在实例化的.cpp文件里面这样编译的时候就能找到函数的入口地址了。
2、函数声明和函数实现分开实现。在实例化模板的地方#include "xxx.cpp",即直接包含.cpp文件,这样也能找到函数入口。本程序引用这种方法.
*/


#include <iostream>
#include "LinkStack.cpp"
using namespace std;
struct Teacher
{
	char name[64];
	int age;
};

int main()
{
	//创建栈
	LinkStack<Teacher> stack;//实例化类

	struct Teacher  tmp;
	struct Teacher  t1;  t1.age = 31;
	struct Teacher  t2;  t2.age = 32;
	struct Teacher  t3;  t3.age = 33;
	//入栈
	stack.push(t1);
	stack.push(t2);
	stack.push(t3);

	//谁是栈顶
	stack.top(tmp);
	cout << tmp.age<<" top \n";
	//清空栈
	stack.clear();
	//入栈
	stack.push(t1);
	stack.push(t2);
	stack.push(t3);

	//栈的删除
	while(stack.size())
	{
		stack.pop(tmp);
		cout << tmp.age<<" pop \n";
	}

	return 0;
}
chunli@http://990487026.blog.51cto.com~/c++/link_stack$


编译运行:

chunli@http://990487026.blog.51cto.com~/c++/link_stack$ g++ main.cpp LinkStack.cpp linklist.cpp -Wall  && ./a.out 
33 top 
33 pop 
32 pop 
31 pop 
chunli@http://990487026.blog.51cto.com~/c++/link_stack$


技术分享

本文谢绝转载原文来自http://990487026.blog.51cto.com

本文出自 “魂斗罗” 博客,谢绝转载!

数据结构与算 5:C++ 顺序/链式存储,栈 模板类实现,编译模板类问题解决