std::cin详解

std::cin

cin>>

cin>> 用于连续从键盘中读取想要的数据以,空格;tab;换行作为分隔符

cin是C++编程语言中的标准输入流对象,即istream类的对象。cin主要用于从标准输入读取数据,这里的标准输入,指的是终端的键盘。此外,cout是流的对象,即ostream类的对象,cerr是标准错误输出流的对象,也是ostream 类的对象。这里的标准输出指的是终端键盘,标准错误输出指的是终端的屏幕。

在理解cin功能时,不得不提标准输入缓冲区。当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在cin的缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。

需要注意

  • cin>>等价于cin.operator>>(),即调用成员函数operator>>()进行读取数据。
  • 当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理
  • 不想略过空白字符,那就使用 noskipws 流控制。比如cin>>noskipws>>input;

从标准输入读取一行字符串的方法

getline读取一行

C++中定义了一个在std名字空间的全局函数getline,因为这个getline函数的参数使用了string字符串,所以声明在了<string>头文件中了。

getline利用cin可以从标准输入设备键盘读取一行,当遇到如下三种情况会结束读操作:1)到文件结束,2)遇到函数的定界符,3)输入达到最大限度。

函数原型有两个重载形式:

istream& getline ( istream& is, string& str);//默认以换行符结束
istream& getline ( istream& is, string& str, char delim)

例如

#include <iostream>
#include <string>

using namespace std;

int main() {
  int flag;
  cout << "please cin a number" << endl;
  cin >> flag;
  cout << "flag: " << flag << endl;

  cin >> flag;
  cout << "flag: " << flag << endl;
  if (flag == 1)
    cout << "true" << endl;
  else {
    cout << "false" << endl;
  }

  string data;
  getline(cin, data);
  cout << "data: " << data << "data size is :" << data.size() << endl;
  return 0;
}

输出

please cin a number
1 2 3 4 5 4
flag: 1
flag: 2
false
data:  3 4 5 4data size is :8

tip: cin会从输入缓冲区读取,以空格,tab,回车为界限,而getline会把空格,tab读进来

<think>我们正在讨论C++中如何通过友元函数重载输入流(std::istream)的问题。根据引用内容,我们可以总结出以下几点关键信息: 1. 重载输入流运算符`>>`为友元函数的原因:因为运算符的左操作数是流对象(如cin),而不是自定义类的对象,因此不能使用成员函数重载(因为成员函数的第一个隐含参数是类的对象,即this指针指向的对象)。所以必须使用非成员函数,而为了访问类的私有成员,这个非成员函数需要声明为类的友元。 2. 基本语法结构: - 在类内部声明友元函数:`friend istream& operator>>(istream& in, 类名& 对象);` - 在类外部定义这个函数。 3. 函数参数和返回值: - 参数:第一个参数是输入流的引用(如istream& in),第二个参数是自定义类对象的引用(非常量,因为要修改对象)。 - 返回值:返回输入流的引用,以支持链式输入(如`cin >> a >> b;`)。 4. 实现步骤: - 在类中声明友元函数。 - 在类外定义该函数,从输入流中读取数据并赋值给对象的私有成员。 5. 注意事项: - 由于输入操作会修改对象,所以第二个参数不能是const的。 - 返回流的引用是为了支持连续输入。 下面我们结合引用中的示例,详细说明如何重载输入流运算符。 ### 步骤详解 #### 1. 类定义中声明友元函数 在类的定义中,使用`friend`关键字声明输入流重载函数。例如: ```cpp class MyClass { private: int data1; double data2; public: // 声明友元函数,注意第二个参数是非常量引用 friend std::istream& operator>>(std::istream& in, MyClass& obj); }; ``` #### 2. 在类外部定义函数 在类定义的外部,实现这个友元函数。在这个函数中,我们可以直接访问类的私有成员。 ```cpp std::istream& operator>>(std::istream& in, MyClass& obj) { in >> obj.data1 >> obj.data2; // 直接访问私有成员 return in; // 返回流引用以支持链式输入 } ``` ### 完整示例代码 下面是一个完整的示例,演示如何重载输入流运算符`>>`和输出流运算符`<<`(为了完整,也给出输出流的重载): ```cpp #include <iostream> #include <iomanip> class MyClass { private: int a; double b; public: // 声明输入输出流操作符重载为友元函数 friend std::istream& operator>>(std::istream& in, MyClass& obj); friend std::ostream& operator<<(std::ostream& out, const MyClass& obj); }; // 输入流重载 std::istream& operator>>(std::istream& in, MyClass& obj) { in >> obj.a >> obj.b; return in; } // 输出流重载 std::ostream& operator<<(std::ostream& out, const MyClass& obj) { out << "a=" << obj.a << ", b=" << std::fixed << std::setprecision(2) << obj.b; return out; } int main() { MyClass obj; std::cout << "请输入整数和浮点数(用空格分隔): "; std::cin >> obj; // 调用友元函数operator>> std::cout << "您输入的是: " << obj << std::endl; // 调用operator<< return 0; } ``` ### 关键点说明 - **为什么用友元?**:因为`operator>>`需要访问类的私有成员,而它又不是类的成员函数(因为第一个参数不是类对象),所以必须声明为友元。 - **链式输入**:通过返回流引用,可以实现连续输入,如`std::cin >> obj1 >> obj2;`。 - **参数类型**:输入流重载的第二个参数必须是非常量引用,因为我们要修改这个对象。 ### 常见问题 1. **是否可以将重载的运算符定义为类的成员函数?** - 对于输入输出流运算符,不能。因为如果定义为成员函数,则第一个参数必须是类对象(即`*this`),而实际上我们调用时第一个参数是流对象(如`cin`),所以必须定义为非成员函数。 2. **为什么输出流重载的第二个参数可以是常量引用?** - 因为输出操作通常不改变对象的状态,所以使用常量引用是安全的。但输入操作需要修改对象,所以必须是非常量引用。 3. **如何同时重载输入和输出?** - 如上例所示,在类中分别声明两个友元函数,然后在外部定义它们。 ### 总结 在C++中,重载输入流运算符`>>`需要: 1. 在类内部声明友元函数`friend istream& operator>>(istream&, 类名&);` 2. 在类外部定义该函数,读取数据到对象的私有成员 3. 返回流引用以支持链式输入 这种方式确保了非成员函数能够访问类的私有成员,同时符合运算符的使用习惯。 §§相关问题§§ 1. 如何重载输出流(`<<`)运算符? 2. 友元函数与成员函数重载运算符的区别是什么? 3. 重载输入流时如何处理错误输入(如类型不匹配)? 4. 是否可以为同一个类重载多个不同的输入流操作? 5. 在继承体系中如何重载输入输出流运算符?</think>### C++ 友元函数与输入流(std::istream)重载详解 在 C++ 中,通过友元函数重载输入流(`std::istream`)运算符(`>>`)是访问类私有数据的标准方法。以下是核心要点和实现步骤: #### 1. **为什么需要友元函数重载输入流?** - **访问私有成员**:输入操作需要直接修改类的私有数据成员[^4] - **参数顺序限制**:运算符左操作数是流对象(`cin`),右操作数是类对象,不符合成员函数要求(成员函数隐含`this`指针作为左操作数)[^5] - **常量左操作数问题**:当左操作数是常量流对象时,只能用友元函数重载[^5] #### 2. **基本实现结构** ```cpp #include <iostream> class MyClass { private: int value; std::string name; public: // 声明友元函数 friend std::istream& operator>>(std::istream& in, MyClass& obj); }; // 定义输入流重载 std::istream& operator>>(std::istream& in, MyClass& obj) { in >> obj.value >> obj.name; // 直接访问私有成员 return in; // 返回流引用以支持链式输入 } ``` #### 3. **关键要素解析** | 要素 | 说明 | 必要性 | |------|------|--------| | **返回类型** | `std::istream&` | 必须返回流引用以支持链式输入(`cin >> a >> b;`)[^1] | | **第一个参数** | `std::istream& in` | 输入流引用(可以是`cin`或文件流) | | **第二个参数** | `MyClass& obj` | **必须是非const引用**(因要修改对象状态)[^5] | | **friend关键字** | 类内声明 | 授予访问私有成员的权限[^3] | #### 4. **完整示例(含输出流重载)** ```cpp #include <iostream> #include <string> class Person { private: std::string name; int age; public: // 输入流重载(友元声明) friend std::istream& operator>>(std::istream& in, Person& p); // 输出流重载(友元声明) friend std::ostream& operator<<(std::ostream& out, const Person& p); }; // 输入流重载实现 std::istream& operator>>(std::istream& in, Person& p) { std::cout << "输入姓名和年龄: "; in >> p.name >> p.age; // 直接修改私有成员 return in; } // 输出流重载实现 std::ostream& operator<<(std::ostream& out, const Person& p) { out << "姓名: " << p.name << ", 年龄: " << p.age; return out; } int main() { Person person; std::cin >> person; // 调用友元operator>> std::cout << person; // 调用友元operator<< return 0; } ``` *执行流程:* ``` 输入姓名和年龄: Alice 30 姓名: Alice, 年龄: 30 ``` #### 5. **最佳实践与注意事项** 1. **参数顺序一致性**: ```cpp // 正确:istream作为第一参数 friend istream& operator>>(istream& in, MyClass& obj); // 错误:不符合标准调用约定 friend istream& operator>>(MyClass& obj, istream& in); ``` 2. **错误处理增强**: ```cpp std::istream& operator>>(std::istream& in, Person& p) { if(!(in >> p.name)) { // 检查输入有效性 throw std::runtime_error("无效姓名输入"); } // ... 其他输入 return in; } ``` 3. **内联优化**: ```cpp // 头文件中声明为内联(高频调用场景) class MyClass { public: friend inline std::istream& operator>>(std::istream& in, MyClass& obj); }; ``` 4. **继承体系处理**: ```cpp class Student : public Person { private: int id; public: // 重写输入流 friend std::istream& operator>>(std::istream& in, Student& s) { operator>>(in, static_cast<Person&>(s)); // 调用基类输入 in >> s.id; return in; } }; ``` #### 6. **与成员函数重载的对比** | 特性 | 友元函数重载 | 成员函数重载 | |------|--------------|--------------| | 访问私有成员 | ✓ | ✓ | | 左操作数为流对象 | ✓ | **✗** (要求左操作数是类对象) | | 支持`cin >> obj`语法 | ✓ | ✗ (需写成`obj >> cin`) | | 常量左操作数支持 | ✓ | ✗ | ### 常见问题解决 **Q:为什么有时需要单独声明友元函数?** A:当运算符定义在类外部时,需在类内用`friend`声明以突破访问限制[^3]。 **Q:如何处理输入格式错误?** A:在重载函数中检查流状态: ```cpp std::istream& operator>>(std::istream& in, MyClass& obj) { if(!(in >> obj.value)) { in.setstate(std::ios::failbit); // 设置错误状态 } return in; } ``` **Q:是否支持文件流输入?** A:完全支持,因`ifstream`继承自`istream`: ```cpp std::ifstream file("data.txt"); MyClass obj; file >> obj; // 使用相同的重载运算符 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值