C++函数模板、特例化、非类型参数、类模板、allocator

这篇具有很好参考价值的文章主要介绍了C++函数模板、特例化、非类型参数、类模板、allocator。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

函数模板

模板对类型能进行参数化成【模板参数】,输入的是类型,生成的是代码。使用的时候,每指定一份类型,模板就会根据类型生成一份新的代码(比如函数模板实例化生成的是【模板函数】),有利于减少代码量,通过较少的代码也能实现函数重载。

调用函数模板的时候,一般通过<>传入【模板参数】,也就是【类型参数】。编译器生成相应的函数代码之后,再通过()传入实参。

模板的实参推演:调用模板的时候可以根据用户传入的实参的类型,推导出模板类型参数的具体类型。

以下是一个简单的 C++ 函数模板案例,用于交换两个值:

#include <iostream>
// 定义一个函数模板,用于交换两个值的内容
template <typename T>// T是一个模板参数,它表示一个占位符,一个模板参数意味着一个模板需要接收一个类型。如果有多个模板参数,则需要接收多种类型。
void swapValues(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
0}

int main() {
    int x = 5, y = 10;
    double m = 3.14, n = 6.28;
    
    std::cout << "Before swapping: x = " << x << ", y = " << y << std::endl;
    swapValues<int>(x, y);//在函数调用点,编译器根据用户指定的类型,从原模板实例化一份代码出来,如果已经实例化代码了,则无需该代码实例化。再根据这个实例化的代码,传值参数。
    std::cout << "After swapping: x = " << x << ", y = " << y << std::endl;

    std::cout << "Before swapping: m = " << m << ", n = " << n << std::endl;
    swapValues(m, n);//模板的实参推演
    std::cout << "After swapping: m = " << m << ", n = " << n << std::endl;

    return 0;
}

注意:模板一般在头文件中定义,在源文件中进行#include包含使用。尽量不要在一个cpp文件中定义,在另一个cpp文件中使用。

模板特例化

函数模板特例化允许我们为特定类型或特定类型组合提供自定义的实现,以满足特殊需求或处理特定情况。

函数模板特例化与函数模板同名,于此同时也能和同名普通函数共存。

函数模板的特例化例子如下,细节是另起一个同名模板,但是关键字的使用方法有差异。

#include <iostream>
#include <cstring> // For strlen and strcpy

// 通用模板函数
template <typename T>
void swapValues(T &a, T &b) {
    T temp = a;
    a = b;
    b = temp;
}

// 模板特化,用于交换 const char* 类型的指针
template <>
void swapValues<const char*>(const char* &a, const char* &b) {
    // 计算字符串的长度
    size_t lengthA = std::strlen(a);
    size_t lengthB = std::strlen(b);

    // 创建临时缓冲区,用于交换字符串的内容
    char* temp = new char[lengthA + 1]; // +1 是为了容纳字符串末尾的空字符 '\0'

    // 拷贝字符串内容到临时缓冲区
    std::strcpy(temp, a);
    
    // 交换字符串指针
    delete[] a; // 释放原来指针 a 指向的内存
    a = new char[lengthB + 1]; // 为 a 分配新的内存空间
    std::strcpy(const_cast<char*>(a), b); // 使用 const_cast 将 const char* 转换为 char*,然后拷贝字符串内容到 a

    delete[] b; // 释放原来指针 b 指向的内存
    b = new char[lengthA + 1]; // 为 b 分配新的内存空间
    std::strcpy(const_cast<char*>(b), temp); // 使用 const_cast 将 const char* 转换为 char*,然后拷贝字符串内容到 b
    
    // 释放临时缓冲区
    delete[] temp;
}

int main() {
    const char* str1 = "Hello";
    const char* str2 = "World";

    std::cout << "Before swapping: str1 = " << str1 << ", str2 = " << str2 << std::endl;
    swapValues(str1, str2);
    std::cout << "After swapping: str1 = " << str1 << ", str2 = " << str2 << std::endl;

    return 0;

函数模板的非类型参数

模板的非类型参数,必须是整数类型(整数或者地址/引用都可以)。只能使用,不能修改。

实际上就相当于传入了一个参数。

#include <iostream>

// 函数模板,使用非类型参数来传递一个整数常量
template <int N>
void printNumber() {
    std::cout << "The number is: " << N << std::endl;
}

int main() {
    // 使用函数模板,并传递常量值作为非类型参数
    printNumber<5>(); // 输出: The number is: 5
    printNumber<10>(); // 输出: The number is: 10

    return 0;
}

类模板

类模板的规则其实与函数模板一样,都是通过传入类型参数生成模板类代码之后再调用。

使用类模板的时候,模板名+类型参数列表才构成了模板名称。但是我们一般在构造和析构函数名的后面不加模板参数列表(比如<T>),其它出现模板名的地方都加上参数类型列表。

本人习惯先用简单的类型参数实现一个类,然后再把这个类改成类模板,

以下是一个使用整数类型的顺序栈(SeqStack)类的示例实现:

#include <iostream>
#include <vector>

class SeqStack {
private:
    std::vector<int> elements;  // 用于存储栈元素的容器

public:
    void push(const int& element) {
        elements.push_back(element);
    }

    void pop() {
        if (!elements.empty()) {
            elements.pop_back();
        }
    }

    int& top() {
        return elements.back();
    }

    bool empty() const {
        return elements.empty();
    }
};

int main() {
    SeqStack stack;

    stack.push(10);
    stack.push(20);
    stack.push(30);

    std::cout << "Top element: " << stack.top() << std::endl;

    stack.pop();

    std::cout << "Top element after pop: " << stack.top() << std::endl;

    if (stack.empty()) {
        std::cout << "Stack is empty." << std::endl;
    } else {
        std::cout << "Stack is not empty." << std::endl;
    }

    return 0;
}

要将这个类改为类模板,需要进行以下修改:

在类定义前声明类模板,指定类型参数。例如,将类名改为 template <typename T> class SeqStack。

在类内部的成员函数和成员变量声明中,使用类型参数 T 代替具体的类型。例如,将 std::vector<int> 改为 std::vector<T>。其次在类内部使用类名的地方加上<T>(比如赋值函数的返回类型,拷贝构造函数和赋值函数的参数类型)。

在类外部定义成员函数时,也需要将函数模板化,并指定类型参数 T。例如,将 void push(const int& element) 改为 template <typename T> void SeqStack<T>::push(const T& element)。

修改后的代码示例如下:

#include <iostream>
#include <vector>

template <typename T>
class SeqStack {
private:
    std::vector<T> elements;  // 用于存储栈元素的容器

public:
    void push(const T& element) {
        elements.push_back(element);
    }

    void pop() {
        if (!elements.empty()) {
            elements.pop_back();
        }
    }

    T& top() {
        return elements.back();
    }

    bool empty() const {
        return elements.empty();
    }
};

int main() {
    SeqStack<int> stack;

    stack.push(10);
    stack.push(20);
    stack.push(30);

    std::cout << "Top element: " << stack.top() << std::endl;

    stack.pop();

    std::cout << "Top element after pop: " << stack.top() << std::endl;

    if (stack.empty()) {
        std::cout << "Stack is empty." << std::endl;
    } else {
        std::cout << "Stack is not empty." << std::endl;
    }

return 0;

allocator

我们调用类模板,传入自定的类型参数,如果模板里new了这个类型参数的数组。因为new的时候会初始化一个对象,所以会调用构造函数,数组大小为1000就创建了1000个类型对象并且调用了1000次该类型的构造函数,析构的时候就调用了1000次析构函数析构该数组。

为了避免这种问题,我们在构造函数里需要把内存开辟和对象构造分开。析构数组的时候需要只用析构容器有效的元素,然后释放数组的堆内存。删除数组元素的时候调用了对象的析构函数,释放元素对象占用的外部资源,而不是仅仅通过移动头尾指针表示删除了元素。

可以借用allocator来解决上述问题。Allocator做了四件事,内存开辟/内存释放,对象构造/对象析构。

C++中allocator的用法:

#include <iostream>
#include <memory> // For std::allocator

int main() {
    // 使用std::allocator分配和释放int类型的内存
    std::allocator<int> allocator;

    // 分配单个int对象的内存
    int* p = allocator.allocate(1);

    // 在分配的内存中构造对象
    allocator.construct(p, 42);

    // 使用分配的对象
    std::cout << *p << std::endl;

    // 销毁对象
    allocator.destroy(p);

	//释放数组内存
    allocator.deallocate(p, 1);

    return 0;
}

C++的容器模板都会把空间配置器写入模板代码中,防止重复开销。文章来源地址https://www.toymoban.com/news/detail-832325.html

到了这里,关于C++函数模板、特例化、非类型参数、类模板、allocator的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++学习】模板进阶——非类型模板参数 | 模板的特化 | 分离编译

    🐱作者:一只大喵咪1201 🐱专栏:《C++学习》 🔥格言: 你只管努力,剩下的交给时间! 模板我们之前一直都在使用,尤其是在模拟STL容器的时候,可以说,模板给类增加了更多的可能性,是C++最重要的部分之一。下面本喵来更深入的讲解一下模板。 在上面代码中,创建了

    2023年04月13日
    浏览(32)
  • 【C++初阶(十)】C++模板(进阶) ---非类型模板参数、模板的特化以及模板的分离编译

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 模板参数可分为类型形参和非类型形参。 类型形

    2024年01月18日
    浏览(35)
  • 【C++】泛型编程 ① ( 函数模板 | 函数模板概念 | 函数模板意义 | 函数模板定义语法 | 函数模板调用语法 | 显式类型调用 | 自动类型推导 )

    在 C++ 语言中 , 泛型编程 的 核心就是 函数模板 和 类模板 ; 函数模板 Function Template 是 C++ 语言 中的 重要特性 ; 函数模板概念 : 建立一个 \\\" 通用函数 \\\" , 不指定该函数的 函数返回值类型 和 函数参数类型 , 仅使用 \\\" 虚拟类型 \\\" 代表 上述 两种类型 , 该 \\\" 通用函数 \\\" 就是 \\\" 函数模

    2024年02月19日
    浏览(36)
  • 【C++】泛型编程 ③ ( 函数模板 与 普通函数 调用规则 | 类型匹配 | 显式指定函数模板泛型类型 )

    上一篇博客 【C++】泛型编程 ② ( 函数模板与普通函数区别 ) 中 , 分析了 函数参数 类型匹配 下的 普通函数 与 函数模板 的调用规则 ; 为 函数模板 重载了 普通函数 , 普通函数有指定的类型 ; 传入实参 , 调用 普通函数 还是 模板函数 , 是有一定的规则的 ; 普通函数 与 传入实

    2024年02月21日
    浏览(41)
  • 小议C++函数签名与模板返回类型

    题记:什么事情都要追问一个为什么,真正理解了为什么,才能活学活用。 下面的代码能编译通过吗? 答案肯定是编译不过。因为下面两个函数的“签名”是一样的: 在 C++ 语言中,函数签名包含函数名称、函数参数类型、函数参数个数等信息,但是不包含返回值类型。 下

    2024年02月08日
    浏览(43)
  • 【C++】C++ 引用详解 ① ( 变量的本质 - 引入 “ 引用 “ 概念 | 引用语法简介 | 引用做函数参数 | 复杂类型引用做函数参数 )

    \\\" 引用 \\\" 语法 是 C++ 语言中 特有的 , 在 C 语言中是没有 引用 这个概念的 ; 分析 引用 之前 , 先回顾下 变量 : 在 【C 语言】变量本质 ( 变量概念 | 变量本质 - 内存空间别名 | 变量存储位置 - 代码区 | 变量三要素 ) 博客中 , 介绍了变量的本质 : 变量 的本质是 内存空间 的 \\\" 别名

    2024年02月11日
    浏览(48)
  • C++ 具名要求-全库范围的概念 -包含分配信息的类类型(Allocator)

    此页面中列出的具名要求,是 C++ 标准的规范性文本中使用的具名要求,用于定义标准库的期待。 某些具名要求在 C++20 中正在以概念语言特性进行形式化。在那之前,确保以满足这些要求的模板实参实例化标准库模板是程序员的重担。若不这么做,则可能导致非常复杂的编译

    2024年02月02日
    浏览(45)
  • 非类型模板参数

    背景 除了日常用的typename以外,模板还有很多非类型模板参数 具体 int 或者 enums 对象指针和函数指针(好像很多编译器都支持void*) 左值引用(用的不多) 模板模板类型 模板模板类型 其他都比较常见且容易理解,重点看看这个: Base前面的typename在C++11之前必须是class,C++11以后才

    2024年02月15日
    浏览(31)
  • C++17完整导引-模板特性之占位符类型模板参数

    自从 C++17 起,你可以使用占位符类型( auto 和 decltype(auto) )作为 非类型模板参数的类型 。这意味着我们可以写出泛型代码来处理不同类型的非类型模板参数。 自从 C++17 起,你可以使用 auto 来声明非类型模板参数。例如: 这允许我们为不同类型实例化非类型模板参数 N :

    2024年02月06日
    浏览(32)
  • C++模板的模板参数(五)

    在C++中,模板的模板参数(Template Template Parameters)是一种特殊的模板参数,允许我们将另一个模板作为模板参数传递给一个模板。这种技术可以用于实现更灵活和通用的模板设计。 模板的模板参数使用两个 “template” 来指示,其中第一个 “template” 用于声明模板参数

    2024年02月10日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包