c++_cout的理解和使用
问题引入
cout << (uf.is_same_set(x, y)) ? 'Y' : 'N'<<endl;
请问大家,这条语句对吗?(这里的uf.is_same_set(x, y)是一个自定义函数,返回bool值;所以不是问题的关键)==》
答案是这条语句报错了。我再写了以下的语句,却能准确无误。
cout << ((uf.is_same_set(x, y)) ? 'Y' : 'N') << endl;
为什么呢? ==》
1.这是因为第一条语句中,<<的优先级会高于三目运算符,即它先返回的是 ostream&;
然后会再执行ostream?'Y':'N'部分;三目判断返回'Y'
但是'Y' 是 char 类型,不是一个可以用 << 运算的对象。
2.cout << ((uf.is_same_set(x, y)) ? 'Y' : 'N') << endl; 这个表达式会先执行三目,返回‘Y’或者‘N’;然后变为:cout<<'Y'<<endl; 所以就没有错。
基于这个问题,我们今天就来聊聊cout
cout是什么?
cout
是一个全局的 输出流对象,本质上是 ostream
类的一个实例,通过 运算符重载(operator<<
),实现了类似 cout << x
这样的打印效果。
此外还有其他流对象是:
名称 | 类型 | 功能 |
---|---|---|
cin | istream | 标准输入流 |
cerr | ostream | 错误输出流 |
clog | ostream | 日志输出流 |
为什么能用 <<
打印?
这是 C++ 的经典设计:通过重载运算符 <<
(插入运算符)来模拟输出操作。它的原型类似于这样:即它使重载的运算符函数;
ostream& operator<<(ostream& os, int value);
ostream& operator<<(ostream& os, const char* str);
ostream& operator<<(ostream& os, char c);
ostream& operator<<(ostream& os, double d);
ostream& operator<<(ostream& os, bool b);
// 等等,针对不同类型都做了重载
这就意味着你可以这样使用:
cout << "Hello" << ' ' << 42 << ' ' << true;
它的底层执行顺序是:
cout << "Hello" // 返回 ostream&<< ' ' // 继续插入<< 42 // 继续插入<< ' ' // ...<< true; // 一连串流式操作
因为每次 <<
都返回 ostream&
,所以可以链式调用。
输出是怎么“流”到控制台的?
cout
内部持有一个缓冲区(buffer),你每调用一次 <<
,其实是将数据写入这个缓冲区。只有当你:
-
显式使用
endl
(刷新并换行); -
或缓冲区满;
-
或程序结束前清理资源;
才会真正把内容输出到终端控制台。所以我们便能理解下面两条语句的区别了。
cout << "hello\n"; // 可能只是写进缓冲区
cout << "world" << endl; // 此时强制刷新缓冲区
endl 是什么?
endl
是一个特殊的东西,它不是字符串,而是一个 函数指针,原型是:
ostream& endl(ostream& os);
它的作用是:
-
向流中插入一个换行符(
\n
); -
刷新缓冲区(flush);
所以cout << "Hello" << endl; 等价于:
cout << "Hello";
cout.put('\n');
cout.flush();
自定义类型如何支持 <<
输出?
你可以为你的类自己写一个 <<
重载,让 cout << 对象
成为可能:
class Person {
public:string name;int age;
};
// 重载 <<
ostream& operator<<(ostream& os, const Person& p) {os << "Name: " << p.name << ", Age: " << p.age;return os;
}
这样你就能这样写:
Person p{"麦兜", 20};
cout << p << endl;
ok,这个知识点实际上涉及到了运算符重载函数的编写;我先说一个结论:
operator<<必须写成全局函数(或友元函数),为什么呢?
因为 cout << p 是这样调用的:operator<<(cout, p); // ostream 是左边,Person 是右边
如果你写成:
class Person { ostream& operator<<(ostream& os); // 错误!只能是 p << cout
};
这就变成了 p << cout,方向颠倒了,所以没法实现 cout << p 的语法。
故哪些运算符建议写成成员函数?哪些建议全局?
运算符类型 | 推荐形式 | 说明 |
---|---|---|
= , [] , () , -> | 成员函数 | 这些操作符需要访问类的内部状态或作用于“左侧对象本身” |
<< , >> , + , - , == , != | 全局函数或友元函数 | 左侧不是类对象(如 cout << obj ),或需要双操作数对称性 |
cout的使用技巧
-
运算符优先级陷阱 (如我们上面的问题)
-
输出 bool 值时注意格式
cout << true << endl; // 输出: 1
cout << boolalpha << true << endl; // 输出: true ✅
3.输出字符和整数要区
char ch = 'A';
int x = 65;
cout << ch << endl; // 输出: A
cout << static_cast<int>(ch) << endl; // 输出: 65
4.其他格式化技巧:
我们需要加上 头文件: #include <iomanip>
1) 设置小数精度 ;如上
2) 宽度对齐 & 填充字符
cout << setw(10) << 42 << endl; // 宽度为10,默认右对齐
cout << setfill('*') << setw(10) << 42 << endl; // 输出:********42
3)左右对齐
cout << left << setw(10) << 42 << endl; // 左对齐
cout << right << setw(10) << 42 << endl; // 右对齐
4) 输出十六进制/八进制
cout << hex << 255 << endl; // ff
cout << oct << 255 << endl; // 377
cout << dec << 255 << endl; // 255 (恢复十进制)
显示符号位(正数也带 + )
cout << showpos << 123 << endl; // +123
再次理解
运算符重载是指可以自定义某个类使用某个运算符的逻辑,譬如+,-,*等等。
而<<这个运算符也是可以被重载的;例如ostream类就是重载了这个运算符 。
cout是ostream的一个对象,它使用<<能够将数据写入这个缓冲区。
实际上cout<<10是operator<<(cout, 10);的语法糖。即他本质上是调用了operator<<,并把要打印的东西(整数,浮点数,字符,字符串)作为参数。
而这个运算符重载函数返回的是ostream&,即引用类型,则他可以实现链式调用;