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

C++面向对象10:模板

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

C++面向对象10:模板

目录

1.函数模板

2.类模板与模板类

类模板

3.模板的非类参数

非类型参数例子:

4.OpenCV中的类模板

5.类模板的特化

6.标准库中的类


1.函数模板

函数模板不是类型、函数或任何其他实体。它是一个空壳子

如果只是定义了一个模板。在编译的时候编译器看到这个模板,其实他不做任何事情。当模板的参数被指定了这个时候变压器会生成一个具体的函数。

函数模板:空壳子

template
T sum(T x, T y)
{
    cout << "The input type is " << typeid(T).name() << endl;
    return x + y;
}

模板函数:在函数模板被实例化以后生成的具体函数

显式实例化

// instantiates sum(double, double)
template double sum(double, double);
// instantiates sum(char, char), template argument deduced
template char sum<>(char, char); 
// instantiates sum(int, int), template argument deduced
template int sum(int, int);

三个函数的写法可能不同,但是都是对模板的实例化。

对T替换

隐式实例化

template
T product(T x, T y)
{
    cout << "The input type is " << typeid(T).name() << endl;
    return x * y;
}
// Implicitly instantiates product(int, int)
cout << "product = " << product(2.2f, 3.0f) << endl;//强制转化成整数。
// Implicitly instantiates product(float, float)
cout << "product = " << product(2.2f, 3.0f) << endl;//实力化一个浮点数的函数。

2.类模板与模板类

如果不使用模板的话,要让类支持多个数据类型是比较麻烦的。

完整代码 

1.删除默认函数。

2.想复用代码,用拷贝粘贴的方式是可以的。但比较麻烦,容易出错。

#include 
using namespace std;

// Class IntMat
class IntMat
{
    size_t rows;
    size_t cols;
    int * data;
  public:
    IntMat(size_t rows, size_t cols): 
                    rows(rows), cols(cols)
    {
        data = new int[rows * cols * sizeof(int)]{};
    }
    ~IntMat()
    {
        delete [] data;
    }
    IntMat(const IntMat&) = delete;//如果不写delete。编译器就会自动提供一个默认的复制构造函数。=delete告诉编译器,我不要这个东西。就是不允许生成默认的来防止内存发生问题。
    IntMat& operator=(const IntMat&) = delete;//赋值运算符重载
    int getElement(size_t r, size_t c);//获取像素。
    bool setElement(size_t r, size_t c, int value);//设置像素
};
int IntMat::getElement(size_t r, size_t c)
{//检查有没有越界。
    if ( r >= this->rows || c >= this->cols)
    {
        cerr << "Indices are out of range" << endl;
        return 0;
    }
    return data[ this->cols * r + c];
}
bool IntMat::setElement(size_t r, size_t c, int value)
{
    if ( r >= this->rows || c >= this->cols)
        return false;

    data[ this->cols * r + c] = value;
    return true;
}

// Class FloatMat
class FloatMat
{
    size_t rows;
    size_t cols;
    float * data;
  public:
    FloatMat(size_t rows, size_t cols): 
                    rows(rows), cols(cols)
    {
        data = new float[rows * cols * sizeof(float)]{};
    }
    ~FloatMat()
    {
        delete [] data;
    }
    FloatMat(const FloatMat&) = delete;
    FloatMat& operator=(const FloatMat&) = delete;
    float getElement(size_t r, size_t c);
    bool setElement(size_t r, size_t c, float value);
};
float FloatMat::getElement(size_t r, size_t c)
{
    if ( r >= this->rows || c >= this->cols)
    {
        cerr << "getElement(): Indices are out of range" << endl;
        return 0.f;
    }
    return data[ this->cols * r + c];
}
bool FloatMat::setElement(size_t r, size_t c, float value)
{
    if ( r >= this->rows || c >= this->cols)
    {
        cerr << "setElement(): Indices are out of range" << endl;
        return false;
    }
    data[ this->cols * r + c] = value;
    return true;
}

int main()
{
    IntMat imat(3,4);
    imat.setElement(1, 2, 256);
    FloatMat fmat(2,3);
    fmat.setElement(1, 2, 3.14159f);

    // FloatMat fmat2(fmat); //error//应该调用复制构造函数。是被删除的,不能再去用了。

    // FloatMat fmat3(2,3); 
    // fmat3 = fmat; //error

    cout << imat.getElement(1,2) << endl;
    cout << fmat.getElement(1,2) << endl;
    
    return 0;
}

类模板
//可以写typename也可以写class,老的版本只能写class,Class有两层意思,
//第1层是类的名字。第二层意思在模板定义的时候来告诉编译器后面这个名字实际上是一个泛型(类型变量T)。 
template
class Mat
{
    size_t rows;
    size_t cols;
    T * data;
  public:
    Mat(size_t rows, size_t cols): rows(rows), cols(cols)
    {
        data = new T[rows * cols * sizeof(T)]{};
    }
    ~Mat()
    {
        delete [] data;
    }
    T getElement(size_t r, size_t c);
    bool setElement(size_t r, size_t c, T value);
};

实例化

显式

template class Mat; 

把代码里面所有T换成int,生成一个int类型的mat类

完整代码

#include 
using namespace std;

// Class Template
template
class Mat
{
    size_t rows;
    size_t cols;
    T * data;
  public:
    Mat(size_t rows, size_t cols): rows(rows), cols(cols)
    {
        data = new T[rows * cols * sizeof(T)]{};
    }
    ~Mat()
    {
        delete [] data;
    }
    Mat(const Mat&) = delete;
    Mat& operator=(const Mat&) = delete;
    T getElement(size_t r, size_t c);
    bool setElement(size_t r, size_t c, T value);
};
//函数模板,一定要加template 

template 
T Mat::getElement(size_t r, size_t c)
{
    if ( r >= this->rows || c >= this->cols)
    {
        cerr << "getElement(): Indices are out of range" << endl;
        return 0;
    }
    return data[ this->cols * r + c];
}
template 
bool Mat::setElement(size_t r, size_t c, T value)
{
    if ( r >= this->rows || c >= this->cols)
    {
        cerr << "setElement(): Indices are out of range" << endl;
        return false;
    }

    data[ this->cols * r + c] = value;
    return true;
}
//显式实例化
template class Mat; // Explicitly instantiate template Mat
//template Mat and Mat will be instantiate implicitly
int main()
{
    Mat imat(3,4);//隐式实例化
    imat.setElement(1, 2, 256);
    Mat fmat(2,3);
    fmat.setElement(1, 2, 3.14159f);
    Mat dmat(2,3);
    dmat.setElement(1, 2, 2.718281828);

    // Mat fmat2(fmat); //error

    // Mat fmat3(2,3);
    // fmat3 = fmat; //error

    cout << imat.getElement(1,2) << endl;
    cout << fmat.getElement(1,2) << endl;
    cout << dmat.getElement(1,2) << endl;
    
    return 0;
}

3.模板的非类参数
template <参数列表> declaration 

参数列表:

可以是一个参数,也可以是很多个。

参数可以是:

类型模板参数(如int)

模板参数(template本身可以做类型,template套template)

非类型模板参数:(它是具体的类型,不是变的而是定的。)

整型(short,int,char )

浮点型(float,double)

指针类型

左值引用类型

vector vec1;
vector vec2;//int是类型,16是非类型,16用来表示元素个数。也可以是向量的其他属性。

declaration(声明):可以是一个函数,也可以是一个类。所以它就可以创建一个函数模板或类模板。

非类型参数例子:
template// T表示元素类型,类里面没有了成员变量。非类型参数要在编译的时候就要确定它。这个矩阵是几行几页?在编译的时候就已经定死,而不是动态申请的。
class Mat
{
    T data[rows][cols];
  public:
    Mat(){}
    T getElement(size_t r, size_t c);
    bool setElement(size_t r, size_t c, T value);
};
Mat vec1(3, 3);
Mat vec2;//构造函数里面不需要再去动态申请的机构,析构函数也不需要释放了。

完整代码

#include 
using namespace std;

// Class Template
template
class Mat
{
    T data[rows][cols];
  public:
    Mat(){}
     the default copy constructor will copy each element of a static array member
     so we do not 'delete' the copy constructor
 the same with the assignment operator
他是静态申请内存,所以就没必要删除了。
    // Mat(const Mat&) = delete;
    // Mat& operator=(const Mat&) = delete;
    T getElement(size_t r, size_t c);
    bool setElement(size_t r, size_t c, T value);
};
template
T Mat::getElement(size_t r, size_t c)
{
    if ( r >= rows || c >= cols)
    {
        cerr << "getElement(): indices are out of range" << endl;
        return 0;
    }
    return data[r][c];
}
template
bool Mat::setElement(size_t r, size_t c, T value)
{
    if ( r >= rows || c >= cols)// rows, cols不再是成员变量。
    {
        cerr << "setElement(): Indices are out of range" << endl;
        return false;
    }

    data[r][c] = value;
    return true;
}

template class Mat; // Explicitly instantiate template Mat
typedef Mat Mat22i;

//template Mat will be instantiate implicitly

int main()
{
    Mat22i mat;
	//越界
    mat.setElement(2, 3, 256);
    cout << mat.getElement(2, 3) << endl;

    mat.setElement(1, 1, 256);
    cout << mat.getElement(1, 1) << endl;

    Mat vec;//隐式实力化
    vec.setElement(2, 0, 3.14159f);
    cout << vec.getElement(2, 0) << endl;

    Mat vec2(vec);//调用了默认复制构造函数。
如果两个数组直接用默认的复制构造函数或者复制操作服务。把一个对象复制给另外一个对象。如果它里面有静态数据。静态数组的每个元素都可以这样拷贝过去。这样的话就不会有内存管理的问题。
    cout << vec2.getElement(2, 0) << endl;

    // vec2 = mat; //error两个类型不匹配,无法赋值。除非重载赋值运算符。

    return 0;
}

4.OpenCV中的类模板

5.类模板的特化
template
class MyVector
{
    size_t length;
    T * data;
  public:
    MyVector(size_t length): length(length)
    { data = new T[length * sizeof(T)]{}; }
    ~MyVector()
    { delete [] data; }
    MyVector(const MyVector&) = delete;
    MyVector& operator=(const MyVector&) = delete;
    T getElement(size_t index);
    bool setElement(size_t index, T value);
};

bool类型占1个字节,因为内存管理的时候,字节是操作的最小单位。但bool设计0或1,1位就够了。但我们希望为bool类型(1字节或1位)节省内存。

特例化没有T

template<>
class MyVector
{
    size_t length;
    unsigned char * data;
  public:
    MyVector(size_t length): length(length)
    {
        int num_bytes = (length - 1) / 8 + 1;
        data = new unsigned char[num_bytes]{};
    }
    ~MyVector()
    {
        delete [] data;
    }
    MyVector(const MyVector&) = delete;
    MyVector& operator=(const MyVector&) = delete;
    bool getElement(size_t index);
    bool setElement(size_t index, bool value);
};

完整代码

#include 
using namespace std;

// Class Template
template
class MyVector
{
    size_t length;
    T * data;
  public:
    MyVector(size_t length): length(length)
    {
        data = new T[length * sizeof(T)]{};
    }
    ~MyVector()
    {
        delete [] data;
    }
    MyVector(const MyVector&) = delete;
    MyVector& operator=(const MyVector&) = delete;
    T getElement(size_t index);
    bool setElement(size_t index, T value);
};
template 
T MyVector::getElement(size_t index)
{
    if (index >= this->length)
    {
        cerr << "getElement(): Indices are out of range" << endl;
        return 0;
    }
    return data[index];
}
template 
bool MyVector::setElement(size_t index, T value)
{
    if (index >= this->length)
    {
        cerr << "setElement(): Indices are out of range" << endl;
        return false;
    }

    data[ index ] = value;
    return true;
}

template class MyVector; // Explicitly instantiate template Mat

// class specialization
template<>
class MyVector
{
    size_t length;
    unsigned char * data;
  public:
    MyVector(size_t length): length(length)
    {
        int num_bytes =  (length - 1) / 8 + 1;
        data = new unsigned char[num_bytes]{};
    }
    ~MyVector()
    {
        delete [] data;
    }
    MyVector(const MyVector&) = delete;
    MyVector& operator=(const MyVector&) = delete;
    bool getElement(size_t index);
    bool setElement(size_t index, bool value);
};
bool MyVector::getElement(size_t index)
{
    if (index >= this->length)
    {
        cerr << "getElement(): Indices are out of range" << endl;//越界
        return 0;
    }
    size_t byte_id = index / 8;在第几个字节
    size_t bit_id = index % 8;在第byte个字节的位程
    unsigned char mask = (1 << bit_id);
    return bool(data[byte_id] & mask) ;
}
bool MyVector::setElement(size_t index, bool value)
{
    if (index >= this->length)
    {
        cerr << "setElement(): Indices are out of range" << endl;
        return false;
    }

    size_t byte_id = index / 8;
    size_t bit_id = index % 8;
    unsigned char mask = (1 << bit_id);

    if (value)
        data[byte_id] |= mask; //位运算操作
    else 
        data[byte_id] &= ~mask;

    return true;
}

int main()
{
    MyVector vec(16);
    vec.setElement(3, 256);
    cout << vec.getElement(3) << endl;
    
    MyVector boolvec(17);17个元素占3个字节
    boolvec.setElement(15, false);
    boolvec.setElement(16, true);

    cout << boolvec.getElement(15) << endl;
    cout << boolvec.getElement(16) << endl;
    return 0;
}

6.标准库中的类

std::basic_string

basic_string是模板类

std::array

数组也是一个列表,但是它是固定长度的。

template<
    class T,  
    std::size_t N
> struct array;// struct 与class等价
*Keyword: typename/class, class/struct
std::array a2 = {1, 2, 3}; 

 其他一些模板

template<
    class T,
    class Allocator = std::allocator// std::allocator申请内存的对象
> class vector;//长度可变。

template<
    class T,
    class Allocator = std::allocator
> class list;
template<
    class Key,
    class Compare = std::less,
    class Allocator = std::allocator
> class set;//集合
template<
    class Key,
    class T,
    class Compare = std::less,
    class Allocator = std::allocator >
> class map;//映射
template<
    class T,
    class Container = std::deque
> class stack;//栈

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

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

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