C++程序设计:原理与实践读书笔记(第十四章)

本文介绍了C++中的泛型编程和模板的使用,包括类模板和函数模板。泛型编程允许编写适用于多种类型的代码,提高代码的复用性和灵活性。模板用于创建如vector和array等容器,可以接受不同类型如double、int、string等。C++14引入了显示声明来改善模板接口检查。文章还讨论了模板实例化、模板实参的推断,并强调了模板在高性能和灵活组合信息场景下的应用。同时,提到了资源管理,如unique_ptr的使用,确保对象生命周期内的资源安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类型作为模板参数,我们用T代替——T是一个参数,能被赋予double、int、string、vector<Record>和Window*之类的“值”。在C++中引入类型参数T的语法为template<typename T>前缀,其含义是“对所有类型T”。例如:

template<typename T>
class vector{
    int sz;                //大小
    T* elem;               //指向元素的指针
    int space;             //大小 + 空闲空间
public:
    vector():sz{0}, elem{nullptr}, space{0}{}
    explicit vector(int s):sz{s}, elem{new T[s]}, space{s}
    {
        for(int i=0;i<sz;i++) elem[i]=0;        //元素被初始化
    }
    vector(const vector&);                      //拷贝构造函数
    vector& operator=(const vector&);           //拷贝赋值
    vector(vector&&);                           //移动构造函数
    vector& operator=(vector&&);                //移动赋值
    ~vector(){delete[] elem;}                   //析构函数
    T& operator[](int n){return elem[n];}       //访问:返回引用
    const T& operator[](int n){return elem[n];}
    int size() const {return sz;}               //当前大小
    int capacity() const {return space;}
    void resize(int newsize);                   //增长
    void push_back(const T& d);
    void reserve(int newalloc);
};

我们可以像下面这样使用模板vector:

vector<double> vd;        //T为double
vector<int> vi;           //T为int
vector<double*> vpd;      //T为double*
vector<vector<int>> vvi;  //T为vector<int>, 其中T为int

我们有时称类模板为类型生成器,称由一个类模板按给定的模板实参生成类型(类)的过程为特例化或模板实例化。例如,vector<char>和vector<Poly_line*>被称为vector的特例化版本。

在使用这种类模板成员函数时,编译器将生成适合的函数。例如,当编译器遇到v.push_back("Norah")时,它会根据模板定义

template<typename T> void vector<T>::push_back(const T& d) {/*...*/);

生成函数

void vector<string>::push_back(const string& d){/*...*/);

泛型编程:编写能够正确处理以参数形式呈现的各种类型的代码,只要这些参数类型满足特定的语法和语义要求。

我们应该:

  • 在对性能要求高的场合使用模板。
  • 在需要灵活组合不同类型信息的场合中使用模板。

C++14提供了一种机制,可以极大地改善模板接口的检查。例如:

//C++11中无法准确陈述对实参T的期望
template<typename T>                    //对所有类型的T
class vector{
    //...
};

//C++14中则增加了显示陈述        
template<typename T>                    //对所有类型的T
    requires Element<t>()               //对所有满足Element的类型T
class vector{
    //...
};

//显示陈述可简化为
template<Element T>                     //对所有令Element<T>()为true的类型T
class vector{
    //...
};

对于任意模板C,“D是B”并不意味着“C<D> 是 C<B>”。

本质上,任何类别的实参都是有用的,但实际上我们只考虑类型和整数作为参数。

template<typename T, int N> struct array{
    T elem[N];            //在成员数组中保存元素
    
    //依赖于默认构造函数、析构函数和赋值操作
    T& operator[](int n);            //访问:返回引用
    constT& operator[](int n) const;

    T* data(){return elem;)          //转换为T*
    const T* data() const {return elem;}

    int size() const{return N;}
};

我们可以像下面这样使用array

array<int, 256> gb;            //256个整数
array<double, 6> ad = {0.0, 1.1, 2.2, 3.3, 4.4, 5.5};
const int max = 1024;

void some_fct(int n)
{
    array<char, max> loc;
    array<char, n> oops;            //错误:编译器不知道n的值
    //...
    array<char, max> loc2 = loc;    //创建副本作为备份
    //...
    loc = loc2;                     //恢复数据
    //...
}

对于一个类模板,当你创建特定类的对象时,需要指定模板实参。例如:

array<char, 1024> buf;        //对buf, T是char 且 N是1024
array<double, 10> b2;         //对b2, T是double 且 N是10

对于函数模板,编译器通常能够根据函数实参判断出模板参数。例如:

template<class T, int N> void fill(array<T, N>& b, const T& val)
{
    for (int i=0;i<N;++i) b[i] = val;
}

void f()
{
    fill(buf, 'x');            //对fill(), T是char且N是1024
    fill(b2, 0.0);             //对fill(), T是double且N是10
}

在技术上,fill(buf, 'x')是fill<char, 1024>(buf, 'x')的简写。fill(b2,0)是fill<double, 10>(b2,0)的简写。

我们可以不必再代码中添加复杂的try...catch语句就能有效处理潜在的资源泄露问题。资源应由构造函数获取,由析构函数释放。

在<memory>标准库中提供了unique_ptr,它是一种存储指针的对象。当销毁unique_ptr时,它会delete所指向的对象。但它有一个重要的限制:你不能将一个unique_ptr对象赋予另一个unique_ptr从而将它们指向相同的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值