c++中的强制类型转换,及基本文件读写(对比c语言)

xiaoxiao2021-03-01  11

这个标题有点乱,只是刚好想把这两点整理一下,就放在一起了!

类型转换

c++除了能使用c语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast。语法为:

static_cast<new_type> (expression) dynamic_cast<new_type> (expression) const_cast<new_type> (expression) reinterpret_cast<new_type> (expression) 注:new_type为目标数据类型,expression为原始数据类型变量或者表达式。  

 

const_cast

用于修改类型的const或volatile属性。 除了const 或volatile修饰之外, new_type和expression的类型是一样的。

const char *a; char *b = const_cast<char*>(a);//去掉const指针const属性 char *a; const char *b = const_cast<const char*>(a);//给指针加上const属性 const int g = 20; int *h = const_cast<int*>(&g);//去掉const常量const属性 const int g = 20; int &h = const_cast<int &>(g);//去掉const引用const属性 const char *g = "hello"; char *h = const_cast<char *>(g);//去掉const指针const属性

static_cast  

相当于传统的C语言里的强制转换,该运算符把expression转换为new_type类型,用来强迫隐式转换,例如non-const对象转为const对象,编译时检查,用于非多态的转换,可以转换指针及其他,但没有运行时类型检查来保证转换的安全性。

基本数据类型之间互转。如:float转成int、int转成unsigned int等

把指针转换成void类型指针 。如:float*转成void*、Bean*转成void*、函数指针转成void*等

子类指针/引用与 父类指针/引用 转换。

注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。

class Parent { public: void test() { cout << "p" << endl; } }; class Child :public Parent{ public: void test() { cout << "c" << endl; } }; Parent *p = new Parent; Child *c = static_cast<Child*>(p); //输出c c->test(); //Parent test加上 virtual 输出 p int e = 10; const int f = static_cast<const int>(e);//正确,将int型数据转换成const int型数据 const int g = 20; int *h = static_cast<int*>(&g);//编译错误,static_cast不能转换掉g的const属性

 

 

reinterpret_cast  

 new_type必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针(new_type)转换成一个整数,也可以把一个整数转换成一个指针(new_type)(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。

错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

int output(int p){ cout << p <<endl;   return 0; } typedef int (*test_func)(int );//定义函数指针test_func int main(){ int p = 10; test_func fun1 = output; fun1(p);//正确 test_func fun2 = reinterpret_cast<test_func>(&p); fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation float i = 10; //&i float指针,指向一个地址,转换为int类型,j就是这个地址 int j = reinterpret_cast<int>(&i); cout << hex << &i << endl; cout << hex << j << endl; return 0; }

 

他们的区别 

static_cast和reinterpret_cast的区别主要在于多重继承,比如:

class A { public: int m_a; }; class B { public: int m_b; }; class C : public A, public B {}; C c; printf("%p, %p, %p", &c, reinterpret_cast<B*>(&c), static_cast <B*>(&c));  

前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。 

dynamic_cast

 

运算形式如下:

dynamic_cast<type*>(e)

dynamic_cast<type&>(e)

dynamic_cast<type&&>(e)

type必须是一个类类型,在第一种形式中,type必须是一个有效的指针,在第二种形式中,type必须是一个左值,在第三种形式中,type必须是一个右值。在上面所有形式中,e的类型必须符合以下三个条件中的任何一个:e的类型是是目标类型type的公有派生类、e的类型是目标type的共有基类或者e的类型就是目标type的的类型。如果一条dynamic_cast语句的转换目标是指针类型并且失败了,则结果为0。如果转换目标是引用类型并且失败了,则dynamic_cast运算符将抛出一个std::bad_cast异常(该异常定义在typeinfo标准库头文件中)。e也可以是一个空指针,结果是所需类型的空指针。 

在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。

class A { public: virtual void test(void) = 0; }; class D { public: virtual void test(void) = 0; }; class B : public A { public: void test(void) { cout << "--------------" << endl; } }; class C : public D { public: void test(void) { cout << "++++++++++++++++" << endl; } }; Parent *p = new Child; Child *c = dynamic_cast<Child*>(p); if (c) { cout << "转换成功" << endl; }  

值得注意的是,我们可以在一个操作中同时完成类型转换和条件检查两项任务,如下代码:

//指针类型 if(Child *c = dynamic_cast<Child*>(p);){ //使用c指向的Child对象 } else{ //使用p指向的Parent对象 } //引用类型 void f(const Parent &b){ try{ const Child &d = dynamic_cast<const Parent &>(b); //使用b引用的Child 对象 } catch(std::bad_cast){ //处理类型转换失败的情况 } }

注:因为不存在所谓空引用,所以引用类型的dynamic_cast转换与指针类型不同,在引用转换失败时,会抛出std::bad_cast异常,该异常定义在头文件typeinfo中。 

char*与int转换

  //char* 转int float int i = atoi("1"); float f = atof("1.1f"); cout << i << endl; cout << f << endl; //int 转 char* char c[10]; //10进制 itoa(100, c,10); cout << c << endl; //int 转 char* char c1[10]; sprintf(c1, "%d", 100); cout << c1 << endl;

文件读写

主要介绍<fstream>相关类以及<stdio.h>的fopen。

ifstream

ifstream 是用来操作文件的输入流类。文件流通过构造函数或者通过调用open函数来完成与文件的绑定。

打开文件

通过构造函数std::ifstream ifs ("test.txt", std::ifstream::in);

通过open()函数

std::ifstream ifs; ifs.open("test.txt",std::ifstream::in);

一个一个字符读取 std::istream::get

可以一个一个读取并返回读取到的值,或者读取连续(n-1)个字符并保存在字符数组中。这种读取方式不怎么适用。

void getbychar(){ std::ifstream ifs("test.txt", std::ifstream::in); char str[256]; ifs.get(str, 10); // 从ifs中读取10-1=9个字符,保存在str内 std::cout << "str: " << str << std::endl; char c = ifs.get(); // 一次读取一个字符 while (ifs.good()) { std::cout << c; c = ifs.get(); } ifs.close(); }

一行一行读取

getline 函数 std::istream::getline istream& getline (char* s, streamsize n ); istream& getline (char* s, streamsize n, char delim );

一次读取一行,从流中读取字符并将其保存于c字符串中,知道遇到界定符delim(’\n’)或者已经向s写入了n个字符。

需要注意的是n要大于文件中最长的一行的字符数。如果此函数因为已经读取了n个字符而停止但是却没有找到界定符,那么failbit将会被设置。

void getbyline(){ std::ifstream ifs("test.txt", std::ifstream::in); char str[256]; while(ifs.good()){ // 如果想测试只读取一行中一些字符,判断内可改为 !ifs.eof() && !ifs.fail() ifs.getline(str,256); std::cout << str << std::endl; } } >> 操作符 std::istream::operator>>

>>操作符就和我们常用的cin>>一样的用法

void getlikecin(){ std::ifstream ifs("test.txt"); std::string str; int num; ifs >> num; std::cout << num << std::endl; while(ifs.good()){ ifs >> str; std::cout << str << std::endl; } ifs.close(); }

如果你运行了上面这个程序,你会发现,最后一个字符串会被输出两次!这是因为ifs完成最后一次读取后不会立即设置eof状态,所以程序会再进行一轮循环,在这轮循环ifs才会设置eof状态。结果就是最后一次读取的内容被输出了两次。

while循环可改为:

while(ifs>>str){ std::cout << str << std::endl; }  

或者

while(ifs.good()){ if(ifs >> str) std::cout << str << std::endl; }

stdio.h

C语言类型的文件读写,只需#include <stdio.h>即可。

打开文件FILE * fopen ( const char * filename, const char * mode ); mode 可取

modedescription“r”读:打开一个文件进行读取操作。文件必须已经存在“w”写:创建一个空文件进行写操作。若同名文件已经存在,则会清空原文件的内容。“a”附加:打开一个文件,在文件末尾进行写操作。若文件不存在,则会创建新文件。“+”更新:需要和上述r/w/a进行结合,结合后文件将会变成即可读也可写。“b”二进制读写:需要和上述模式进行结合成”rb”,”wb”,”ab”“x”避免重写已有文件:新C标准但不是C++的一部分,通过和”w”结合成”wx”,”wbx”等,若同名文件已存在,则会迫使此函数失败。 FILE *pFile; pFile = fopen("test.txt","r");

读取文件int fscanf ( FILE * stream, const char * format, ... ); 根据format声明的格式从stram中读取数据并保存在后面的参数中。这种读取适用于文件格式固定,每个字段类型确定的情况下进行读取操作。

#include <stdio.h> int main () { char str [80]; float f; FILE * pFile; pFile = fopen ("myfile.txt","w+"); fprintf (pFile, "%f %s", 3.1416, "PI"); // 先向文件写入 3.1416 PI rewind (pFile); // 设置pFile的位置指示回到文件开头 fscanf (pFile, "%f", &f); // 读取一个浮点数 fscanf (pFile, "%s", str); // 读取一个字符串 fclose (pFile); printf ("I have read: %f and %s \n",f,str); return 0; }

 

转载请注明原文地址: https://www.6miu.com/read-3650184.html

最新回复(0)