c++第9章·群体类和群体数据的组织

群体数据:自定义类型的数据由多个基本类型或自定义类型的元素组成

群体类:对于群体数据,仅有系统预定义的操作是不够的,在很多情况下,还需要设计与某些具体问题相关的特殊操作,并按照面向对象的方法将数据与操作封装起来

群体

函数模板

template <class 类型参数1, class类型参数2, ...>
返回值类型  模板名(形参表)
{
    函数体
}

代码中的class也可换成typename

template<参数表>class标识符,指明可以接受一个类模板名作为参数

实例化

template <class T>
void Swap(T & x, T & y)
{
    T tmp = x;
    x = y;
    y = tmp;
}

T 是类型参数,代表类型。编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动地保留。同一个类型参数只能替换为同一种类型。编译器在编译到调用函数模板的语句时,会根据实参的类型判断该如何替换模板中的类型参数

#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
    T tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int n = 1, m = 2;
    Swap(n, m);  //编译器自动生成 void Swap (int &, int &)函数
    double f = 1.2, g = 2.3;
    Swap(f, g);  //编译器自动生成 void Swap (double &, double &)函数
    return 0;
}

编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误

编译器对模板进行实例化时,并非只能通过模板调用语句的实参来实例化模板中的类型参数,模板调用语句可以明确指明要把类型参数实例化为哪种类型。可以用:

模板名<实际类型参数1, 实际类型参数2, ...>
#include <iostream>
using namespace std;
template <class T>
T Inc(T n)
{
    return 1 + n;
}
int main()
{
    cout << Inc<double>(4) / 2;     //好好看看这里是怎么用的
    return 0;
}

此时,Inc返回的是2.5,不因为4是int而改变(因为指明了double),函数模板是不允许隐式类型转换的,调用时类型必须严格匹配

可以有不只一个类型参数

template <class Tl, class T2>
T2 print(T1 argl, T2 arg2)
{
    cout << arg1 << " " << arg2 << endl;
    return arg2;
}

注意

类模板

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

类是对一组对象的公共性质的抽象,而类模板则是对不同类的公共性质的抽象,因此类模板是属于更高层次的抽象。由于类模板需要一种或多种类型参数,所以类模板也常常称为参数化类

代码

template <typename T>
class Complex{
public:
    //构造函数
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }
    
    //运算符重载
    Complex<T> operator+(Complex &c)    \\Complex<T>是具化了返回类型
    {
        Complex<T> tmp(this->a+c.a, this->b+c.b);
        return tmp;
    }
        
private:
    T a;
    T b;
}

int main()
{
    //对象的定义,必须声明模板类型,因为要分配内容
    Complex<int> a(10,20);  
    Complex<int> b(20,30);
    Complex<int> c = a + b;
    
    return 0;
}

template <typename T>
class Blob
{
public:
    Blob();
    Blob(std::initializer_list<T> i);
    T func(T const &str);//在类内声明
};
 
//类外定义
template <typename T>
T Blob<T>::func(T const &str)    \\这里可以好好看看
{
	
}

建立对象:

有点像vector<类型名>

类模板还有很多知识,但是书上没有了,考试应该也不会涉及

线性群体

线性群体中的元素按位置排列有序

直接访问群体——数组类

#ifndef ARRAY_H
#define ARRAY_H
#include <cassert>

template<class T>
class Array {
private:
	T* list;  //T类型指针,用于存放动态分配的数组内存首地址
	int size; //数组大小(元素个数)
public:
	Array(int sz = 50); //构造函数
	Array(const Array<T>& a); //复制构造函数
	~Array(); //析构函数
	Array<T>& operator=(const Array<T>& rhs); //重载"="使数组对象可以整体赋值
	T& operator[](int i); //重载"[]",使Array对象可以起到C++普通数组的作用
	const T& operator [] (int i) const; //"[]"运算符针对const的重载
	operator T* (); //重载到T*类型的转换,使Array对象可以起到C++普通数组的作用
	operator const T* () const; //到T*类型转换操作符针对const的重载
	int getSize() const; //取数组的大小
	void resize(int sz); //修改数组的大小
};

//构造函数
template<class T>
Array<T>::Array(int sz) {
	assert(sz >= 0);
	size = sz;
	list = new T[size];
}

//析构函数
template<class T>
Array<T>::~Array() {
	delete[] list;
}

//复制构造函数
template<class T>
Array<T>::Array(const Array<T>& a) {
	size = a.size;
	list = new T[size];
	for (int i = 0; i < size; i++)
		list[i] = a.list[i];
}

//重载"="运算符,将对象rhs赋值给本对象,实现对象之间的整体赋值
template<class T>
Array<T>& Array<T>::operator=(const Array<T>& rhs) {
	if (&rhs != this) {
		if (size != rhs.size) {
			delete[] list;
			size = rhs.size;
			list = new T[size];
		}
		for (int i = 0; i < size; i++)
			list[i] = rhs.list[i];
	}
	return *this;              //因为返回的是引用类型,所以这里加了*,返回的引用会自动绑定
}

//重载下标运算符,实现与普通数组一样通过下标访问元素,并且具有越界检查功能
template<class T>
T& Array<T>::operator[] (int n) {
	assert(n >= 0 && n < size);
	return list[n];
}

template<class T>
const T& Array<T>::operator[] (int n) const {
	assert(n >= 0 && n < size);
	return list[n];
}

//重载指针转换运算符,将Array类的对象名转换为T类型的指针
template<class T>
Array<T>::operator T* () {            //因为返回的是地址,所以没有返回类型
	return list;
}

template<class T>
Array<T>::operator const T* () const {
	return list;
}

//取当前数组的大小
template<class T>
int Array<T>::getSize() const {
	return size;
}

//将数组大小修改为sz
template<class T>
void Array<T>::resize(int sz) {
	assert(sz >= 0);
	if (sz == size)
		return;
	T* newList = new T[sz];
	int n = (sz < size) ? sz : size;
	for (int i = 0; i < n; i++)
		newList[i] = list[i];
	delete[] list;
	list = newList;
	size = sz;             //这里不用delete[] newList,因为这一段内存空间已经给予了list了
}
#endif

注意看一下 =,T* 的运算符重载,返回类型是引用(原因后面有说)

浅复制与深复制

如果类模板中涉及到地址(或是如果类中成员函数有指针)则需要设计复制构造函数实现深复制(浅复制在之前已经说过——共用一段内存空间,在执行析构函数时会将两个对象都删掉)

特殊的运算符重载

返回值类型为对象的引用

简而言之——当这个运算符在我们一般写法是放在左边的,那么他的重载函数返回类型就得是引用(指针符例外)

指针转换运算符的作用

在上面的那段长代码中表现的就是operator T*

顺序访问群体——链表类

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值