C++學習筆記。頭文件與源文件、解決頭文件重覆引用、要求整數卻輸入字元、Delete[]、判斷文件存在且數據為空、ifstream回到文件頭、override、=default ...
記錄開發簡單職工管理系統遇到的一些問題,黑馬教程
https://www.bilibili.com/video/BV1et411b73Z P147 ~ P166
頭文件與源文件
![image-20230916205810968](https://linxiaoxu.oss-cn-hangzhou.aliyuncs.com/static/pic/2023/09/20230916205816_image-20230916205810968.png)
- 頭文件只聲明,源文件來實現(本質上是類內聲明類外實現)
- 源文件需要引用特定的頭文件
ifndef OOPFINAL_WORKER_H
#define OOPFINAL_WORKER_H
#include <iostream>
#include <string>
using namespace std;
class Worker {
public:
virtual void ShowInfo() = 0;
virtual string getDeptName() = 0;
int m_Id;
string m_Name;
int m_DeptId;
};
#endif //OOPFINAL_WORKER_H
#ifndef OOPFINAL_MANAGER_H
#define OOPFINAL_MANAGER_H
#include <bits/stdc++.h>
#include "Worker.h"
using namespace std;
class Manager : public Worker {
public:
Manager(int id, string name, int dId);
void ShowInfo() override;
string getDeptName() override;
};
#endif //OOPFINAL_MANAGER_H
#include "../headers/Manager.h"
void Manager::ShowInfo() {
cout << "職工編號 " << m_Id << "\t職工姓名 "
<< m_Name << "\t崗位 " << getDeptName() << "\t崗位職責:完成老闆交給的任務,並下發任務給普通員工" << endl;
}
string Manager::getDeptName() {
return "經理";
}
Manager::Manager(int id, string name, int dId) {
this->m_Id = id;
this->m_Name = name;
this->m_DeptId = dId;
}
頭文件重覆引用
#ifndef,#define,#endif
#ifndef 的方式依賴於巨集名字不能衝突(最好採用這種方式)
- 保證同一個文件只會被編譯一次
- 內容完全相同的兩個文件只會編譯其中一個一次
#pragma once
同一個文件不會被編譯多次(物理上的同一個文件,不是指內容相同的兩個文件)
要求整數卻輸入字元
這種情況下會標誌位 failbit 被置 1,字元一直在緩衝區中,沒被讀出,可能出現死迴圈
-
cin.restate() 當cin.rdstate()返回 0 (即ios::goodbit)時表示無錯誤,可以繼續輸入或者操作,若返回4則發生非致命錯誤即ios::failbit,則不能繼續輸入或操作
-
cin.fail() 可以判斷流操作是否失敗,輸入錯誤後,cin.fail()返回為真 錯誤流標誌位被置failbit為true, 當恢復時 failbit被設置為false
-
cin.clear() (預設為0 ,即無錯誤)清除錯誤的標誌位
clear有多種狀態
- goodbit 無錯誤
- Eofbit 已到達文件尾
- failbit 非致命的輸入/輸出錯誤,可輓回
- badbit 致命的輸入/輸出錯誤,無法輓回 若在輸入輸出類里.需要加ios::標識符號
- 通過設置cin.clear() 或 cin.clear(istream::goodbit)清除錯誤狀態
-
cin.ignore() 清除緩衝區
如果輸入錯誤,通過cin.clear()清除了錯誤狀態標誌後,下一次cin輸入時,仍會從緩衝區中讀取數據,而之前的錯誤輸入此時仍存在緩衝區中,所以還會再次被讀取造成錯誤,所以要清空緩衝區
- cin.ignore(要清除的位元組長度,標識)
cin.ignore(1024,’\n’)
該函數就是將選中的位元組取出拋棄掉(cin操作時是以char為單位的)
標識清除的最大長度是1024個位元組。
清除時如果遇到‘\n’就停止,不管是否是1024個位元組。
如果沒有遇到‘\n’就只清除1024個
cin.ignore()的預設參數為cin.ignore(1,EOF),及清除文件描述符前一個位元組
cout << "輸入選擇" << endl;
while (cin >> choice, cin.fail()) {
cout << "輸入有誤,請重新輸入" << endl;
cin.clear(); // 清除錯誤標誌位
cin.ignore(); // 清除緩衝區
}
Delete[]
在09講,添加職工函數中,老師釋放了數組空間,但沒釋放數組元素的空間,為什麼?
- 依舊需要原來的數組元素,在釋放數組前執行了原有元素地址的拷貝
- 新數組 = 舊數組元素 + 新添加的數組元素
以下旨在探究 Delete 數組,會不會調用數組元素的析構器
答案是不會,需要自己手動調用
#include <iostream>
using namespace std;
class Father {
public:
Father() = default;
virtual ~Father() {
cout << "父元素析構器" << endl;
};
};
class Son1 : public Father {
public:
~Son1() {
cout << "一號子類析構器" << endl;
}
};
class Son2 : public Father {
public:
~Son2() {
cout << "二號子類析構器" << endl;
}
};
int main() {
Father **array = new Father *[10];
array[0] = new Son1();
array[1] = new Son2();
array[2] = new Son2();
// delete array[0];
// delete array[1];
// delete array[2];
delete[] array;
cout << "---------" << endl;
Father *test = new Son1();
delete test;
return 0;
}
文件存在且數據為空
- 判斷文件是否被打開
- 讀入一個字元,判斷字元是不是文件尾部標誌
ifs.eof() 判斷是要在讀取到文件結束符之後才會置為true,意思也就是說即使打開一個空文件,你不讀取裡面的數據,ifs.eof() 會預設置為false
peek() 嘗試讀取第一個字元,但不提取(游標位不後移)
// char ch;
// ifs >> ch;
ifs.peek();
if (ifs.eof()) {
// 文件為空
cout << "文件為空" << endl;
m_EmpArray = NULL;
m_EmpNum = 0;
this->m_FileIsEmpty = true;
ifs.close();
return;
}
ifstream 回到文件頭
使用 ifstream 進行文本文件讀取時,如果讀指針位於文件末尾,無法直接通過調用seekg(0, ios::beg) 回到文件開頭,而是需要先調用 clear() 清除指針狀態,再調用seekg(0, ios::beg) 才能成功返迴文件頭
- ios::beg 預設的,從流的開頭開始定位
- ios::cur 從流的當前位置開始定位
- ios::end 從流的末尾開始定位
override
override明確地表示一個函數是對基類中一個虛函數的重載。更重要的是,它會檢查基類虛函數和派生類中重載函數的簽名不匹配問題。如果簽名不匹配,編譯器會發出錯誤信息。
=default
=default
是C++11引入的一種特性,它允許顯式要求編譯器生成預設的特殊成員函數。特殊成員函數包括預設構造函數、複製構造函數、移動構造函數、複製賦值運算符、移動賦值運算符以及析構函數
參考資料
cplusplus.com/reference/istream/istream/seekg/
Set Position with seekg() in C++ File Handling - GeeksforGeeks
c++新特性:=default - 知乎 (zhihu.com)