栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++(三):模板

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++(三):模板

C++:模板
  • 1. 前言
  • 2. 模板函数
    • 2.1 示例
      • 2.1.1 输出不同类型的队列
      • 2.1.2 比较大小(compare)
    • 2.2 函数模板与函数的区别
  • 3. 非类型模板形参
  • 4. 模板类
    • 4.1 定义
    • 4.2 示例:queue
      • 4.3 函数模板特化
  • 5. 智能指针AutoPtr
    • 5.1 AutoPtr
    • 5.2 示例
  • 总结

1. 前言

C++最重要的特征之一就是代码重用,为了实现这一操作,代码必须具有通用性。通用代码不受数据类型的影响,并且可以自动适应数据类型的变化。为了实现以上操作,我们需要运用到模板,模板C++支持参数化程序设计的工具,通过其可以实现参数多态性。参数多态性就是将程序所处理的对象的类型参数化,使得一段程序可以用于处理多种不同类型的对象。

2. 模板函数

函数模板用于生成函数,函数模板的定义形式:

template<模板参数表>
类型名 函数名(参数表){
	函数体定义
}
2.1 示例 2.1.1 输出不同类型的队列

例子:

#include
using namespace std;

template 

void outputArray(const T* array, int count) {
    for (int i = 0; i < count; i++)
        cout << array[i] << "";
    cout << endl;
}

int main() {
    const int A_COUNT = 8, B_COUNT = 8, C_COUNT = 20;
    int a[A_COUNT] = { 1,2,3,4,5,6,7,8 };
    double b[B_COUNT] = { 1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8 };
    char c[C_COUNT] = "welcome to see you!";

    cout << "a array contains:" << endl;
    outputArray(a, A_COUNT);
    cout << "b array contains:" << endl;
    outputArray(b, B_COUNT);
    cout << "c array contains:" << endl;
    outputArray(c, C_COUNT);

    return 0;

}

结果:

函数模板中声明了类形参数T,表示一种抽象类型。当编译器检测到程序中调用函数outputArray时,便用outputArray的第一个实参的类型替换掉整个模板定义中的T,并建立用来输出指定类型数组的一个完整函数,然后在编译新建的函数。

2.1.2 比较大小(compare)

代码:

int compare(const Type& v1, const Type& v2) {
    if (v1 < v2) return -1;
    if (v1 > v2) return 1;
    return 0;
}
int main() {
    const char* cp1 = "world", * cp2 = "hi";
    int i1 = 1, i2 = 2;
    
    cout << compare(i2, i1) << endl;
    cout< 

结果:

同上。

2.2 函数模板与函数的区别
  1. 函数模板本身在编译时不会生成任何目标代码,只有由模板生成的实例会生成目标代码
  2. 被多个源文件引用的函数模板,应当连同函数体一同放在头文件中,而不能像普通函数一样只将声明放在头文件中。
  3. 函数指针也只能指向模板的实例,而不能指向模板本身。
3. 非类型模板形参
  1. 模板的非类型形参就是内置类型形参,如templateclass B{};其中int a就是非类型的模板
  2. 非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板内部是常量
  3. 非类型模板的形参只能是整型,指针和引用,double,string,string**这样的类型是不允许的,但是double &,double *对象的引用或指针是允许的
  4. 调用非类型模板形参的实参必须是一个常量表达式,也就是说他必须能在编译时计算出结果
  5. 任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
  6. 全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
  7. sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
  8. 当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如template class A{};如果有int b,这时A m;将出错,因为b不是常量,如果const int b,这时A m;就是正确的,因为这时b是常量。
  9. 非类型形参一般不应用于函数模板中
  10. 非类型模板形参的形参和实参间允许以下转换。

数组到指针,函数到指针:如:template class A{}; int b[1]; A m;即数组到指针的转换。
const修饰符的转换:如:template class A{}; int b; A<&b> m; 即从int *到const int *的转换。
提升转换:如:template class A{}; const short b=2; A m; 即从short到int 的提升转换
整值转换:如:template class A{}; A<3> m; 即从int 到unsigned int的转换。
常规转换

4. 模板类

使用类模板使用户可以为类定义的一种模式,使得类中的某些数据成员、某些成员函数的参数,返回值或局部变量能取不同类型。

4.1 定义
template <模板参数表>
class 类名{
	类成员声明
}

注:类中类成员的声明的方法与普通类的定义几乎相同,只是在他的各个成员(数据成员和函数成员)中通常要用到模板的类型参数T

在类模板以外定义其成员函数的形式:

template<模板参数表>
类型名 类目<模板参数标识符列表>::函数名(参数表)

使用模板类来建立对象时,应按如下形式声明:

模板名<模板参数表> 对象名1,...,对象名 n;
4.2 示例:queue

代码:
queue.cpp

#include "queue.h"
#include
#include
#include
using namespace std;

template<>
void Queue::push(const char* const& str) {
	char* pVal = new char[strlen(str) + 1];
	strcpy(pVal, str);
	QueueItem* p = new QueueItem(pVal);
	if (empty()) {
		head = tail = p;
	}
	else {
		tail->next = p;
		tail = p;
	}

}

template<>
void Queue::pop() {
	if (empty()) {
		return ;
	}
	QueueItem* p = head;
	head = head->next;
	delete[]p->item;
	delete p;
}

template<>
int compare(const char* const &a, const char* const &b) {
	return strcmp(a, b);
}


//void testTemplate()
int main() {
	double a = 1.2;
	double b = 1.5;
	cout << compare(a, b)< qt;
	double d = 3.3;
	qt.push(1);
	qt.push(d);
	qt.push(10);
	cout << qt< qi(data, data + 5);
	cout << qi<<" "< vi(data, data + 5);
	qi.assign(vi.begin(), vi.end());
	cout << endl;
	cout << qi;

	Queue qst;
	char str[10];
	strcpy(str, "I am ");
	qst.push(str);
	strcpy(str, "Jiawen ");
	qst.push(str);
	strcpy(str, "Zheng");
	qst.push(str);
	cout << endl << qst<(str, str1) << endl;
	cout << "2 1 " <(str1, str) << endl;

}

queue.h

#ifndef QUEUE_H
#define QUEUE_H
#include 
using namespace std;

template class Queue;
template class QueueItem {
    QueueItem(const Type& t) :item(t), next(0) {}
    Type item;
    QueueItem* next;
    friend class Queue;
    friend ostream& operator<<(ostream& os, const Queue& q);
public:
    QueueItem* operator++() {
        return next;
    }
    Type& operator*() {
        return item;
    }
};
template class Queue {
public:
    //队列初始化
    Queue() :head(0), tail(0) {}
    Queue(const Queue& q) :head(0), tail(0) {
        copy_items(q);
    }

    //It 模板,可以适应所有类型的变量
    template
    Queue(It beg, It end) : head(0), tail(0) {
        copy_items(beg, end);
    }

    template void assign(It beg, It end);
    Queue& operator=(const Queue&);

    ~Queue() {
        destroy();
    }

    Type& front() {
        return head->item;
    }

    const Type& front() const {
        return head->item;
    }

    void push(const Type&);

    void pop();

    bool empty() const {
        return head == 0;
    }

    friend ostream& operator<<(ostream& os, const Queue& q) {
        os << "<";
        QueueItem* p;
        for (p = q.head; p; p = p->next) {
            os << p->item << "";
        }
        os << ">";
        return os;
    }

    const QueueItem* Head() const {
        return head;
    }

    const QueueItem* End() const {
        return (tail == NULL) ? NULL : tail->naxt;
    }

private:
    QueueItem* head;
    QueueItem* tail;
    void destroy();
    void copy_items(const Queue&);
    template void copy_items(It beg, It end);
};

template void Queue::destroy() {
    while (!empty()) {
        pop();
    }
}
template  void Queue::pop() {
    QueueItem* p = head;
    head = head->next;
    delete p;
}

template  void Queue::push(const Type& val) {
    QueueItem* pt = new QueueItem(val);
    if (empty()) {
        head = tail = pt;
    }
    else {
        tail->next = pt;
        tail = pt;
    }
}
template<>
void Queue::push(const char* const& val);

template <>
void Queue::pop();

template 
void Queue::copy_items(const Queue& orig) {
    for (QueueItem* pt = orig.head; pt; pt = pt->next) {
        push(pt->item);
    }
}
template 
Queue& Queue::operator=(const Queue& q) {
    destroy();
    copy_items(q);
}

template  template void Queue::assign(It beg, It end) {
    destroy();
    copy_items(beg, end);
}

template  template void Queue::copy_items(It beg, It end) {
    while (beg != end) {
        push(*beg);
        ++beg;
    }
}

template 
int compare(const Type& v1, const Type& v2) {
    if (v1 < v2) return -1;
    else if (v1 > v2) return 1;
    else return 0;
}

template <>
int compare(const char* const& v1, const char* const& v2);


#endif // QUEUE_H

结果:

出现的问题:
在编写int compare(const char* const &a, const char* const &b)函数时一直出现未找到,实例的错误,原因是头函数中的定义是:

template <>
int compare(const char* const& v1, const char* const& v2);

而我在cpp文件中写的是:

template<>
int compare(const char* const a, const char* const b) {
	return strcmp(a, b);
}

少了一个&,所以还是要细心点

4.3 函数模板特化

申明在头文件中,实现在cpp文件中

  • 申明(头文件中)
template <>
int compare(const char* const& v1, const char* const& v2);
  • 实现(cpp文件中)
template<>
int compare(const char* const a, const char* const b) {
	return strcmp(a, b);
}
5. 智能指针AutoPtr 5.1 AutoPtr

auto_ptr 是C++标准库提供的类模板,auto_ptr对象通过初始化指向由new创建的动态内存,它是这块内存的拥有者,一块内存不能同时被分给两个拥有者。当auto_ptr对象生命周期结束时,其析构函数会将auto_ptr对象拥有的动态内存自动释放。即使发生异常,通过异常的栈展开过程也能将动态内存释放。auto_ptr不支持new 数组。
头文件为:#include

5.2 示例

代码:
Autoptr.h

#ifndef AUTOPTR_H
#define AUTOPTR_H
#include
using namespace std;

template
class AutoPtr {
public:
	AutoPtr(T* pData);//构造函数
	AutoPtr(const AutoPtr& h);
	AutoPtr& operator = (const AutoPtr& h);
	
	~AutoPtr();

	//运算符重载
	T* operator->() {
		return m_pData;
	}

	//返回的指针不允许改变
	const T* operator->() const {
		return m_pData;
	}

	T& operator*() {
		return *m_pData;
	}

	const T& operator*() const {
		return *m_pData;
	}

	

private:
	void decrUser();//减一操作
	T* m_pData;//数据
	int* m_nUser; //用户数,用*直接指向user
};

//template
//ostream& operator<< (ostream& os, const T& m);

//构造函数
template
AutoPtr::AutoPtr(T* pData) {
	m_pData = pData;
	m_nUser = new int(1);//记录用户数,该语句表示,new出一个int并且初始化为1
	
}

//拷贝操作
template
AutoPtr::AutoPtr(const AutoPtr& h) {
	m_pData = h.m_pData;
	m_nUser = h.m_nUser;
	(*m_nUser)++;
}


//生命周期结束,析构函数,用户数减一
template
AutoPtr::~AutoPtr() {
	decrUser();
}

template
AutoPtr& AutoPtr::operator=(const AutoPtr& h) {
	
	if (this == &h) return* this;
	decrUser();
	m_pData = h.m_pData;
	m_nUser = h.m_nUser;
	(*m_nUser)++;
	return *this;
}


//减少用户数
template
void AutoPtr::decrUser() {
	--(*m_nUser);
	if ((*m_nUser) == 0) {//当用户数为0时
		delete m_pData;
		m_pData = 0;
		delete m_nUser;
		m_nUser = 0; 
	}
}
#endif


autoptr.cpp

#include"CMatrix.h"
#include"autoptr.h"
#include
#include
using namespace std;


void TestAutoPtr() {
	AutoPtr m1(new CMatrix);
	double data[6] = { 1,2,3,4,5,6 };
	m1->Create(2, 3, data);

	cout << m1;

	AutoPtr m2(m1);
	(*m2).Set(0, 0, 10);
	cout << m1 << m2;
}

int main() {
	TestAutoPtr();
	return 0;

}

结果:

问题:

  • 无法输出的问题
    在进行测试时<<报错
总结

模板是C++支持参数化多态性的工具,函数模板实现理类型参数化,将函数处理的数据类型作为参数,提高了代码的可重用性。类模板使用户可以为类定义一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值能取不同类型。
参考
1
2

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/511517.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号