【C++初阶】C++入门相关知识(2):输入输出 & 缺省参数 & 函数重载

【C++初阶】C++入门相关知识(2):输入输出 & 缺省参数 & 函数重载

🎈主页传送门:良木生香

🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》《鼠鼠的C++学习之路》

🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离


上期回顾:在上一篇文章中,我们对C++进行了初步的认识,学习了C++的发展历史,第一个C++程序以及命名空间,我们知道,C++的出现就是为了改进和完善C语言的不足,使得程序更加高效,程序员编写起来更加方便快捷,那么本篇文章我们继续往下认识C++的入门相关知识

目录

一、C++的输入&输出

1.1、核心载体:头文件

1.2、核心的IO对象:cin与cout

1.2.1、std::cin 标准输入流

1.2.2、std::cout 标准输出流

1.3、流操作符: << 与 >> 的双重身份

1.4、换行与缓冲区:endl的应用

1.5、C++IO的核心优势:从手动格式到自动适配

1.5.1、自动识别类型

1.5.2、自定义类型支持

1.6、面向对象的底层支撑:IO流的设计思想

1.7、提高输入输出效率

二、C++的缺省参数

2.1、缺省参数的定义及其基本用法

2.2、全缺省与半缺省

2.2.1、全缺省

2.2.2、半缺省

三、C++的函数重载

3.1、函数重载的定义:


一、C++的输入&输出

在C语言时代,我们一依赖printf和scanf两个函数完成输入和输出,手动指定格式符、处理类型不匹配的风险是每个开发者的日常。而C++带来了全新的IO库,以类型安全、可扩展的设计重构了输入输出的体验。

1.1、核心载体:<iostream>头文件

<iostream>头文件是Input Output stream的缩写,是C++标准输入输出的核心头文件,它不仅定义了cin,cout,endl等等标准的IO对象,同时封装了底层IO操作的复杂逻辑,让开发者无需关注缓冲区管理、设备交互等等细节

在这里有一个值得注意的小细节,在VS的编译器中(也就是微软公司的编译器),<iostream>头文件是会间接包含<stdio.h>这个头文件的,因此在微软的编译器中,我们可以混着使用cin与scanf.但是这只适用于微软的编译器,对于跨平台的兼容性不并不是很好,所以在编写代码的时候尽量不要混用

1.2、核心的IO对象:cin与cout

1.2.1、std::cin 标准输入流

std::cinistream类的对象,主要是面向窄字符(char类型)的标准输入流.它从键盘或者其他的输入设备获取数据,通过流提取运算符 >> 完成数据的读取,可参考以下代码:

#include <iostream> int main() { int age; std::cout << "请输入你的年龄:"; std::cin >> age; // 从标准输入读取整数 std::cout << "你的年龄是:" << age << "岁" << std::endl; return 0; }

1.2.2、std::cout 标准输出流

std::coutostream类的对象,同样是面对窄字符的标准输出流.它通过流插入运算符 <<向屏幕或者其他输出设备写入数据,支持链式调用,具体使用可参考以下代码:

#include <iostream> int main() { std::cout << "Hello, C++ IO Stream!" << std::endl; return 0; }

1.3、流操作符: << 与 >> 的双重身份

在C语言中,<< >>这两个符号分别代表的是左移/右移运算符.而在C++中,通过重载函数的技术,这两个运算符被赋予了新的角色:

  • << : 流插入运算符,用于向输出流写入数据
  • >> : 流提取运算符,用于从输入流提取数据

这种重载实现了 “语法糖” 效果,让 IO 操作更符合直觉。同时,运算符的返回值是流对象本身,支持链式调用:

std::cout << "姓名:" << "张三" << ",年龄:" << 25 << std::endl;

链式调用的底层原理是:每次<<操作返回ostream&,使得后续操作可以直接作用于同一个流对象。

1.4、换行与缓冲区:endl的应用

在我们刚刚开始学习C++语言的时候,老师经常会告诉我们,这个是换行符,其实不然,它的作用远不止于此:

表面功能:相当于插入换行符 \n,实现文本换行深层功能:强制舒心输出缓冲区,将缓冲区的数据立即写入输出设备

这与直接使用\n有着显著的区别:

// 仅换行,不刷新缓冲区,性能更优 std::cout << "Hello\n"; // 换行+刷新缓冲区,适用于需要立即输出的场景(如调试日志) std::cout << "Hello" << std::endl;

频繁使用endl会导致缓冲区频繁刷新,降低IO的性能,在性能高敏感的场景中,应当优先使用 \n,仅需要在立即输出的时候使用endl.

1.5、C++IO的核心优势:从手动格式到自动适配

1.5.1、自动识别类型

在C语言中,我们使用printf()需要手动指定格式符,如%s %d %f等等,一旦格式符与变量类型不相同,就会导致未定义行为,所以这也是C++针对C语言做的优化之一.C++IO流则是通过函数重载实现了自动类型识别:

int num = 42; double pi = 3.14; std::string name = "C++"; std::cout << num << " " << pi << " " << name << std::endl; // 自动识别类型,输出:42 3.14 C++

这种设计不仅简化了代码,还从根本上避免了格式不匹配的问题。

1.5.2、自定义类型支持

C++ IO 流的最大优势之一是支持自定义类型的输入输出。通过重载 << 和 >>运算符,我们可以让自定义类/结构体像内置类型一样直接参与 IO 操作:

#include <iostream> struct Point { int x, y; }; // 重载<<运算符,实现Point类型的输出 std::ostream& operator<<(std::ostream& os, const Point& p) { return os << "(" << p.x << ", " << p.y << ")"; } int main() { Point p{10, 20}; std::cout << "点的坐标:" << p << std::endl; // 输出:点的坐标:(10, 20) return 0; }

这种可扩展性让 C++ IO 流成为面向对象编程的理想选择。

1.6、面向对象的底层支撑:IO流的设计思想

C++IO流的设计深度融合了面向对象的核心思想:

  • 继承体系:istream和ostream作为基类,派生出文件流(fsteam)、字符串流(sstream)等子类,实现了IO操作的统一接口
  • 多态与重载:通过运算符重载和虚函数,实现了不同类型数据的IO适配
  • 封装性:底层缓冲区管理、设备交互等细节被封装在流内部,开发者只需要关注高层接口

这些设计使得IO流库即灵活又易于拓展,但是也意味着需要掌握更多面向对象只是才能深入理解其底层应用原理

1.7、提高输入输出效率

在需要输入输出性能的地方,C++在这方面是比不过C语言的,想要让C++的输入和输出像C语言一样,那就要在代码前面加上这段代码:

ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);

总之:C++ IO 流不仅是输入输出的工具,更是面向对象设计思想的典型实践。掌握其核心原理与最佳实践,将为你构建更健壮、可扩展的 C++ 应用打下坚实基础。

二、C++的缺省参数

2.1、缺省参数的定义及其基本用法

缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参则采⽤该形参的缺省值,否则使⽤指定的实参,像下面这样:

#include<iostream> //这里定义一个函数 void Func(int a = 10) { std::cout << a; } int main() { int a = 20; //这里将参数a传入函数中 Func(a); return 0; }

运行结果如下:

但是这时候我们不将a的值传进去,我们看看结果是什么:

#include<iostream> //这里定义一个函数 void Func(int a = 10) { std::cout << a; } int main() { int a = 20; //这里不传a的值进去 Func(); return 0; }

运行结果为:

总结:我们在定义一个函数时,如果给里面的参数进行了初始化,那么在主函数调用时,如果给函数传进了参数,那就使用传进来的参数,如果没有传参数,那就使用在函数定义时所设定的初始值

2.2、全缺省与半缺省

2.2.1、全缺省

全缺省顾名思义就是函数中全部参数给缺省值,例子如下:

#include<iostream> //这里函数用三个参数做例子,可以更直观地看出全缺省的作用 void Func(int a = 10, int b = 20, int c = 30) { std::cout << a << " " << b << " " << c << std::endl; } int main() { int a = 1, b = 2, c = 3; Func(a,b,c); return 0; }

运行结果如下:

既然是全缺省,那是不是可以所有参数都不传进去?确实可以,想下面这样:

//这里为了方便,只展示主函数,其余的与上面的代码保持相同 int main() { int a = 1, b = 2, c = 3; Func(); return 0; }

运行结果如下:

这时候又有老铁要问了,能不能值传部分参数的值进去?我不想传完,行不行?答案是可以的,像这样:

int main() { int a = 1, b = 2, c = 3; Func(a); Func(a, b); return 0; }

我们可以传只传第一个参数的值进去,也可以只传前两个参数进去,但是如果我们想只传第一个和第三个的参数进去呢?写成这样可以吗?

Func(a, ,c);

很显然是不行的,为什么?因为在制定语法的时候这是不被允许的,但是我们如果真的只想传第一个和第三个的值呢?我们可以怎么做?简单,只用调换一下位置就行了:

#include<iostream> //像这样子调换一下b和c的位置即可 void Func(int a = 10, int c = 30 int b = 20) { std::cout << a << " " << b << " " << c << std::endl; } int main() { int a = 1, b = 2, c = 3; //只传入a和c的值 Func(a, c); return 0; }

运行结果如下:

这样一来,只有a和c的值被修改了

小贴士:在传入值的时候,只能从左向右依次传值,不能跨越地,间隔跳跃地传值

2.2.2、半缺省

与全缺省相反,半缺省就是对函数中部分变量进行初始化,其中规定只能从右往左依次缺省,不能间隔跳跃缺省:

#include<iostream> //只缺省最后一个参数 void Func(int a , int b, int c=10) { std::cout << a << " " << b << " " << c << std::endl; } int main() { int a = 1, b = 2, c = 50; //这时候就要将前两个参数全部传进去 Func(a, b); return 0; }

运行结果如下:

如果想要让两个参数缺省,那就这样子写:

#include<iostream> //void Func(int a = 10,int b,int c = 20) 这种格式是错误的 void Func(int a , int b =20, int c=10) { std::cout << a << " " << b << " " << c << std::endl; } int main() { int a = 1, b = 2, c = 50; Func(a); return 0; }

运行结果如下:

小贴士:带缺省参数的函数调用,C++规定必须是从左到右依次给实参,不能跳跃给实参函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,这时候规定必须是函数声明时给缺省值.

三、C++的函数重载

在C语言中,我们经常会碰到这样问题:写很多个功能相同的函数但是命名却不能相同,这样就导致了程序员在编写代码时的效率大幅度降低,而C++的函数重载就是为了解决这个问题的

3.1、函数重载的定义:

C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤中出现同名函数的。

 #include<iostream> //这是参数类型不相同 int add(int a, int b) { std::cout << "int (a+b) " << a + b << std::endl; return a + b; } double add(double a, double b) { std::cout << "double (a + b) " << a + b << std::endl; return a + b; } //这是参数个数不相同 int add2(int a, int b, int c) { std::cout <<"int (a + b + c) " <<a + b + c << std::endl; return a + b + c; } int add2(int a, int b, int c, int d) { std::cout << "int (a + b + c + d) " << a + b + c + d << std::endl; return a + b + c + d; } //这是参数类型顺序不同 void add3(int a, double b) { std::cout << "int a,int b" << std::endl; } void add3(double b, int a) { std::cout << "int b,int a " << std::endl; } int main() { int a = 10, b = 20, c = 30, d = 40; double a2 = 1.2, b2 = 2.2; add(a, b); add(a2, b2); add2(a, b, c); add2(a, b, c, d); return 0; }

运行结果如下:

小贴士:返回值的不同不能作为重载条件,因为如果是同名函数的话,编译器不能区分调用哪个:

#include<iostream> int fxx() { std::cout << "int fxx()" << std::endl; } void fxx() { std::cout << "void fxx()" << std::endl; } int main(){ fxx(); return 0; }

但是也要小心缺省函数带来的类似返回值不同的效果:

#include<iostream> void fxx(int a){ std:::cout << "fxx(int a)" << std::endl; } void fxx(){ std::cout << "fxx()" << std::endl; } int main(){ //这就是缺省参数带来的类似返回值不同的效果 fxx(); return 0; }

那么以上就是本次所有的内容了文章是自己写的哈,有什么描述不对的、不恰当的地方,恳请大佬指正,看到后会第一时间修改,感谢您的阅读~

Read more

【Linux】线程池(一)C++ 手写线程池:基于策略模式实现高性能日志模块

【Linux】线程池(一)C++ 手写线程池:基于策略模式实现高性能日志模块

文章目录 * 池化技术 * 线程池的日志模块 * 日志与策略模式 * 日志模块 * 两个核心问题 * 设计文件等级 * 刷新策略 * 获取日志时间 * logger类实现 * 内部类LogMessage实现 * 日志刷新流程图及源码 池化技术 池化技术可以减少很多的底层重复工作,例如创建进程、线程、申请内存空间时的系统调用和初始化工作,例如线程池,先预先创建好一些线程,当任务到来时直接将预先创建好的线程唤醒去处理任务,效率会远远高于任务到来时临时创建线程。例如内存池,但我们要用1mb空间时内存池会一次性申请20mb空间,效率会远远高于用多少空间申请多少空间(申请空间会调用系统调用)。 线程池是执行流级别的池化技术,STL中的空间配置器和内存池是内存块管理级别的池化技术。 线程池的日志模块 下⾯开始,我们结合我们之前所做的所有封装,进⾏⼀个线程池的设计。在写之前,我们要做如下准备。 * 准备线程的封装 * 准备锁和条件变量的封装 * 引⼊日志,对线程进⾏封装 日志与策略

By Ne0inhk
【C++】map详解

【C++】map详解

📢博客主页:https://blog.ZEEKLOG.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 ZEEKLOG🙉 📢未来很长,值得我们全力奔赴更美好的生活✨ 文章目录 * 📢前言 * 🏳️‍🌈一、pair类型介绍 * 🏳️‍🌈二、map的增删查 * 🏳️‍🌈三、map的数据修改 * 🏳️‍🌈四、使用样例 * 🏳️‍🌈五、multimap和map的差异 * 👥总结 📢前言 map的结构和set很类似,部分功能就不演示了,上一篇博客中有 Map 是 C++ 中非常重要的关联容器之一。它以键值对的形式存储数据,其中每个键都是唯一的,这意味着不能有重复的键。如果尝试插入一个已存在的键,将会覆盖该键对应的值。 Map 的内部结构是红黑树,这使得它具有很多优点。首先,数据是有序的,这有助于高效地进行查找、

By Ne0inhk
【C++】类和对象(中)

【C++】类和对象(中)

一、类的默认成员函数 编译器会自动生成的成员函数称为默认成员函数。一个类,不写的情况下编译器会默认生成以下6个默认成员函数。另外在C++11中,增加了两个默认成员函数,移动构造和移动赋值。默认成员函数从两方面学习: 1. 我们不写时,编译器默认生成的函数行为是啥?满足我们的需求吗? 编译器默认生成的函数不满足我们的需求,那如何自己实现? 二、构造函数 构造函数主要任务是对象实例化时初始化对象。就像每次写栈或队列时需要初始化Stack Init()、Queue Init(),用了构造函数就不需要写这一步。 构造函数的特点:函数名与类名相同:类class Stack,类中的函数Stack()无返回值。也无void对象实例化时系统会自动调用对应的构造函数构造函数可以重载如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成无参构造函数、全缺省构造函数、我们不写构造时编译器默认生成的构造函数,都叫做默认构造函数。但是这三个函数有且只有一个存在,不能同时存在。无参构造函数和全缺省构造函数虽然构成函数重载,但是调用时会存在歧

By Ne0inhk
C++ ODB ORM 完全指南:从入门到实战应用

C++ ODB ORM 完全指南:从入门到实战应用

文章目录 * ODB基本概念 * ODB框架安装 * 常见操作 * ODB类与接口 * 测试示例 ODB基本概念 ODB 是一个针对 C++ 的对象关系映射(ORM)库,它允许开发者以面向对象的方式操作数据库,将C++ 对象与数据库表进行映射,从而避免直接编写 SQL 语句,简化数据库操作。 特点: * 对象 - 关系映射:将 C++ 类映射到数据库表,类的成员变量映射到表的字段,对象的创建、修改、删除等操作会自动转换为对应的数据库操作(如 INSERT、UPDATE、DELETE)。 * 代码生成机制:ODB 不依赖运行时反射(C++ 本身不支持),而是通过编译期代码生成实现映射:开发者使用特殊的注解(如 #pragma db object)标记需要持久化的类,然后通过 ODB 编译器生成与数据库交互的代码(

By Ne0inhk