C++ 入门:输入输出、缺省参数与函数重载
在 C 语言时代,我们依赖 printf 和 scanf 完成输入输出,手动指定格式符且存在类型不匹配的风险。C++ 引入了全新的 IO 库,以类型安全和可扩展的设计重构了这一体验。
一、C++ 的输入与输出
1.1 核心载体: 头文件
<iostream> 是 Input Output Stream 的缩写,定义了 cin, cout, endl 等标准 IO 对象,并封装了底层缓冲区管理细节。
注意: 在 Visual Studio 编译器中,<iostream> 可能会间接包含 <stdio.h>,允许混用 cin 与 scanf。但这并非跨平台标准行为,建议避免混用以保证代码的可移植性。
1.2 核心 IO 对象:cin 与 cout
1.2.1 std::cin 标准输入流
std::cin 是 istream 类的对象,面向窄字符(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::cout 是 ostream 类的对象,通过流插入运算符 << 向屏幕写入数据,支持链式调用。
#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 的应用
初学者常误以为 endl 仅是换行符。实际上,它还强制刷新输出缓冲区,将数据立即写入设备。
// 仅换行,不刷新缓冲区,性能更优
std::cout << "Hello\n";
// 换行 + 刷新缓冲区,适用于调试日志等需立即输出的场景
std::cout << "Hello" << std::endl;
频繁使用 endl 会导致性能下降。在高敏感场景中,优先使用 \n,仅在需要立即输出时使用 endl。
1.5 C++ IO 的核心优势
1.5.1 自动识别类型
C++ IO 流通过重载实现了自动类型识别,避免了 C 语言中格式符与变量类型不匹配的未定义行为。
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 自定义类型支持
通过重载 << 和 >> 运算符,可以让自定义类像内置类型一样参与 IO 操作:
#include <iostream>
struct Point { int x, y; };
// 重载<<运算符
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;
return 0;
}
1.6 面向对象的设计思想
C++ IO 流深度融合了面向对象思想:
- 继承体系:
istream和ostream作为基类,派生出文件流 (fstream)、字符串流 (sstream) 等子类。 - 多态与重载:实现不同类型数据的 IO 适配。
- 封装性:底层缓冲区管理被封装,开发者只需关注高层接口。
1.7 提高输入输出效率
若需提升 IO 性能至接近 C 语言水平,可在代码开头添加以下配置:
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
二、C++ 的缺省参数
2.1 定义及基本用法
缺省参数允许在声明或定义函数时为参数指定默认值。调用时若未传实参则使用默认值。
#include<iostream>
void Func(int a = 10) {
std::cout << a;
}
int main() {
Func(); // 输出 10
return 0;
}
2.2 全缺省与半缺省
2.2.1 全缺省
所有参数均设置默认值。调用时可传全部、部分或不传参数。
void Func(int a = 10, int b = 20, int c = 30) {
std::cout << a << " " << b << " " << c << std::endl;
}
int main() {
Func(); // 10 20 30
Func(1); // 1 20 30
Func(1, 2); // 1 2 30
return 0;
}
规则: 只能从左向右依次传值,不能跳跃传参。例如无法直接跳过中间参数只传第一个和第三个。
2.2.2 半缺省
部分参数设置默认值,规定必须从右往左依次缺省,不可间隔跳跃。
// 错误示例: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() {
Func(1); // 输出 1 20 10
return 0;
}
注意: 函数声明和定义分离时,缺省参数只能在声明中给出,不能在两者同时出现。
三、C++ 的函数重载
3.1 函数重载的定义
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;
}
int main() {
int a = 10, b = 20;
double a2 = 1.2, b2 = 2.2;
add(a, b);
add(a2, b2);
return 0;
}
关键点: 返回值的不同不能作为重载条件,因为编译器无法区分调用哪个函数。
// 错误示例:仅返回值不同
int fxx() { return 0; }
void fxx() { }
此外,需注意缺省参数可能导致的歧义,例如一个带缺省参数的函数和一个无参的同名函数可能产生冲突。

