《21天学通C++》(第十四章) 宏和模板介绍(2)

相较于宏,C++更推荐使用模板编程,因为它们提供了更好的类型安全、更清晰的语法和更易于调试的代码

1.模板函数

语法

cpp 复制代码
template <typename T>
void function(T param) {
    // 函数体,使用T作为类型参数
}

例子

cpp 复制代码
#include <iostream>
using namespace std; 

// 模板函数,用于计算两个值的和
template <typename T>
T add(T a, T b) {
    return a + b;
}

int main() {
    int intResult = add(10, 20);      // 自动推导T为int
    double doubleResult = add(10.5, 20.5); // 自动推导T为double
    cout << "Sum of ints: " << intResult << endl;
    cout << "Sum of doubles: " << doubleResult << endl;

	// 也可以显式指定模板参数类型
    long longResult = add<long>(100, 200);//指定为int
    cout << "Sum of longs: " << longResult << endl;
    system("pause");
    return 0;
}

模板函数特化: 模板函数还可以进行特化,为特定类型提供特定的实现。

例子

cpp 复制代码
#include <iostream>
using namespace std;
// 通用模板函数
template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

// 特化模板函数,针对整数类型
template <>
int maximum<int>(int a, int b) {
    cout << "Maximum of two integers: " << (a > b ? a : b) << endl;
    return (a > b) ? a : b;
}

int main() {
    
    int i = maximum(10, 20);    // 使用特化版本
    double d = maximum(3.14, 2.72); // 使用通用模板版本
    cout << "Max int: " << i << endl;
    cout << "Max double: " << d << endl;
    system("pause");
    return 0;
}

2.模板类

语法

cpp 复制代码
template <typename T>
class MyClass {
public:
    T member; // 使用模板参数T作为类的成员变量类型
    void function(T param) {
        // 使用模板参数T作为函数参数类型
    }
};

例子

cpp 复制代码
#include <iostream>
using namespace std;

// 模板类Box的定义
template <typename T>
class Box {
private:
    T contents; // 盒子里存储的对象,类型由模板参数T决定

public:
    // 构造函数,用于初始化盒子里的对象
    Box(const T& value) : contents(value) {}

    // 成员函数,用于获取盒子里的对象
    T getContents() const {
        return contents;
    }

    // 成员函数,用于设置盒子里的对象
    void setContents(const T& value) {
        contents = value;
    }
};

int main() {
    // 创建一个存储整数的盒子
    Box<int> intBox(10);
    cout << "The Box contains: " << intBox.getContents() << endl;

    // 创建一个存储字符串的盒子
    Box<string> stringBox("Hello, World!");
    cout << "The Box contains: " << stringBox.getContents() << endl;

    // 修改盒子里的内容
    intBox.setContents(20);
    cout << "After setting a new value, the Box contains: " << intBox.getContents() << endl;
    system("pause");
    return 0;
}

3.声明包含多个参数的模板

cpp 复制代码
#include <iostream>
using namespace std; 
// 声明一个有两个类型参数的模板类
template <typename T1, typename T2>
class Pair {
private:
    T1 first;  // 第一个元素的类型为T1
    T2 second; // 第二个元素的类型为T2

public:
    // 构造函数,初始化first和second
    Pair(const T1& f, const T2& s) : first(f), second(s) {}

    // 获取第一个元素的值
    T1 getFirst() const { return first; }

    // 获取第二个元素的值
    T2 getSecond() const { return second; }
};

int main() {
    // 创建一个整型和双精度浮点型的Pair对象
    Pair<int, double> intDoublePair(10, 20.5);
    
    // 使用cout输出Pair对象的两个元素的值
    cout << "First: " << intDoublePair.getFirst() << ", Second: " << intDoublePair.getSecond() << endl;
    system("pause");
    return 0; 
}

4.声明包含默认参数的模板

cpp 复制代码
#include <iostream>
using namespace std; 

// 声明一个模板类PairWithDefault,它有两个类型参数,
// 第二个参数T2有默认值int
template <typename T1, typename T2 = int>
class PairWithDefault {
private:
    T1 first;  // 第一个元素的类型为T1
    T2 second; // 第二个元素的类型为T2,默认为int

public:
    // 构造函数,使用两个参数分别初始化first和second
    PairWithDefault(const T1& f, const T2& s = 0) : first(f), second(s) {}

    // 获取第一个元素的值
    T1 getFirst() const { return first; }

    // 获取第二个元素的值
    T2 getSecond() const { return second; }
};

int main() {
    // 使用默认的第二个参数类型(即int)创建PairWithDefault对象
    PairWithDefault<double> defaultPair(3.14);
    cout << "First: " << defaultPair.getFirst() << ", Second: " << defaultPair.getSecond() << endl;

    // 明确指定第二个参数类型为char,并创建PairWithDefault对象
    PairWithDefault<double, char> explicitPair(2.71, 'A');
    cout << "First: " << explicitPair.getFirst() << ", Second: " << static_cast<char>(explicitPair.getSecond()) << endl;
    system("pause");
    return 0; 
}

5.参数量可变的模板

语法

cpp 复制代码
template <typename... Types>
class MyClass {
    // ...
};

例子

cpp 复制代码
#include <iostream>
#include <string>
using namespace std; 

// 变长模板函数print的声明
// 它可以接受任意数量和类型的参数
template <typename... Args>
void print(const Args&... args) {
    (cout << ... << args) << endl; // 使用C++17的折叠表达式来连续输出所有参数,并在最后输出换行符
}

int main() {
    // 调用print函数,传递多个不同类型的参数
    print(1, 2, "Hello", 'a'); // 输出: 1 2 Hello a
    system("pause");
    return 0; // 程序正常结束,返回0
}

6.使用static_assert执行编译阶段检查

static_assert是C++11新增的功能,能够在不满足指定条件时禁止编译,它允许开发者在编译时对程序进行额外的约束和检查,从而提高代码的安全性和可维护性。

语法

cpp 复制代码
static_assert(expression, "error message");
  • expression:要检查的条件,必须是一个在编译时就能确定的布尔表达式
  • "error message":如果条件不为真,编译器将显示这条错误信息
    例子
cpp 复制代码
#include <iostream>

template <int N>
void doSomethingElse() {
    // 确保N大于0
    static_assert(N > 0, "N must be positive");

    // code

}

int main() {
    doSomethingElse<1>(); // 通过编译,因为1是正数
    // doSomethingElse<-1>(); // 不通过编译,因为-1不是正数
    system("pause");
    return 0;
}

PS:

  1. 务必使用模板来实现通用概念
  2. 非必要情况,请使用模板而不是宏
  3. 编写模板函数和模板类时,尽可能使用const
相关推荐
2202_754421545 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
我只会发热12 分钟前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
懷淰メ21 分钟前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm35 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
宁静@星空41 分钟前
006-自定义枚举注解
java·开发语言
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
武子康1 小时前
Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据库·sql·mybatis·springboot
珹洺1 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
每天吃饭的羊1 小时前
python里的数据结构
开发语言·python
码农飞飞1 小时前
详解Rust枚举类型(enum)的用法
开发语言·rust·match·枚举·匹配·内存安全