这个标题有点乱,只是刚好想把这两点整理一下,就放在一起了!
c++除了能使用c语言的强制类型转换外,还新增了四种强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast。语法为:
static_cast (expression)
dynamic_cast (expression)
const_cast (expression)
reinterpret_cast (expression)
注:new_type为目标数据类型,expression为原始数据类型变量或者表达式。
用于修改类型的const或volatile属性。 除了const 或volatile修饰之外, new_type和expression的类型是一样的。
const char *a;
char *b = const_cast(a);//去掉const指针const属性
char *a;
const char *b = const_cast(a);//给指针加上const属性
const int g = 20;
int *h = const_cast(&g);//去掉const常量const属性
const int g = 20;
int &h = const_cast(g);//去掉const引用const属性
const char *g = "hello";
char *h = const_cast(g);//去掉const指针const属性
相当于传统的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(p);
//输出c
c->test();
//Parent test加上 virtual 输出 p
int e = 10;
const int f = static_cast(e);//正确,将int型数据转换成const int型数据
const int g = 20;
int *h = static_cast(&g);//编译错误,static_cast不能转换掉g的const属性
new_type必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针(new_type)转换成一个整数,也可以把一个整数转换成一个指针(new_type)(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。
错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。
int output(int p){
cout << p <(&p);
fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation
float i = 10;
//&i float指针,指向一个地址,转换为int类型,j就是这个地址
int j = reinterpret_cast(&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(&c), static_cast (&c));
前两个的输出值是相同的,最后一个则会在原基础上偏移4个字节,这是因为static_cast计算了父子类指针转换的偏移量,并将之转换到正确的地址(c里面有m_a,m_b,转换为B*指针后指到m_b处),而reinterpret_cast却不会做这一层转换。
运算形式如下:
dynamic_cast
dynamic_cast
dynamic_cast
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(p);
if (c) {
cout << "转换成功" << endl;
}
值得注意的是,我们可以在一个操作中同时完成类型转换和条件检查两项任务,如下代码:
//指针类型
if(Child *c = dynamic_cast(p);){
//使用c指向的Child对象
}
else{
//使用p指向的Parent对象
}
//引用类型
void f(const Parent &b){
try{
const Child &d = dynamic_cast(b);
//使用b引用的Child 对象
}
catch(std::bad_cast){
//处理类型转换失败的情况
}
}
注:因为不存在所谓空引用,所以引用类型的dynamic_cast转换与指针类型不同,在引用转换失败时,会抛出std::bad_cast异常,该异常定义在头文件typeinfo中。
//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;
主要介绍
相关类以及
的fopen
。
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();
}
一行一行读取
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;
}
}
>>
操作符就和我们常用的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;
}
C语言类型的文件读写,只需#include
即可。
打开文件FILE * fopen ( const char * filename, const char * mode );
mode 可取
mode | description |
---|---|
“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
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;
}