C++的第十二天笔记

默认参数

默认参数指当函数调用中省略了实参时自动使用的一个值。设置默认参数需要通过函数原型。

复制代码
 //语法:void printFocal(double fo=2.0, ostream& os = cout);
 //fo的默认值为2.0,os的默认对象是cout。
  • 默认参数只能写在 "函数声明" 里,定义里不能重复写。

  • 默认参数必须 "从右往左连续指定",不能跳过中间参数。

复制代码
 void func(int a, int b, int c = 30);       // 可以
 // void func(int a = 10, int b, int c); → 编译报错
  • 调用时,默认参数的位置不能 "跳过",要按顺序传参
复制代码
 void func(int a, int b = 10, int c = 20);
复制代码
 ...
 func(5);          // 正确:a=5,b=10(默认),c=20(默认)
 // func(5, , 25); // 错误:不能跳过b直接给c传参(C++不支持)
 ...
  • 默认参数不能和 "重载函数" 冲突
复制代码
 // 错误:两个函数会冲突
 void func(int a);          // 无默认参数
 void func(int a, int b = 10); // 第二个参数有默认值
 ​
 // 调用时:func(5) → 编译器不知道选哪个(是第一个?还是第二个用默认值?)
  • 默认参数可以是 "全局变量、常量、表达式"
函数重载

函数多态是C++在C语言的基础上新增的功能。函数多态(函数重载)能够让程序员使用多个同名函数。

复制代码
 // 重载1:接收“焦距+输出流”(适配文件/屏幕输出)
 void printFocal(double fo, ostream& os) {
     os << "物镜焦距:" << fo << " mm" << endl;
 }
 ​
 // 重载2:只接收“焦距”(默认输出到屏幕,简化调用)
 void printFocal(double fo) {
     printFocal(fo, cout); // 直接调用重载1,避免重复代码
 }
 ​

编译器判定两个函数是否为重载,只看参数的数量类型顺序。不看返回值、参数名。

结合学习的const和引用,这两种情况能形成合法重载;

  • const可以改变引用和指针的参数类型,但对于值传递不会改变参数类型
复制代码
 // 1. 普通引用 vs const引用 → 重载(正确)
 void print(const string& s); // 接收const字符串(保护数据,可接临时对象)
 void print(string& s);       // 接收非const字符串(可修改实参)
 ​
 // 2. 指针 vs const指针 → 重载(正确)
 void func(int* p);         // 接收非const指针(可修改指针指向的值)
 void func(const int* p);   // 接收const指针(不可修改指针指向的值)
 ​
 // 3. 值参数 vs const值参数 → 不算重载(错误,编译器视为同一类型)
 // void func(int a);
 // void func(const int a); // 编译报错(const不改变值参数的类型)
函数模板

函数模板是通用的函数描述。使用泛型来定义函数。

复制代码
 // 函数模板定义:template关键字+模板参数(T是通用类型名,随便起,比如T/U/V都可以)
 template <typename T>//声明一个通用类型T(typename可以换成class,效果一样)
 ​
 void my_print(const T& data) {  // 用T作为参数类型,const引用传参(高效+安全)
     cout << "输出数据:" << data << endl;
 }
复制代码
  my_print(100);          // 编译器生成void my_print(const int&)
  my_print(3.14);         // 生成void my_print(const double&)
 my_print("Hello 模板");  // 生成void my_print(const char* const&)(const指针的引用)
  my_print(string("C++")); //生成void my_print(const string&)
  • 模板参数的声明:template <typename T> (typenameclass 可以互换,早期只有class)

  • template <typename T> 必须直接作用于紧随其后的那个函数(或类),不能隔其他函数 / 代码。

  • 模板参数可以有多个(比如支持两个不同类型的参数)

    复制代码
     template <typename T1, typename T2>
  • 模板的 "类型限制":不是所有类型都能用。实参类型支持才能操作。

    复制代码
     template <typename T>
     T add(const T& a, const T& b) {
         return a + b;  // 要求T类型支持“+”运算符
     }
    复制代码
      // add("abc", "def"); // 错误:C风格字符串(char*)不支持'+'运算符
  • 模板可以和引用 /const/ 继承结合

重载的模板
复制代码
 // 模板1:通用模板(适配同类型的基本类型)
 template <typename T>
 T add(const T& a, const T& b) {
     cout << "通用模板(同类型):";
     return a + b;
 }
复制代码
 // 模板2:全特化重载模板(适配 string 类型,属于模板重载的一种形式)
 template <>  // 空模板参数列表,表示“全特化”(针对具体类型定制)
 string add(const string& a, const string& b) {
     cout << "重载模板(string专用):";
     return a.append(b); // string用append()拼接,比+更安全
 }
复制代码
 // 模板3:重载模板(适配两个不同类型,如int+double、double+int)
 template <typename T1, typename T2>  // 两个不同的模板参数
 double add(const T1& a, const T2& b) {
     cout << "重载模板(不同类型转double):";
     return static_cast<double>(a) + static_cast<double>(b); // 统一转double
 }
模板的局限性

模板的通用逻辑是写死的,如果遇到 "不支持这些操作" 的类型,模板就会失效。

解决方案一:重载运算符。

复制代码
 struct Point {  //定义坐标结构体
     int x;
     int y;
 };
 ​
 // 重载+运算符:让Point支持a + b
 Point operator+(const Point& a, const Point& b) {
     Point res;
     res.x = a.x + b.x; // 自定义坐标相加逻辑
     res.y = a.y + b.y;
     return res;
 }
 ​
 // 原通用模板(无需修改!)
 template <typename T>
 T add(const T& a, const T& b) {
     return a + b; // 现在Point支持+,模板能正常调用
 }

解决方案二:具体模板化

复制代码
 // 1. 通用模板(适用于int、double等支持+的类型)
 template <typename T>
 T add(const T& a, const T& b) {
     cout << "通用模板:";
     return a + b;
 }
 ​
 // 2. 全特化模板(针对string类型的专属实现)
 template <> // 空模板参数列表,表示“全特化”
 string add(const string& a, const string& b) {
     cout << "string专属模板:";
     return a.append(b); // 特殊逻辑:用append()拼接,比+更安全
 }
解决方案 优点 缺点
重载运算符 模板无需修改,通用性强 只能给 "自己能修改的类型" 重载
模板具体化 不修改原类型,灵活度高 每个特殊类型都要写一个模板,繁琐
显示实例

函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成定义,得到模板实例。 ,函数调用导致编译器生成一个实例,这种实例化方式成为隐式实例化

直接命令编译器创建特定实例,这种这种实例化方式成为显示实例化。

复制代码
 template <typename T>
 T add(const T& a, const T& b) { // 模板里已定义形参列表
     return a + b;
 }

推荐写法

复制代码
 // 语法:template  返回类型  模板名<具体类型>;
 template int add<int>(const int&, const int&); 
 // 解释:命令编译器生成“T=int”版本的add函数,参数列表和模板一致,不用重复写

不推荐写法

复制代码
 // 语法:template  返回类型  模板名<具体类型>(形参列表) { 实现 };
 template int add<int>(const int& a, const int& b) {
     return a + b; // 这里要重复模板的实现,容易和原模板不一致,不推荐
 }
相关推荐
夏幻灵19 小时前
[从零开始学JAVA|第一篇 ] 分清关键字 方法名 字面量 标识符
java·开发语言
小徐Chao努力19 小时前
【Langchain4j-Java AI开发】03-提示词与模板
java·开发语言·人工智能
Aliex_git19 小时前
Vue2 - Watch 侦听器源码理解
前端·javascript·vue.js·笔记·学习
cike_y19 小时前
Spring5入门&IOC容器
java·开发语言·spring·jdk·ioc·jdk1.8
你疯了抱抱我19 小时前
【QQ】空间说说批量删除脚本(不用任何额外插件,打开F12控制台即可使用)
开发语言·前端·javascript
Tandy12356_20 小时前
手写TCP/IP协议栈——实现ping响应不可达
c语言·网络·c++·网络协议·tcp/ip·计算机网络
Aliex_git20 小时前
HTTP 协议发展整理
笔记·网络协议·http
沐知全栈开发20 小时前
Web 词汇表
开发语言
程芯带你刷C语言简单算法题20 小时前
Day37~求组合数
c语言·开发语言·学习·算法·c
程序员-周李斌20 小时前
transmittable-thread-local[线程池跨线程值传递]
java·开发语言·算法·散列表