C++類型轉(zhuǎn)換和IO流操作處理教程
前言
首先我們看看C語言中的類型轉(zhuǎn)換:
在 C 語言中,如果 賦值運算符左右兩側(cè)類型不同,或者形參與實參類型不匹配,或者返回值類型與 接收返回值類型不一致時,就需要發(fā)生類型轉(zhuǎn)化 , C 語言中總共有兩種形式的類型轉(zhuǎn)換: 隱式類型 轉(zhuǎn)換和顯式類型轉(zhuǎn)換 。
1. 隱式類型轉(zhuǎn)化:編譯器在編譯階段自動進行,能轉(zhuǎn)就轉(zhuǎn),不能轉(zhuǎn)就編譯失敗
2. 顯式類型轉(zhuǎn)化:需要用戶自己處理
int main()
{
int i = 1;
// 隱式類型轉(zhuǎn)換
double d = i;
printf("%d, %.2f\n", i, d);
int* p = &i;
// 顯示的強制類型轉(zhuǎn)換
int address = (int)p;
printf("%x, %d\n", p, address);
}對于上面這種C語言的類型轉(zhuǎn)換其實是有一些缺陷的,轉(zhuǎn)換的可視性比較差,所有的轉(zhuǎn)換形式都是以一種相同形式書寫,難以跟蹤錯誤的轉(zhuǎn)換。
下面我們看看C++中對于類型轉(zhuǎn)換的修改
一、C++的四種類型轉(zhuǎn)換
為什么C++需要重新改進類型轉(zhuǎn)換呢?
C 風(fēng)格的轉(zhuǎn)換格式很簡單,但是有不少缺點的:
1. 隱式類型轉(zhuǎn)化有些情況下可能會出問題:比如數(shù)據(jù)精度丟失
2. 顯式類型轉(zhuǎn)換將所有情況混合在一起,代碼不夠清晰
因此 C++ 提出了自己的類型轉(zhuǎn)化風(fēng)格,注意 因為 C++ 要兼容 C 語言,所以 C++ 中還可以使用 C 語言的 轉(zhuǎn)化風(fēng)格 。
標(biāo)準(zhǔn) C++ 為了加強類型轉(zhuǎn)換的可視性,引入了四種命名的強制類型轉(zhuǎn)換操作符:
static_cast 、 reinterpret_cast 、 const_cast 、 dynamic_cast
第一種:static_cast
static_cast 用于非多態(tài)類型的轉(zhuǎn)換(靜態(tài)轉(zhuǎn)換),編譯器隱式執(zhí)行的任何類型轉(zhuǎn)換都可用
static_cast ,但它不能用于兩個不相關(guān)的類型進行轉(zhuǎn)換
下面我們用代碼演示一下:
int main()
{
int i = 1;
double d = static_cast<double>(i);
float c = static_cast<float>(d);
return 0;
}我們只需要記?。簊tatic_cast適用于可以隱式轉(zhuǎn)換的類型。
第二種:reinterpret_cast
reinterpret_cast 操作符通常為操作數(shù)的位模式提供較低層次的重新解釋,用于將一種類型轉(zhuǎn)換 為另一種不同的類型 。
下面我們用代碼演示一下:
int main()
{
int i = 1;
int* p = &i;
int t = reinterpret_cast<int>(p);
cout << t << endl;
int c = 10;
int* d = reinterpret_cast<int*>(c);
cout << d << endl;
return 0;
}
對于reinterpret_cast的使用我們只需要記住適用于不同類型之間的轉(zhuǎn)換即可。
第三種:const_cast
const_cast 最常用的用途就是刪除變量的 const 屬性,方便賦值.
int main()
{
const int a = 2;
int* p = const_cast<int*>(&a);
cout << *p << endl;
*p = 10;
cout << *p << endl;
return 0;
}對于const類型的常變量一般是不能直接修改的,但是可以像我們上面那樣間接的修改,主要還是因為常變量是放在棧中的不是放在常量區(qū)的,注意:常量區(qū)是一定不可以修改的。
下面我們看一道??嫉念}:
int main()
{
const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl;
cout << *p << endl;
return 0;
}上面這段代碼的運行結(jié)果是什么?很多人都會以為是3和3,因為p指向a的空間,修改*p那么a中的內(nèi)容也會被修改,思路沒錯,但是沒有考慮到編譯器的優(yōu)化,這道題的正確答案是2 3。
由于const變量在編譯器看來是不會被修改的,所以本來應(yīng)該先從內(nèi)存讀數(shù)據(jù)到寄存器結(jié)果被編譯器優(yōu)化為直接從寄存器拿數(shù)據(jù)。對于這種情況我們只需要讓編譯器不優(yōu)化,這樣的話編譯器就會從內(nèi)存中讀取a的內(nèi)容打印了:

可以看到這次的結(jié)果就正確了。所以對于const_cast轉(zhuǎn)化是將原本const屬性去掉單獨分出來,這個時候我們就應(yīng)該小心了,const變量盡量不要去改變。
第四種:dynamic_cast
dynamic_cast 用于將一個父類對象的指針 / 引用轉(zhuǎn)換為子類對象的指針或引用 ( 動態(tài)轉(zhuǎn)換 )
向上轉(zhuǎn)型:子類對象指針 / 引用 -> 父類指針 / 引用 ( 不需要轉(zhuǎn)換,賦值兼容規(guī)則 )
向下轉(zhuǎn)型:父類對象指針 / 引用 -> 子類指針 / 引用 ( 用 dynamic_cast 轉(zhuǎn)型是安全的 )
注意:
1. dynamic_cast 只能用于父類含有虛函數(shù)的類
2. dynamic_cast 會先檢查是否能轉(zhuǎn)換成功,能成功則轉(zhuǎn)換,不能則返回 0

我們可以看到父類指針是天然可以接收子類的指針或者引用的,那么如果是將父類給子類呢?

可以看到如果我們不加類型轉(zhuǎn)化的話連編譯都過不了,那么我們用類型轉(zhuǎn)換再試試:

可以看到經(jīng)過類型轉(zhuǎn)換后是沒問題的,并且dynamic_cast在轉(zhuǎn)換中會保證安全性。下面我們看看如果不用dynamic_cast轉(zhuǎn)化會出現(xiàn)什么情況:
class A
{
public:
virtual void f() {}
int _a = 0;
};
class B : public A
{
public:
int _b = 0;
};
void Func(A* ptr)
{
B* btr = (B*)ptr;
cout << btr << endl;
btr->_a++;
btr->_b++;
cout << btr->_a << endl;
cout << btr->_b << endl;
}
int main()
{
A aa;
B bb;
Func(&aa);
Func(&bb);
return 0;
}
我們可以看到直接出錯了,下面我們用安全轉(zhuǎn)換試一下:

我們可以看到當(dāng)子類接收父類指針造成越界的時候安全轉(zhuǎn)化會檢查能否成功轉(zhuǎn)化,對于不能成功轉(zhuǎn)化的直接返回0就像上圖一樣。
總結(jié):
Func中的ptr如果是指向子類對象,那么轉(zhuǎn)回子類類型是沒問題的。
ptr如果是指向父類對象,那么轉(zhuǎn)回子類類型是存在越界風(fēng)險的。
注意 強制類型轉(zhuǎn)換關(guān)閉或掛起了正常的類型檢查 ,每次使用強制類型轉(zhuǎn)換前,程序員應(yīng)該仔細(xì)考慮是 否還有其他不同的方法達到同一目的,如果非強制類型轉(zhuǎn)換不可,則應(yīng)限制強制轉(zhuǎn)換值的作用 域,以減少發(fā)生錯誤的機會。 強烈建議:避免使用強制類型轉(zhuǎn)換
以上就是C++類型轉(zhuǎn)換的全部內(nèi)容了,下面我們進入IO流的學(xué)習(xí)。
二、C++IO流
C 語言的輸入與輸出:
C 語言中我們用到的最頻繁的輸入輸出方式就是 scanf () 與 printf() 。 scanf(): 從標(biāo)準(zhǔn)輸入設(shè)備 ( 鍵 盤 ) 讀取數(shù)據(jù),并將值存放在變量中 。 printf(): 將指定的文字 / 字符串輸出到標(biāo)準(zhǔn)輸出設(shè)備 ( 屏幕 ) 。 注意寬度輸出和精度輸出控制。
語言借助了相應(yīng)的緩沖區(qū)來進行輸入與輸出。
對 輸入輸出緩沖區(qū) 的理解:
1. 可以 屏蔽掉低級 I/O 的實現(xiàn) ,低級 I/O 的實現(xiàn)依賴操作系統(tǒng)本身內(nèi)核的實現(xiàn),所以如果能夠屏 蔽這部分的差異,可以 很容易寫出可移植的程序 。
2. 可以 使用這部分的內(nèi)容實現(xiàn) “ 行 ” 讀取的行為 ,對于計算機而言是沒有 “ 行 ” 這個概念,有了這 部分,就可以定義 “ 行 ” 的概念,然后解析緩沖區(qū)的內(nèi)容,返回一個 “ 行 ” 。
流是什么: “
流 ” 即是流動的意思,是物質(zhì)從一處向另一處流動的過程 ,是對一種 有序連續(xù) 且 具有方向性 的 數(shù) 據(jù) ( 其單位可以是bit,byte,packet )的 抽象描述。 C++ 流是指信息從外部輸入設(shè)備(如鍵盤)向計算機內(nèi)部(如內(nèi)存)輸入和從內(nèi)存向外部輸出設(shè) 備(顯示器)輸出的過程。這種輸入輸出的過程被形象的比喻為 “ 流 ” 。
它的 特性 是: 有序連續(xù) 、 具有方向性 為了實現(xiàn)這種流動, C++ 定義了 I/O 標(biāo)準(zhǔn)類庫,這些每個類都稱為流 / 流類,用以完成某方面的功 能。 C++IO流: C++ 系統(tǒng)實現(xiàn)了一個龐大的類庫,其中 ios 為基類,其他類都是直接或間接派生自 ios 類。

C++標(biāo)準(zhǔn)IO流 :
C++ 標(biāo)準(zhǔn)庫提供了 4 個全局流對象 cin 、 cout 、 cerr 、 clog ,使用 cout 進行標(biāo)準(zhǔn)輸出,即數(shù)據(jù)從內(nèi) 存流向控制臺 ( 顯示器 ) 。使用 cin 進行標(biāo)準(zhǔn)輸入即數(shù)據(jù)通過鍵盤輸入到程序中 ,同時 C++ 標(biāo)準(zhǔn)庫還 提供了 cerr 用來進行標(biāo)準(zhǔn)錯誤的輸出 ,以及 clog 進行日志的輸出 ,從上圖可以看出, cout 、 cerr 、 clog 是 ostream 類的三個不同的對象,因此這三個對象現(xiàn)在基本沒有區(qū)別,只是應(yīng)用場景不 同。
在使用時候必須要包含文件并引入 std 標(biāo)準(zhǔn)命名空間。
注意:
1. cin 為緩沖流。 鍵盤輸入的數(shù)據(jù)保存在緩沖區(qū)中,當(dāng)要提取時,是從緩沖區(qū)中拿 。如果一次輸 入過多,會留在那兒慢慢用, 如果輸入錯了,必須在回車之前修改,如果回車鍵按下就無法 挽回了 。 只有把輸入緩沖區(qū)中的數(shù)據(jù)取完后,才要求輸入新的數(shù)據(jù) 。
2. 輸入的數(shù)據(jù)類型必須與要提取的數(shù)據(jù)類型一致 ,否則出錯。出錯只是在流的狀態(tài)字 state 中對 應(yīng)位置位(置 1 ),程序繼續(xù)。
3. 空格和回車都可以作為數(shù)據(jù)之間的分格符,所以多個數(shù)據(jù)可以在一行輸入,也可以分行輸 入。但如果是 字符型和字符串,則空格( ASCII 碼為 32 )無法用 cin 輸入,字符串中也不能有 空格 。回車符也無法讀入。
4. cin 和 cout 可以直接輸入和輸出內(nèi)置類型數(shù)據(jù),原因: 標(biāo)準(zhǔn)庫已經(jīng)將所有內(nèi)置類型的輸入和 輸出全部重載了 :

下面我們看看OJ中的循環(huán)輸入:
int main()
{
string str;
while (cin >> str)
{
cout << str << endl;
}
return 0;
}不知道我們是否會有疑問>>符號是如何像邏輯判斷操作符那樣在循環(huán)體中進行判斷的呢?

我們可以看到>>符號的返回值是istream&,也不是bool類型,下面我們看文檔:


其實在文檔中我們發(fā)現(xiàn)不管是C++11還是C++98都重載了operator bool,重載的目的就是支持自定義類型隱式轉(zhuǎn)換為自定義類型,也就是說支持將istream&轉(zhuǎn)化為bool類型。當(dāng)然其實日常使用中我們看到最多的是內(nèi)置類型隱式轉(zhuǎn)換成自定義類型,比如下面這樣:
class A
{
public:
A(int a)
:_a1(1)
, _a2(2)
{}
private:
int _a1;
int _a2;
};
int main()
{
A aa = 1;
return 0;
}
上圖中我們的aa就是自定義類型,1是內(nèi)置類型,將1給aa的過程中發(fā)生了隱式類型轉(zhuǎn)化,由內(nèi)置類型轉(zhuǎn)化為自定義類型。

上圖中我們可以看到,正常情況下我們是不能將自定義類型轉(zhuǎn)化為內(nèi)置類型的,但是當(dāng)我們重載了int的轉(zhuǎn)換后就可以了:
operator int()
{
return _a1 + _a2;
}

下面我們將日期類實現(xiàn)為像>>一樣可以判斷的:
class Date
{
friend ostream& operator << (ostream& out, const Date& d);
friend istream& operator >> (istream& in, Date& d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
operator bool()
{
if (_year > 0)
{
return true;
}
else
{
return false;
}
}
private:
int _year;
int _month;
int _day;
};
istream& operator >> (istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
ostream& operator << (ostream& out, const Date& d)
{
out << d._year << " " << d._month << " " << d._day;
return out;
}
int main()
{
Date d1(0, 5, 30);
while (d1)
{
cout << d1 << endl;
}
return 0;
}我們重載bool類型的時候直接用year做判斷了這里只是為了演示,對于年份為0的日期進入while循環(huán)后會直接退出,如果是年份非0的日期則會死循環(huán)的打印。
C++文件IO流:
C++ 根據(jù)文件內(nèi)容的數(shù)據(jù)格式分為 二進制文件 和 文本文件 。采用文件流對象操作文件的一般步驟:
1. 定義一個文件流對象 ifstream ififile( 只輸入用 ) ofstream ofifile( 只輸出用 ) fstream iofifile( 既輸入又輸出用 )
2. 使用文件流對象的成員函數(shù)打開一個磁盤文件,使得文件流對象和磁盤文件之間建立聯(lián)系
3. 使用提取和插入運算符對文件進行讀寫操作,或使用成員函數(shù)進行讀寫
4. 關(guān)閉文件
struct ServerInfo
{
char _address[32];
int _port;
Date _date;
};
struct ConfigManager
{
public:
ConfigManager(const char* filename)
:_filename(filename)
{}
void WriteBin(const ServerInfo& info)
{
ofstream ofs(_filename, ios_base::out | ios_base::binary);
ofs.write((const char*)&info, sizeof(info));
}
void ReadBin(ServerInfo& info)
{
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs.read((char*)&info, sizeof(info));
}
void WriteText(const ServerInfo& info)
{
ofstream ofs(_filename);
ofs << info._address << " " << info._port<< " "<<info._date;
}
void ReadText(ServerInfo& info)
{
ifstream ifs(_filename);
ifs >> info._address >> info._port>>info._date;
}
private:
string _filename; // 配置文件
};上面是一個文件管理的類,里面封裝了二進制讀寫和文本讀寫的接口,由于C++IO流屬于了解性的內(nèi)容所以我們就不再詳細(xì)的講解每個函數(shù),有不會的接口大家自行查文檔即可。下面我們用一個實例使用一下以上的接口:
int main()
{
ServerInfo winfo = { "192.0.0.1", 80, { 2022, 4, 10 } };
// 二進制讀寫
ConfigManager cf_bin("test.bin");
cf_bin.WriteBin(winfo);
ServerInfo rbinfo;
cf_bin.ReadBin(rbinfo);
cout << rbinfo._address << " " << rbinfo._port <<" "
<<rbinfo._date << endl;
// 文本讀寫
ConfigManager cf_text("test.text");
cf_text.WriteText(winfo);
ServerInfo rtinfo;
cf_text.ReadText(rtinfo);
cout << rtinfo._address << " " << rtinfo._port << " " <<
rtinfo._date << endl;
return 0;
}C++ 文件流的優(yōu)勢就是可以對內(nèi)置類型和自定義類型,都使用 一樣的方式,去流插入和流提取數(shù)據(jù),當(dāng)然這里自定義類型Date 需要重載 >> 和 << 。
istream& operator >> (istream& in, Date& d)
ostream& operator << (ostream& out, const Date& d)
stringstream 的簡單介紹 :
在 C 語言中,如果想要將一個整形變量的數(shù)據(jù)轉(zhuǎn)化為字符串格式,如何去做?
1. 使用 itoa() 函數(shù)
2. 使用 sprintf() 函數(shù)
但是兩個函數(shù)在轉(zhuǎn)化時,都得 需要先給出保存結(jié)果的空間 ,那空間要給多大呢,就不太好界定, 而且 轉(zhuǎn)化格式不匹配時,可能還會得到錯誤的結(jié)果甚至程序崩潰 。
在 C++ 中,可以使用 stringstream 類對象來避開此問題。
在程序中如果想要使用 stringstream ,必須要包含頭文件 。
在該頭文件下,標(biāo)準(zhǔn)庫三個類: istringstream 、 ostringstream 和 stringstream ,分別用來進行流的輸入、輸出和輸入輸出操 作,下面主要介紹 stringstream 。
stringstream 主要可以用來:
1. 將數(shù)值類型數(shù)據(jù)格式化為字符串
#include<sstream>
int main()
{
int a = 12345678;
string sa;
// 將一個整形變量轉(zhuǎn)化為字符串,存儲到string類對象中
stringstream s;
s << a;
s >> sa;
// clear()
// 注意多次轉(zhuǎn)換時,必須使用clear將上次轉(zhuǎn)換狀態(tài)清空掉
// stringstreams在轉(zhuǎn)換結(jié)尾時(即最后一個轉(zhuǎn)換后),會將其內(nèi)部狀態(tài)設(shè)置為badbit
// 因此下一次轉(zhuǎn)換是必須調(diào)用clear()將狀態(tài)重置為goodbit才可以轉(zhuǎn)換
// 但是clear()不會將stringstreams底層字符串清空掉
// s.str("");
// 將stringstream底層管理string對象設(shè)置成"",
// 否則多次轉(zhuǎn)換時,會將結(jié)果全部累積在底層string對象中
s.str("");
s.clear(); // 清空s, 不清空會轉(zhuǎn)化失敗
double d = 12.34;
s << d;
s >> sa;
string sValue;
sValue = s.str(); // str()方法:返回stringsteam中管理的string類型
cout << sValue << endl;
return 0;
}2. 字符串拼接
int main()
{
stringstream sstream;
// 將多個字符串放入 sstream 中
sstream << "first" << " " << "string,";
sstream << " second string";
cout << "strResult is: " << sstream.str() << endl;
// 清空 sstream
sstream.str("");
sstream << "third string";
cout << "After clear, strResult is: " << sstream.str() << endl;
return 0;
}3. 序列化和反序列化結(jié)構(gòu)數(shù)據(jù)
struct ChatInfo
{
string _name; // 名字
int _id; // id
Date _date; // 時間
string _msg; // 聊天信息
};
int main()
{
// 結(jié)構(gòu)信息序列化為字符串
ChatInfo winfo = { "張三", 135246, { 2022, 4, 10 }, "晚上一起看電影吧"
};
ostringstream oss;
oss << winfo._name << " " << winfo._id << " " << winfo._date << " "
<< winfo._msg;
string str = oss.str();
cout << str << endl<<endl;
// 我們通過網(wǎng)絡(luò)這個字符串發(fā)送給對象,實際開發(fā)中,信息相對更復(fù)雜,
// 一般會選用Json、xml等方式進行更好的支持
// 字符串解析成結(jié)構(gòu)信息
ChatInfo rInfo;
istringstream iss(str);
iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
cout << "-------------------------------------------------------"
<< endl;
cout << "姓名:" << rInfo._name << "(" << rInfo._id << ") ";
cout <<rInfo._date << endl;
cout << rInfo._name << ":>" << rInfo._msg << endl;
cout << "-------------------------------------------------------"
<< endl;
return 0;
}注意:
1. stringstream 實際是在其底層維護了一個 string 類型的對象用來保存結(jié)果 。
2. 多次數(shù)據(jù)類型轉(zhuǎn)化時,一定要用 clear() 來清空,才能正確轉(zhuǎn)化 ,但 clear() 不會將 stringstream 底層的 string 對象清空。
3. 可以 使用 s. str("") 方法將底層 string 對象設(shè)置為 "" 空字符串 。
4. 可以 使用 s.str() 將讓 stringstream 返回其底層的 string 對象 。
5. stringstream 使用 string 類對象代替字符數(shù)組,可以避免緩沖區(qū)溢出的危險,而且其會對參 數(shù)類型進行推演,不需要格式化控制,也不會出現(xiàn)格式化失敗的風(fēng)險 ,因此使用更方便,更 安全。
總結(jié)
C++IO流這部分知識大多都是偏了解性的知識,所以我們沒有在細(xì)細(xì)的講解,實際上學(xué)到這一部分很多人對C++都基本入門了,這個時候是完全有能力通過官方文檔來自己運用這部分內(nèi)容。
到此這篇關(guān)于C++類型轉(zhuǎn)換和IO流操作處理的文章就介紹到這了,更多相關(guān)C++類型轉(zhuǎn)換和IO流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)LeetCode數(shù)組練習(xí)題
這篇文章主要介紹了C++實現(xiàn)LeetCode的幾道數(shù)組練習(xí)題,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Qt?QtCreator添加自定義注釋的實現(xiàn)方法
在寫代碼的時候我們?yōu)榱艘?guī)范化,一般會加文件注釋、類注釋和函數(shù)注釋,本文主要介紹了Qt?QtCreator添加自定義注釋的實現(xiàn)方法,具有一定的參考價值,感興趣的可以了解一下2023-11-11
C++ 內(nèi)聯(lián)函數(shù)inline Function示例詳解
文章介紹了C++內(nèi)聯(lián)函數(shù)的含義、內(nèi)聯(lián)展開、編譯器何時內(nèi)聯(lián)以及內(nèi)聯(lián)對鏈接和性能的影響,內(nèi)聯(lián)函數(shù)允許在頭文件中定義函數(shù),以避免ODR錯誤,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-11-11
C++中繼承與多態(tài)的基礎(chǔ)虛函數(shù)類詳解
這篇文章主要給大家介紹了關(guān)于C++中繼承與多態(tài)的基礎(chǔ)虛函數(shù)類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09
概述C++中的 public protected private friend關(guān)鍵字的用法
這篇文章簡要概述了C++中的 public protected private friend關(guān)鍵字的用法,非常不錯,具有參考借鑒價值,感興趣的朋友一起學(xué)習(xí)吧2016-08-08
VSstudio中scanf返回值被忽略的原因及解決方法(推薦)
這篇文章主要介紹了VSstudio中scanf返回值被忽略的原因及其解決方法,scanf返回值被忽略,接下來我就告訴大家該如何解決這個問題,需要的朋友可以參考下2022-09-09

