return0;
}
8.浮点数
1).表示和存储
系统存储浮点数的方式:
一部分表示值,一部分用于对值进行放大或缩小。
例如:
34.123和3412.3,它们除了小数点位置不同外,其他都是一样的,可以用下面的形式表示
基准值缩放因子
34.123:
0.34123100
3412.3:
0.3412310000
缩放因子作用是移动小数点的位置,一般为2的幂,不是10的幂
2).浮点数的类型
float:
至少32位,
double:
至少64位,且不少于float,
longdouble:
至少喝double一样多,80、96或128位
头文件:
cfloat(C++)和float.h(C)中有系统设置
9.类型转换:
1).整型提升(integralpromotion)
C++:
a).C++将bool、char、unsignedchar、signedchar、short值转换为int。
通常int类型选择为计算机最自然的类型,计算机使用这种类型时,运算速度可能最快
b).unsignedshort与int的转换:
如果short比int短,unsignedshort将被转换为int
如果相同,unsignedshort将被转换为unsignedint
此规则确保在对unsignedshort进行提升时不会损失数据
c).
(1)如果有一个操作数的类型是longdouble,则将另一个操作数转换为longdouble。
(2)否则,如果有一个操作数的类型是double,则将另一个操作数转换为double。
(3)否则,如果有一个操作数的类型是float,则将另一个操作数转换为float。
(4)否则,说明操作数都是整数,因此执行整型提升
(5)如果有一个操作数的类型是unsignedlong,则将另一个操作数转换为unsignedlong。
(6)否则,如果有一个操作数的类型是longint,则另一个操作数为unsignedint,则转换取决于两个类型的相对长度。
如果long能够表示unsignedint的所有可能,则将unsignedint转换为long。
(7)否则,将两个操作数转换为unsignedlong
(8)否则,如果有一个操作数的类型是long,则将另一个操作数转换为long
(9)否则,如果有一个操作数的类型是unsignedint,则将另一个操作数转换为unsignedint
(10)如果编译器到达此处,则说明两个操作数都是int类型。
C:
将有符号和无符号的char和short类型都自动转换为int。
在某些情况下,也会自动转换为unsignedint(例如:
当short与int相同的大小,那么unsignedshort比int大,在这种情况下,将把unsignedshort转换为unsignedint)。
参数传递时的转换:
C++将对char和short类型进行整型提升。
对float参数提升为double。
2).强制类型转换:
a).一般格式
(typeName)value:
C语言中的格式,
typeName(value):
C++语言的格式,新格式的想法是,要让强制类型转换就像是函数调用。
这样对内置类型的强制类型转哈un就像是为用户定义的类涉及的类型转换。
b).C++中的4个强制类型转换操作符
.dynamic_cast基类指针(或引用)转换成派生类指针(引用)时使用,注意:
基类对象无法赋值给派生类的对象(任何类型转换操作符都不可以)
.const_cast去掉参数中的常量性(const性)和volatile性
.static_cast一般的显示类型转换时使用
.reinterpret_cast
可以根据目的选择一个合适的操作符,而不使用通用的类型转换,指出进行类型转换的原因,并让编译器能够检查程序的行为是否与设计者想法吻合。
显式转换符号的一股形式如下:
cast-name(expression);
.dynamic_cast:
1].与C++支持的其他强制转换不同的是dynamic_cast是在运行时刻执行的.
2].如果指针或左值操作数不能被转换成目标类型dynamic_cast将失败如果针对指针类型的dynamic_cast失败,则dynamic_cast的结果是0,如果针对引用类型的dynamic_cast失败则dynamic_cast会抛出一个异常
3].dynamic_cast比其他C++转换操作要安全因为其他转换不会检验转换是否真正能被执行.
4].dynamic_cast被用来执行从基类指针到派生类指针的安全转换它常常被称为安全的向下转换downcasting.
(不使用dynamic_cast,将一个基类的指针赋值给派生类的指针将会引发编译出错,即不允许直接将基类的指针之间赋值给派生类的指针)
5].当我们必须使用派生类的特性而该特性又没有出现在基类中时,我们常常使用dynamic_cast
6].使用dynamic_cast后,必须进行判断是否转换成功,否则在测试dynamic_cast的结果是否为0之前就使用它是一种最常见的错误
实例:
voidcompany:
:
payroll(employee*pe)
{
programmer*pm=dynamic_cast(pe);
//如果pe指向programmer类型的一个对象,则dynamic_cast成功,并且pm指向programmer对象的开始
if(pm)
{
//用pm调用programmer:
:
bonus()
}
//如果pe不是指向programmmer类型的一个对象,则dynamic_cast失败,并且pm的值为0
else
{
//使用employee的成员函数
}
}
7].因为不存在空引用,所以不可能通过比较dynamic_cast的结果(dynamic_cast的结果引用)是否为0来检验dynamic_cast是否成功.如果一个引用的dynamic_cast失败则会抛出一个bad_cast类类型的异常,必须包含头文件.(注意:
VC6.0和g++中没有这个头文件)
if(programmer*pm=dynamic_cast(pe))
就不能被改写为
if(programmer&pm=dynamic_cast(pe))
实例:
#include
voidcompany:
:
payroll(employee&re)
{
try
{
programmer&rm=dynamic_cast(re);
//用rm调用programmer:
:
bonus()
}
catch(std:
:
bad_cast)
{
//使用employee的成员函数
}
}
8].对引用的dynamic_cast不可能忽略失败的转换并在没有测试其结果前使用它而指针是有可能的,但是使用异常给程序增加了相应的运行开销,
.const_cast:
将转换掉表达式的常量性(以及volatile对象的volatile性).
例如:
externchar*string_copy(char*);
constchar*pc_str;
char*pc=string_copy(const_cast(pc_str));
.static_cast与void*型指针
a).任何非const数据类型的指针都可以被赋值给void*型的指针.
b).有时void*型的指针被称为泛型generic指针因为它可以指向任意数据类型的指针.
c).const数据类型类型的指针只能赋值给constvoid*型的指针.
d).在C++中不存在从void*型指针到特殊类型的指针之间的自动转换
把void*型的指针赋值给任意显式类型时C++要求显式强制转换.(原因一)
实例:
intival=10;
void*pv;
int*pi=&ival;
constchar*pc="acastingcall";
pv=pi;//ok:
pv指向ival
//pc=pv;//错误:
没有标准的转换
//ok:
仍然是错误的,但是现在可以通过编译!
//因为在赋值前用了显式强制转换
//当程序失败时应该首先检查强制转换
pc=static_cast(pv);
char*pstr=newchar[strlen(pc)+1];
strcpy(pstr,pc);
cout<delete[]pstr;
执行显式强制转换的第二个原因是希望改变通常的标准转换(原因二)
doubled;
inti;
i+=d;//i提升成double型然后再把它加到d上,最后把结果截取成int型来执行赋值
//通过显式地将d强制转换成int型,消除了把i从int型到double型的不必要提升
i+=static_cast(d);
编译器隐式执行的任何类型转换都可以由static_cast显式完成
doubled=97.0;
charch=static_cast(d);
为什么要这样做呢?
因为从一个较大类型到一个较小类型的赋值会导致编译器产生一个警告,以提醒我们潜在的精度损失.当我们提供显式强制转换时,警告消息被关闭,强制转换告诉编译器和程序的读者我们不关心潜在的精度损失.
static_cast实例:
intmain()
{
inti=0;
//为隐式类型转换(implicittypeconversion)
i=3.541+3;
//显式类型转换(explicittypeconversion)
i=static_cast(3.541)+3;
cout<
return0;
}
.reinterpret_cast:
a).reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换。
例如,假设你有一个函数指针数组:
typedefvoid(*FuncPtr)();//FuncPtris一个指向函数的指针,该函数没有参数返回值类型为void
FuncPtrfuncPtrArray[10];//funcPtrArray是一个能容纳10个FuncPtrs指针的数组
让我们假设你希望(因为某些莫名其妙的原因)把一个指向下面函数的指针存入funcPtrArray数组:
intdoSomething();
你不能不经过类型转换而直接去做,因为doSomething函数对于funcPtrArray数组来说有一个错误的类型。
在FuncPtrArray数组里的函数返回值是void类型,而doSomething函数返回值是int类型。
funcPtrArray[0]=&doSomething;//错误!
类型不匹配
(
强转格式:
funcPtrArray[0]=(void(*)())doSomething;//
)
//reinterpret_cast可以让你迫使编译器以你的方法去看待它们:
funcPtrArray[0]=reinterpret_cast(&doSomething);//thiscompiles
转换函数指针的代码是不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正确的结果,所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危急时刻。
一把锋利的刀。
一把非常锋利的刀。
b).强转格式实例:
(不使用reinterpret_cast)
voidfunc(void*v)//1.定义一个函数
{
inti=(int)v;
}
voidmain()
{
func((void*)5);//2.调用函数
void(*pfunc)();//3.定义一个函数指针
pfunc=(void(*)())func;//4.由于func函数类型与pfunc指针格式不同,所以赋值前需要将该函数强转后,再赋值给函数指针
pfunc();
}
c).其他实例:
structData
{
shorta;
shortb;
}obj;
intmain()
{
cout<<&obj<obj.a=11;
obj.b=22;
longadd=0x00476738;
Data*p=reinterpret_cast(add);
cout<a<cout<b<return0;
}
10.字符串
说明:
1).C++对字符串长度没有限制.
2).拼接字符串:
C++允许拼接字符串常量,即将两个用引用括起的字符串合并为一个。
注意:
任何两个由空白(空格,tab键,换行符)分隔的字符串常量都将自动拼接成一个。
cout<<"AB""CD"<cout<<"AB"
"CD"<3).getline和get函数
a).都读取一行数据
b).getline()丢弃换行符;get()将换行符保留在输入序列中
c).get()函数的使用:
charch[20]="";
charc[20]="";
cin.get(ch,20);//第一次输入后,换行符将留在队列中
cin.get(c,20);//第二次调用时先看到\n,所以认为结束,因此什么都没有读取
改进:
cin.get(ch,20).get();//cin.get();读取一个字符,用来处理换行符,为下一行输入做准备
cin.get(c,20).get();
或
cin.get(ch,20);
cin.get();
cin.get(c,20).get();
d).getline():
读取字符串,并将最后一位换行符替换为'\0'保存。
e).建议使用get()读取一行字符串,可以进行错误检查,而getline更加简单一些。
通过get()读取后,可以判断停止读取的原因:
(1).已经读取了整行.
(2).由于数组已经填满
实例:
intmain()
{
charch[10]="";
charc[10]="";
cin.get(ch,10);
if(cin.get()=='\n')//如果是正常结束,则读出的将是换行符;否则则表明是数组已满而停止输入
{
cout<<"ok..."<}
cin.get(c,10);
return0;
}
11.头文件
旧头文件新头文件
string.hcstring
stdlib.hcstdlib
stdio.hcstdio
12.动态内存分配
//实例1:
int*p=NULL;
p=new(nothrow)int[10];
if(p==0)
{
cout<<"Error:
memorycouldnotbeallocated";
}
//实例2:
intmain()
{
int*p=NULL;
try
{
p=newint;//在分配失败时抛std:
:
bad_alloc
}
catch(bad_alloc&)
{
cout<<"'new'threwanexception"<cout<<"Error:
memorycouldnotbeallocated";
}
try
{
p=new(nothrow)int;//在分配失败时不抛异常,它返回NULL
}
catch(bad_alloc&)
{
cout<<"thislineshouldneverappear.";
}
return0;
}
中申明的关于抛异常的重载版本:
namespacestd
{
classbad_alloc
{
//...
};
}
//newanddelete
void*operatornew(std:
:
size_t)throw(std:
:
bad_alloc);
voidoperatordelete(void*)throw();
//arraynewanddelete
void*operatornew[](std:
:
size_t)throw(std:
:
bad_alloc);
voidoperatordelete[](void*)throw();
//newanddelete
void*operatornew(std:
:
size_t,std:
:
nothrow_tconst&)throw();
voidoperatordelete(void*,std:
:
nothrow_tconst&)throw();
//arraynewanddelete
void*operatornew[](std:
:
size_t,std