泛型编程 Traits实现,是什么
泛型编程(Generic Programming)是一种通过编写与特定类型无关的代码来实现代码复用和抽象的编程范式。
在C++中,模板(Templates)是实现泛型编程的主要手段。
Traits(特征)是泛型编程中常用的一种技术,
用于在编译时通过类型萃取(Type Traits)提供关于类型的信息,
从而实现类型的不同处理方法。
迭代器就是一个指针
迭代器 给算法的 一个接口
什么是容器
在C++中,容器(Containers)是用于存储和管理对象集合的数据结构。C++标准库(Standard Template Library, STL)提供了一组丰富的容器,它们分为几类,每种容器适用于不同的需求。常见的容器类型包括
容器分类
序列(顺序)容器、关联容器和无序容器
序列(顺序)容器:数组队列向量链表
关联(树)
无序:哈希表
复杂度。。。。
模板实例化是可以定制的
偏特化
部分特化
不能有二义性
默认模板参数
模板的实例化 在编译的时候运行-----
代码膨胀
优化
模板 概述
在C++中,模板(Templates)是一种通用编程工具,允许程序员编写泛型代码,即代码不仅能够处理一种特定的数据类型,而是可以适用于多种不同的数据类型。模板提供了一种在编译时生成代码的机制,根据特定的模板参数生成特定类型的代码实例。
### 主要类型
C++中的模板主要分为函数模板和类模板两种。
#### 函数模板(Function Templates)
函数模板允许定义一个通用函数,其参数和返回类型可以是任意类型。通过函数模板,可以避免为相似但参数类型不同的函数编写多个重复的代码。
```cpp
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
```
#### 类模板(Class Templates)
类模板允许定义通用的类,其中成员变量的类型和成员函数的参数类型可以是模板参数。类模板使得类能够操作多种类型的数据,而无需为每种数据类型编写不同的类定义。
```cpp
template <typename T>
class Array {
public:
Array(int size) : size(size) {
data = new T[size];
}
T& operator[](int index) {
return data[index];
}
~Array() {
delete[] data;
}
private:
T* data;
int size;
};
```
### 模板参数
模板参数是在定义模板时指定的类型或值。在模板中,可以定义类型参数(如`typename T`或`class T`)和非类型参数(如整数值)。模板参数使得模板更加灵活,可以适应不同的需求和场景。
```cpp
template <typename T, int size>
class FixedArray {
public:
FixedArray() {
data = new T[size];
}
T& operator[](int index) {
return data[index];
}
~FixedArray() {
delete[] data;
}
private:
T* data;
};
```
### 实例化模板
在使用模板时,需要根据具体的类型或值对模板进行实例化。实例化过程发生在编译时,根据模板参数生成相应的代码实例。
#### 函数模板的实例化
```cpp
int main() {
int a = 5, b = 10;
double x = 3.5, y = 4.8;
int result1 = max<int>(a, b); // 显式实例化,类型参数为int
double result2 = max<double>(x, y); // 显式实例化,类型参数为double
return 0;
}
```
#### 类模板的实例化
```cpp
int main() {
Array<int> intArray(5); // 类模板实例化,类型参数为int
intArray[0] = 10;
std::cout << intArray[0] << std::endl;
Array<double> doubleArray(3); // 类模板实例化,类型参数为double
doubleArray[0] = 3.14;
std::cout << doubleArray[0] << std::endl;
return 0;
}
```
### 模板特化
模板特化(Template Specialization)允许为特定的数据类型或值提供定制的实现。可以对模板的一个或多个特定实例进行特化,提供特定类型或值的专门实现。
```cpp
// 类模板的部分特化示例
template <typename T, typename U>
class MyClass {
// 通用实现
};
template <typename T>
class MyClass<T, int> {
// 特化实现,当U为int时的特定实现
};
// 函数模板的特化示例
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
template <>
const char* max<const char*>(const char* a, const char* b) {
return (strcmp(a, b) > 0) ? a : b;
}
```
### 使用建议
- **理解模板参数的类型推导**:模板参数可以根据实际参数的类型进行推导,但有时需要显式指定。
- **注意模板的实例化时机**:模板实例化发生在使用时,编译器会根据需要生成相应的代码。
- **避免模板滥用**:虽然模板提供了强大的泛型编程能力,但过度使用模板可能会增加代码复杂性和编译时间。
通过学习和实践,逐步掌握C++模板的使用方法和技巧,能够极大地提高代码的灵活性和重用性。
操作符重载
哎又是自学的一天
要用map来实现,字典数据要用文件存储
什么是泛型函数
泛型函数(Generic Function)是指可以操作不同数据类型的函数,
而不需要为每种类型单独编写函数定义。
在C++中,泛型函数通常通过模板(Templates)来实现。
模板函数可以在编译时根据传入的参数类型自动生成相应的函数代码。
其实就是用模板函数来完成啦,所以给出我半独立写的代码
其中出现了小插曲
#include <iostream>
using namespace std;
template <typename T>
void swapab(T &a, T &b) {
// T t=0; 特别是T t = 0;这一行会导致问题,因为它假定所有类型都可以用0来初始化,但这是不正确的。例如,对于浮点数、指针和自定义类型,这种初始化可能会失败。
T t = a;
a = b;
b = t;
}
int main() {
int a = 5, b = 10;
double x = 3.5, y = 4.8;
cout<<"Before swap: a = " << a << ", b = " << b << endl;
swapab(a, b);
cout<<"After swap: a = " << a << ", b = " << b << endl;
cout<<"Before swap: x = " << x << ", y = " << y << endl;
swapab(x, y);
cout<<"After swap: x = " << x << ", y = " << y << endl;
return 0;
}
主要问题和修改
其中我对于写法存在浅显认知 我道歉呜呜呜
-
错误的变量定义和赋值:
T &t=0;
:你试图创建一个引用,并将其初始化为0,这是非法的。T = &a;
:T
是一个类型名,不能直接赋值。&a = &b;
:左值必须是可以修改的变量,不能是引用的地址。- &是取得地址啊 啊啊
-
正确的变量定义和赋值:
- 使用非引用类型的临时变量
T t
来保存a
的值
- 使用非引用类型的临时变量
来来来 第二个作业
作业(真的是 善变的男人 又增加内容 不过应该能学到很多)
B.利用映射,实现一个简单的英汉词典,要求输入中文或者英文,给出对应的翻译词汇,如果没有输出默认提示
要求:使用文件保存词典数据。
加分项:
1.能够通过命令行交互添加新的词汇
2.能够将词典排序后输出
我太菜了,还是分解成3个小任务吧
- 创建词典数据文件。
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main(){
// 使用 ofstream 类创建文件:
// 这会创建一个名为 example.txt 的文件。如果文件已存在,它将被覆盖。
ofstream outFile("dict.txt") ;
// ofstream outFile("example.txt");
if (!outFile) {
cerr << "无法创建文件。" << endl;
return 1;
}
// 使用 << 运算符将内容写入文件:
outFile << "apple,苹果" << endl;
outFile << "banana,香蕉" << endl;
outFile << "orange,橙子" << endl;
outFile << "hello,你好" << endl;
outFile << "world,世界" << endl;
// 关闭文件
outFile.close();
// 提示用户文件创建成功
cout << "文件创建并写入成功。" << endl;
return 0;
}
- 读取词典数据文件并加载到映射中。
- 允许用户查询词典。
额 这两步直接就完成任务了
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <sstream>
using namespace std;
// 定义函数loadDictionary
// 不需要返回,函数名字 传入 字符串和文件名 做映射 在字典
// 打开文件进行读取
// 如果无法打开,就报错
// 定义词语,显示请输入
// 死循环等待输入
// 查找单词
// 如果没找到 显示没有
// 接着显示输入一个英文和中文词语
void load_Dic(const string& filename , map<string, string>&dictionary ){
ifstream file(filename); // 打开文件进行读取
if(!file){
cerr << "无法打开,就报错" << filename <<endl;
return;
}
string word;
cout<<"显示请输入"<<endl;
while(getline(file, word)){
istringstream iss(word);
string eng,chine;
if (getline(iss, eng, ',')&& getline(iss, chine))
{
dictionary[eng] = chine;
dictionary[chine] = eng;
}
}
file.close(); // 关闭文件
}
// 在main函数中使用loadDictionary函数
// 初始化映射
// 告诉系统 文件名是什么
// 加载词典
// 用户输入并翻译
// 死循环等待输入
// 查找单词
// if
int main(){
map<string, string>dictionary;
string filename = "dict.txt";
load_Dic(filename, dictionary);
string word;
cout<< " input "<< endl;
while (cin >>word)
{
// 查找单词
auto it = dictionary.find(word);
if(it != dictionary.end()){
cout<< "translate" << it->second << endl;
}
else{
cout << " no word "<<endl;
}
cout << "enter a word again:";
}
return 0;
}
来吧 增加 两个功能
这是添加新的词汇
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <sstream>
using namespace std;
// 传入文件名和字典,读取文件内容并映射到字典中
void load_Dic(const string& filename, map<string, string>&dictionary){
ifstream file(filename);
if(!file){
cerr << "can not open" << filename << endl;
return;
}
string line;
while(getline(file, line)){
istringstream iss(line);
string eng, chine;
if(getline(iss, eng, ',')&& getline(iss, chine)){
dictionary[eng] = chine;
dictionary[chine] = eng;
}
}
file.close();
}
// save new word
void save_Dic(const string&filename, const map<string, string>& dictionary){
ofstream file(filename);
if(!file){
cerr << "can not open" << filename<< "to save" <<endl;
return;
}
for(const auto& entry : dictionary){
file << entry.first << "," << entry.second << endl ;
}
file.close();
}
int main(){
map<string, string>dictionary;
string filename = "dict.txt";
load_Dic(filename, dictionary);
string word;
string y;
string command;
cout<< " input "<< endl;
while (cin >>word)
{
// 查找单词
auto it = dictionary.find(word);
if(it != dictionary.end()){
cout<< "translate" << it->second << endl;
}
else{
cout << " no word "<<endl;
cout<< " do u want to add the word";
cin >> y ;
cout << "请输入一个英文或中文单词或命令(add: 添加新词汇, quit: 退出): ";
if( y == "y"){
while (cin >> command) {
if (command == "add") {
string eng, chine;
cout << "请输入要添加的英文单词: ";
cin >> eng;
cout << "请输入要添加的中文翻译: ";
cin >> chine;
dictionary[eng] = chine;
dictionary[chine] = eng; // 双向映射
cout << "成功添加新词汇: " << eng << " -> " << chine << endl;
save_Dic(filename, dictionary); // 保存词典数据到文件
} else if (command == "quit") {
break; // 退出循环
} else
cout << "请输入一个英文或中文单词或命令(add: 添加新词汇, quit: 退出): ";
}
}
}
cout << "enter a word again:";
}
return 0;
}
感觉有点小bug,不管了做出来就不错了
最后的附加来啦
#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <sstream>
#include <algorithm>
using namespace std;
// 加载词典函数
void load_Dic(const string& filename, map<string, string>& dictionary) {
ifstream file(filename);
if (!file) {
cerr << "无法打开文件: " << filename << endl;
return;
}
string line;
while (getline(file, line)) {
istringstream iss(line);
string eng, chine;
if (getline(iss, eng, ',') && getline(iss, chine)) {
dictionary[eng] = chine;
dictionary[chine] = eng; // 双向映射
}
}
file.close();
}
// 保存词典函数
void save_Dic(const string& filename, const map<string, string>& dictionary) {
ofstream file(filename);
if (!file) {
cerr << "无法打开文件: " << filename << " 进行保存" << endl;
return;
}
for (const auto& entry : dictionary) {
file << entry.first << "," << entry.second << endl;
}
file.close();
}
// 显示词典并处理用户交互
void displayDictionary(const string& filename) {
map<string, string> dictionary;
load_Dic(filename, dictionary);
string word;
string y;
string command;
cout << "请输入一个英文或中文单词或命令(add: 添加新词汇, quit: 退出): ";
while (cin >> word) {
auto it = dictionary.find(word);
if (it != dictionary.end()) {
cout << "翻译: " << it->second << endl;
} else {
cout << "词典中没有找到该单词。" << endl;
cout << "是否要添加该单词? (y/n): ";
cin >> y;
if (y == "y") {
cout << "请输入要添加的英文单词: ";
cin >> word;
cout << "请输入要添加的中文翻译: ";
cin >> command;
dictionary[word] = command;
dictionary[command] = word; // 双向映射
save_Dic(filename, dictionary); // 保存更新后的词典
cout << "成功添加新词汇: " << word << " -> " << command << endl;
}
}
// 排序并输出字典
cout << "当前词典内容(按照英文单词排序):" << endl;
vector<pair<string, string>> sorted_dict(dictionary.begin(), dictionary.end());
sort(sorted_dict.begin(), sorted_dict.end());
for (const auto& entry : sorted_dict) {
cout << entry.first << " -> " << entry.second << endl;
}
cout << endl << "请输入一个英文或中文单词或命令(add: 添加新词汇, quit: 退出): ";
}
}
int main() {
string filename = "/home/ubuntu2204/桌面/cpp/day04/homework/homework02/dict.txt";
displayDictionary(filename);
return 0;
}
// g++ /home/ubuntu2204/桌面/cpp/day04/homework/homework02/final_version_dictionary.cpp -o final_version_dictionary
// ./final_version_dictionary
结束啦 剩下的作业 enmmm 不想做了 略略略
继续学习
模板元编程(Template Metaprogramming)
是指在编译期间利用C++模板系统进行计算和代码生成的技术。
它的核心思想是将计算推移到编译期间,
通过模板实例化和递归展开等方式
实现复杂的计算和代码生成,以提高程序的性能和灵活性。
模板元编程的特点和用途包括:
-
编译期计算:模板元编程允许在编译期进行计算,生成编译期常量或类型。
-
泛化和重用:通过模板,可以编写泛化的代码,处理不同类型和参数的情况,避免代码重复。
-
性能优化:将计算移至编译期可以提高程序运行时的性能,因为一些计算工作在编译期已经完成。
-
元编程技巧:利用模板元编程可以实现各种编译期技巧,如条件编译、递归展开和元编程逻辑等。
模板推理过程的文档意义:
-
理解模板参数推导:在C++中,模板推导(template deduction)是指编译器根据函数或类模板的使用情况自动推导模板参数的过程。理解模板推导过程有助于开发者确保代码正确地利用了模板的泛化能力,并理解编译器如何根据上下文推断模板参数。
-
文档化推导过程:编写文档描述模板推导过程有助于团队内部或者未来阅读代码的开发者理解模板的使用方法和限制条件。特别是对于复杂的模板代码,详细的推导过程描述可以作为使用文档,帮助他人更快地理解代码意图和调试模板推导相关的问题。
是真的抽象啊
模板元函数(template metaprogramming)和模板函数(template function)虽然都涉及到C++中的模板机制,但它们的作用和使用场景有所不同。
### 模板函数(Template Function):
- **定义**:模板函数是指通过模板机制定义的函数,可以用于生成针对不同数据类型的函数实例。
- **作用**:模板函数允许在不同类型之间重用相同的代码逻辑,提高代码的重用性和灵活性。
- **示例**:下面是一个简单的模板函数示例,实现了交换两个变量的功能:
```cpp
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
```
- **调用方式**:模板函数的调用是通过传递具体的类型参数来实例化函数,例如 `swap<int>(x, y)` 或者 `swap<double>(x, y)`。
### 模板元函数(Template Metaprogramming):
- **定义**:模板元函数是指利用C++模板系统在编译期间进行计算和生成代码的技术。
- **作用**:模板元函数允许在编译期间执行复杂的计算和生成代码,生成的结果可以是常量表达式、类型信息等。
- **示例**:前面提到的求最大公约数的示例就属于模板元函数的应用,通过模板递归展开在编译期间计算出最大公约数。
```cpp
template <int A, int B>
struct GCD {
static constexpr int value = GCD<B, A % B>::value;
};
template <int A>
struct GCD<A, 0> {
static constexpr int value = A;
};
```
- **调用方式**:模板元函数的结果可以在编译期间通过静态成员或者 constexpr 变量访问,例如 `GCD<24, 36>::value`。
### 主要区别总结:
1. **执行时间**:模板函数在运行时实例化,而模板元函数在编译期间执行。
2. **功能**:模板函数用于生成函数的多个实例,而模板元函数用于在编译期间进行计算和代码生成。
3. **使用场景**:模板函数适用于需要针对不同类型重用相同逻辑的情况,而模板元函数适用于需要在编译期间进行复杂计算或代码生成的场景。
虽然它们都是通过模板机制实现的,但模板函数和模板元函数的设计目的和使用方式不同,开发者在使用时需根据具体需求选择合适的模板形式。
哎 垃圾模板元函数 比元学习都难理解
gpt的简单例子
#include <iostream>
// 模板元函数,计算阶乘
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
// 特化模板,基本情况
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
int main() {
constexpr int result = Factorial<5>::value; // 计算5的阶乘
std::cout << "Factorial of 5 is: " << result << std::endl;
return 0;
}
template <int N>
struct Factorial {
static constexpr int value = N * Factorial<N - 1>::value;
};
-
template <int N>
:这是一个模板声明,表明后面的结构体Factorial
是一个接受一个整数类型参数N
的模板。这个N
将会在使用时被具体化为一个具体的整数值。 -
struct Factorial { ... }
:定义了一个结构体Factorial
,用来计算阶乘。 -
static constexpr int value = N * Factorial<N - 1>::value;
:这是模板结构体中的静态成员变量定义。它使用递归方式来计算N
的阶乘。具体解释如下:-
Factorial<N - 1>::value
:这是一个递归调用,用来计算(N-1)
的阶乘值。 -
N * Factorial<N - 1>::value
:将当前的N
乘以(N-1)
的阶乘值,得到N
的阶乘值。 -
介绍的好清楚啊
-
特化模板
template <>
struct Factorial<0> {
static constexpr int value = 1;
};
在模板元编程中,我们经常需要为特定的情况提供特殊处理。在阶乘计算中,当 N
为 0
时,阶乘的结果是 1
。这种特殊情况可以通过特化模板来处理:
-
template <>
:这是一个特化模板的语法,表示我们要为一个特定的模板参数N
提供一个特殊的定义。 -
struct Factorial<0> { ... }
:这是特化模板的定义,它提供了当模板参数N
等于0
时的特殊处理。 -
static constexpr int value = 1;
:这里直接定义了Factorial<0>
的静态成员变量value
为1
,因为0
的阶乘定义为1
constexpr
在 C++11 及以后的标准中,constexpr
是一个关键字,用于声明可以在编译期间求值的常量表达式。在模板元编程中,我们经常会用到 constexpr
来确保计算在编译期间进行,以提高性能和效率。
在我们的例子中,使用 constexpr
可以确保我们在编译期间计算出阶乘的结果,并且可以直接将结果用作编译期常量。例如:
constexpr int result = Factorial<5>::value;
这行代码将在编译时期计算出 5
的阶乘值,并将结果存储在 result
中。
模板元编程利用 C++ 的模板机制,在编译期间进行复杂计算和代码生成,以生成常量表达式和类型信息等。虽然初学时可能感觉抽象和复杂,但它提供了强大的编译期计算能力,有助于提高程序的性能和灵活性。
今天终于结束了,要补得太多了,但是好喜欢这种跟大家一起学习的感觉
转眼间就1W个字了