25.5.17(C++)

在昨天学习了函数模板的基本概念、语法以及注意事项后,那么现在不妨用个小案例来检验下自己的学习成功吧!

函数模板案例

案例描述

  • 利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
  • 排序规则从小到大,排序算法为选择排序
  • 分别利用char数组和int数组进行测试

当我看到案例要求用选择排序算法来完成时,我的大脑对其竟然是几近空白。虽然我之前学过冒泡、选择、插入等排序算法,但由于太久没有用到,我几乎快忘干净了。我仔细回想之前所学的知识,但脑海里只有冒泡排序有个大致思路,插入排序有些许印象,而选择排序我几乎忘光了…我内心很纠结,在想我到底是该就案例把选择排序草草过一遍呢?还是老老实实地去找个讲选择排序的视频再学一边呢?最后理智还是战胜了懒惰,我又去找了个讲选择排序的视频把它老老实实地学了一遍。接下来我把我对选择排序的理解进行一个大致的讲解

选择排序

  • 本质:遍历数据并进行排序
  • 特点:

1、对于n个数据,只需要执行n-1轮
2、需要定义一个额外变量来记录未排序区域中最小元素的位置
3、 每一轮只执行一次交换操作

以下为选择排序的C语言代码

for (int i = 0;i < n - 1;i++)   //外层循环 n-1表示选择排序需要进行的次数
{
	int p = i;   //初始化 先默认最小元素下标为0 p是用来记录最小元素位置的元素
	for (int j = 1 + i;j < n;j++)   //内层循环
	{
		if (arr[j] < arr[p])   //比较大小
		{
			p = j;   //赋值语句
		}
	}
	int temp = arr[i];   //交换元素语句
	arr[i] = arr[p];
	arr[p] = temp;
}

好的,在我又学习了一遍选择排序后,我感到无比开心,庆幸自己没有放过自己,接下来进入正题

案例实现
思考:既然是函数模板的案例,而案例描述中又提到要分别用char数组和int数组进行测试,那么为了提高代码复用性,则必须要在此案例中的交换函数和选择排序算法中使用到函数模板相关的知识,具体代码如下:

  • 交换函数模板
template<class T>   //声明创建模板
void mySwap(T& a, T& b)   //交换函数模板
{
	T temp = a;
	a = b;
	b = temp;
}
  • 选择排序算法模板
template<class T>   //声明创建模板
void mySort(T arr[],int len)   //选择排序算法模板
{
	for (int i = 0;i < len - 1;i++)
	{
		int max = i;   
		for (int j = 1 + i;j < len;j++)
		{
			if (arr[max] < arr[j])
			{
				max = j;
			}
		}
		if (max != i)
		{
			mySwap(arr[max], arr[i]);
		}
	}
}

既然模板有了,那么就该上案例了

void test01()
{
	//测试char数组
	char charArr[] = "bacdfe";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr,num);
}

void test02()
{
	//测试int数组
	int intArr[] = {5,2,6,3,1,4};
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

MVP结算画面:

在这里插入图片描述
在成功完成函数模板案例后,我不禁想问:普通函数和函数模板有什么区别呢?诶!接下来的学习恰好解决了我的疑惑

普通函数和函数模板的区别

普通函数和函数模板的区别:

  • 普通函数调用时可以自动发生类型转换(隐式类型转换)
int myAdd01(int a, int b)
{
	return a + b;
}
void test01()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << myAdd01(a, c) << endl;
}

在这里插入图片描述
普通函数调用时可以发生隐式类型转化,将char型的字符“c”转化成int型的ASCII码表值——99,故最终相加结果为10+99=109

  • 函数模板调用时,若用显示指定类型,可以发生隐式转换

在这里插入图片描述
在“<>”中明确指定T的类型,告诉编译器不用去推导了,非int类型直接转成int类型即可

  • 函数模板调用时,若用自动类型推导,则不可以发生隐式转换
    在这里插入图片描述
    一个int类型,一个char类型,编译器无法推导出一致的T的类型,故不可以发生隐式类型转换

总结:建议使用显示指定类型的方式调用函数模板,因为这样可以自己确定通用类型T

了解完普通函数和函数模板的区别后,那么,我什么时候应该调用普通函数,什么时候应该调用函数模板呢?是不是应该去学一下?没错,我又学了一下下

普通函数和函数模板调用规则

  • 如果函数模板和普通模板都可以调用,优先调用普通函数
    注意:若此时普通函数只有声明没有实现,也不会调用函数模板,而是会报错

  • 可以通过空模板参数列表强制调用函数模板
    在这里插入图片描述
    注意:哪怕普通函数和函数模板同时存在,因为有了空模板参数列表“<>”,也会强制调用函数模板

  • 函数模板可以发生函数重载
    在这里插入图片描述

  • 如果函数模板可以产生更好的匹配,优先调用函数模板
    在这里插入图片描述
    注意:此时调用函数模板而不调用普通函数的原因是——调用普通函数还要发生隐式类型转换,编译器嫌麻烦,不如调用函数模板做自动类型推导

学完了这些之后,我感觉函数模板好强大呀,只要正确掌握其用法,便可以极大地提高编程效率,让我无往不利。但现实,真的如我想象的那般美好吗?

模板的局限性

哈哈哈哈哈,函数模板果然不是万能的!我就知道它有它的局限性。正如我沏茶时总偏爱紫砂壶的温润,可煮咖啡时却不得不换用玻璃壶的透亮。人类文明史上,青铜器替代了石器却也始终无法取代玉器的礼器地位,正如我们既需要模板的抽象之美,又必须接纳其局限的现实。我也终于明白:承认边界的智慧,恰是突破边界的开始…

  • 模板并非万能,有些特定数据类型,需要用具体化方式做特殊实现
    在这里插入图片描述
    总结:
  • 利用具体化模板,可以解决自定义类型的通用化
  • 学习模板不是为了写模板,而是在STL中能够运用系统提供的模板

哈基模,你这家伙,我学了半天怎么写具体化的模板来解决自定义类型的通用化,你却最后告诉我其实只要会在STL中会使用系统提供的模板就可以了!?哈基模,那我刚刚吃过的那些屎算什么…我强忍着恶心好不容易理清了思路,你却告诉我这些都是我的一厢情愿…唉,终究是我错付了…

正当我失意之际,我又偶然得知你居然还有私生子!?!继承了你一部分特性的类模板?!!这无疑又给了我当头一棒…哈基模,我已经找不到任何言语来形容你的所作所为了。凌晨三点,我仍未眠…

<think>好的,用户问的是“C++怎么初始化结构体”。我需要先回顾C++中结构体的初始化方法,然后整理出几种不同的方式,并举例说明。首先,用户可能是个刚开始学习C++的新手,不太清楚结构体初始化的各种方法,或者在实际编码中遇到了问题,需要明确的指导。 首先,结构体在C++中和类很相似,默认成员是public的,而类是private的。初始化结构体的方法有很多种,比如直接初始化、列表初始化、构造函数初始化等。用户可能需要了解这些不同方式的适用场景和语法区别。 接下来,我需要考虑用户可能的真实需求。用户可能在编写代码时遇到了初始化结构体的问题,比如编译错误,或者不知道如何正确初始化结构体成员。他们可能需要具体的示例和步骤,以及不同方法的优缺点比较。 需要分步骤讲解各种初始化方法,确保语言简洁明了,避免使用过于专业的术语,但又要准确。例如,直接初始化是C风格的方式,适用于简单的结构体;列表初始化是C++11引入的,更现代且安全;构造函数初始化则适用于需要复杂逻辑的情况。 同时,要提到C++11及更高版本支持的新特性,比如成员默认值,这样用户可以在定义结构体时直接为成员变量赋初值,简化初始化过程。此外,聚合初始化也是一个重要的点,特别是当结构体包含数组成员时,如何正确初始化。 还要注意一些常见错误,比如忘记初始化某些成员导致未定义行为,或者结构体中含有私有成员时不能使用列表初始化等。这些可能是用户在实际操作中容易遇到的问题,需要提前指出。 最后,总结不同方法的适用场景,帮助用户根据具体情况选择最合适的初始化方式。比如,简单结构体用列表初始化,复杂情况用构造函数,或者需要灵活赋值的用成员函数。 确保回答结构清晰,每个方法都有示例代码,并说明注意事项。这样用户能直观理解并应用在自己的代码中,避免常见的陷阱。</think>在 C++ 中,结构体(`struct`)的初始化方式灵活多样,以下是 **6 种常见方法**及其详细说明和代码示例: --- ### 1. **直接初始化(C 风格)** 通过 `{}` 按成员声明顺序赋值,**仅适用于公有成员且无构造函数的结构体**。 ```cpp struct Point { int x; int y; }; Point p1 = {10, 20}; // 按顺序初始化 x=10, y=20 ``` --- ### 2. **成员列表初始化(C++11 起)** 使用 `{}` 并**指定成员名**,可乱序初始化,更直观安全。 ```cpp struct Student { std::string name; int age; }; Student s1 = {.name = "Alice", .age = 18}; // C++20 起支持指定成员名 Student s2{"Bob", 20}; // C++11 通用列表初始化 ``` --- ### 3. **构造函数初始化** 通过定义构造函数实现**灵活控制初始化逻辑**,适用于复杂场景。 ```cpp struct Rectangle { int width; int height; // 自定义构造函数 Rectangle(int w, int h) : width(w), height(h) {} }; Rectangle rect(30, 40); // width=30, height=40 ``` --- ### 4. **默认成员初始化(C++11 起)** 在结构体定义中**直接为成员赋默认值**。 ```cpp struct Config { int timeout = 5000; // 默认值 bool logging = false; }; Config cfg1{}; // timeout=5000, logging=false Config cfg2{2000, true}; // 覆盖默认值 ``` --- ### 5. **聚合初始化(含数组成员)** 针对包含数组的结构体,需**嵌套使用 `{}`**。 ```cpp struct Matrix { int data[2][2]; }; Matrix mat = { {{1, 2}, {3, 4}} }; // 初始化二维数组 ``` --- ### 6. **通过成员函数初始化** 定义成员函数实现**动态或延迟初始化**。 ```cpp struct SensorData { float temperature; void init(float temp) { temperature = temp; } }; SensorData sensor; sensor.init(25.5); // 手动调用初始化 ``` --- ### ⚠️ **注意事项** 1. **构造函数与聚合初始化冲突** 若结构体定义了构造函数,则无法使用 `{}` 聚合初始化(需通过构造函数传参)。 2. **私有成员限制** 若成员为 `private`,只能通过构造函数或公有方法初始化。 3. **C++ 版本兼容性** - 指定成员名的初始化(如 `.name = "Alice"`)需 **C++20** 或更高版本。 - 列表初始化(`{}`)需 **C++11** 或更高版本。 --- ### 📊 **方法对比表** | 方法 | 适用场景 | 优点 | 缺点 | |--------------------|-------------------------|-------------------------|-------------------------| | 直接初始化 | 简单公有结构体 | 语法简洁 | 依赖成员顺序,易出错 | | 成员列表初始化 | C++11+ 项目 | 可读性强,支持乱序 | 需要编译器支持新标准 | | 构造函数初始化 | 复杂初始化逻辑 | 灵活控制,支持校验 | 代码量稍多 | | 默认成员初始化 | 需要预设默认值 | 简化对象创建 | 无法动态调整默认值 | | 聚合初始化 | 含数组的结构体 | 直观处理嵌套数据 | 语法嵌套较复杂 | | 成员函数初始化 | 延迟初始化或动态配置 | 可多次调用,灵活 | 需手动调用,增加步骤 | 根据具体需求选择最适合的初始化方式即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值