C++ 类型转换总结


C++类型转换总结 1111.…………转换成字符串 --通用方法(针对非 COM数据类型) 用sprintf 完成转换 char buffer[200]; char c = '1'; int i = 35; long j = 1000; float f = 1.7320534f; sprintf( buffer, "%c",c); sprintf( buffer, "%d",i); sprintf( buffer, "%d",j); sprintf( buffer, "%f",f); --短整型(int) itoa(i,temp,10); 按十进制转换 itoa(i,temp,2); 按二进制方式转换 --长整型(long) ltoa(l,temp,10); --浮点数(float,double) int decimal, sign; char *buffer; double source = 3.1415926535; buffer = _fcvt( source, 7, &decimal, &sign ); decimal 表示小数点的位置,sign 表示符号:0为正数,1为负数 --CString 变量 str = "2008北京奥运"; buf = (LPSTR)(LPCTSTR)str; --BSTR 变量 BSTR bstrValue = ::SysAllocString(L"程序员"); char * buf = _com_util::ConvertBSTRToString(bstrValue); SysFreeString(bstrValue); 或者 char szFileName[len]; size_t size = wcstombs(szFileName, Filename, len); --CComBSTR 变量 CComBSTR bstrVar("test"); char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str); --使用 CString 的成员函数 Format 来转换 整数(int) str.Format("%d",i); 浮点数(float) str.Format("%f",i); 2222. 字符串转换成………… char* temp strcpy(temp,"123") --短整型(int) i = atoi(temp); --长整型(long) l = atol(temp); --浮点(double) d = atof(temp); --CString 变量 CString name = temp; --BSTR 变量 BSTR bstrValue = ::SysAllocString(L"程序员"); SysFreeString(bstrValue); --CComBSTR 是ATL 对BSTR 的封装,_bstr_t 是C++对BSTR 的封装,BSTR 是 32位指针,但并不直接指向字串的缓冲区。 BSTR b=_com_util::ConvertStringToBSTR("数据"); SysFreeString(bstrValue); (使用前需要加上 comutil.h 和comsupp.lib) 反之可以使用 char *p=_com_util::ConvertBSTRToString(b); --CComBSTR 变量 CComBSTR 类型变量可以直接赋值 CComBSTR bstrVar2(temp); --_bstr_t 变量 _bstr_t 类型的变量可以直接赋值 _bstr_t bstrVar2(temp); 3333.VARIANTVARIANTVARIANTVARIANT VARIANT 的类型总结如下: Byte bVal; // VT_UI1. Short iVal; // VT_I2. long lVal; // VT_I4. float fltVal; // VT_R4. double dblVal; // VT_R8. VARIANTVARIANTVARIANTVARIANT_BOOL boolVal; //VT_BOOL. SCODE scode; //VT_ERROR. CY cyVal; //VT_CY. DATE date; //VT_DATE. BSTR bstrVal; //VT_BSTR. DECIMALFAR* pdecVal // VT_BYREF|VT_DECIMAL. IUnknown FAR* punkVal; //VT_UNKNOWN. IDispatch FAR* pdispVal; //VT_DISPATCH. SAFEARRAYFAR* parray; // VT_ARRAY|*. Byte FAR* pbVal; // VT_BYREF|VT_UI1. short FAR* piVal; // VT_BYREF|VT_I2. long FAR* plVal; // VT_BYREF|VT_I4. float FAR* pfltVal; // VT_BYREF|VT_R4. double FAR* pdblVal; // VT_BYREF|VT_R8. VARIANTVARIANTVARIANTVARIANT_BOOLFAR* pboolVal; // VT_BYREF|VT_BOOL. SCODEFAR* pscode; // VT_BYREF|VT_ERROR. CYFAR* pcyVal; // VT_BYREF|VT_CY. DATEFAR* pdate; // VT_BYREF|VT_DATE. BSTRFAR* pbstrVal; // VT_BYREF|VT_BSTR. IUnknown FAR*FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN. IDispatch FAR*FAR* ppdispVal; // VT_BYREF|VT_DISPATCH. SAFEARRAYFAR*FAR* pparray; // VT_ARRAY|*. VARIANTVARIANTVARIANTVARIANTFAR* pvarVal; // VT_BYREF|VT_VARIANTVARIANTVARIANTVARIANT. void FAR* byref; // Generic ByRef. char cVal; // VT_I1. unsigned short uiVal; // VT_UI2. unsigned long ulVal; // VT_UI4. int intVal; //VT_INT. unsigned int uintVal; //VT_UINT. char FAR* pcVal; // VT_BYREF|VT_I1. unsigned short FAR* puiVal; // VT_BYREF|VT_UI2. unsigned long FAR* pulVal; // VT_BYREF|VT_UI4. int FAR* pintVal; // VT_BYREF|VT_INT. unsigned int FAR* puintVal; //VT_BYREF|VT_UINT. _variantvariantvariantvariant_t 是VARIANT VARIANT VARIANT VARIANT 的封装类,其赋值可以使用强制类型转换,其构造函数 会自动处理这些数据类型。 C++C++C++C++垃圾回收机制垃圾回收机制垃圾回收机制垃圾回收机制 标准 C++C++C++C++没有垃圾回收机制的原因: 1)1)1)1) 没有共同基类 C++是从 C发展而成,允许直接操作指针,允许将一个类型转换为另 一个类型,对于一个指针无法知道它真正指向的类型;而 Java 或C# 都有一个共同基类 2)2)2)2) 系统开销 垃圾回收所带来的系统开销,不符合 C++高效的特性,使得不适合做 底层工作 3)3)3)3) 耗内存 C++产生的年代内存很少,垃圾回收机制需要占用更多的内存 4)4)4)4) 替代方法 C++C++有析构函数、智能指针、引用计数去管理资源的释放,对GC 的需求不迫切 封装与友员的矛盾 1)1)1)1) 访问控制符 public,public,public,public, protected,protected,protected,protected, private private private private 只在编译阶段访问控 制。 也就是说运行时刻可访问 private 成员,用函数指针。 class FFF; typedef void (FFF::*PFunc)(); PFunc pF; class FFF { public: FFF() { pF = &FFF::Print; } private: void Print() { cout<<"hello"< 变为 � C 带.h 的是非模板化的头文件,而 C++是模板化的头文件。 .h 文件是不含命名空间的,所以以前的是不需要 using 的。 当然也可以用老的 C头文件,但是,在同一个程序里不能混 着用,虽然编译可以通过,但是可能会出现问题。 3333) register register register register 变量 在C语言中,是不能对 register 变量取地址的,因为它没有虚地 址,在寄存器里。而C++中,它是可以取址的,这时,C++编译 器会忽略 register,不会把它放到寄存器里。 4444) const const const const 变量 在C语言中,const 是只读的变量,不是常量;C++中它是常量。 区别用例子描述如下: const int MAX = 10; char buffer[MAX]; //C中它是不合法的,可以用 enum 或 define 替代; //C++中它是合法的。 本质:C语言中 const 变量分配在全局静态区,默认是外部链接; C++中则在常量区,编译过程中值就定了,默认是内部链接,用 extern可改为外部链接。 const int a; //在C里是合法的,在C++里必须赋初值,因为 C++ 里是编译期间就需要它的值了,而 C里是保存在内存里而不是 符号表里。 5555) typedeftypedeftypedeftypedef 在C语言中, structA { }; A a; //这是错误的,在 C++中是合法的。 structA a; //这是正确的 用了 typedef 后,C中A a;也是合法的了。 6666) struct/union/classstruct/union/classstruct/union/classstruct/union/class � 对于 struct/union 数据成员与函数成员的访问性来说,其默 认是 public; � class 则是 private。 � union 联合体不能作为基类继承。 union A { }; class B: public A//编译出错 { }; � 匿名 union union { int a; float b; }; 在程序里可以直接访问 a、b。所以,如果匿名 union 定义 为全局的,注意使得它为内部链接的,否则很容易跟外面的 变量起冲突。 定义为 static 即可为内部链接。 关于内部链接,请参考 http://blog.csdn.net/yeming81/archive/2010/05/31/5637704.asp x 7777) 小作用域 在C语言中,如下写法是会出编译错误的: for( int i = 0; i < 10; i++) { } 8888)CCCC语言没有函数重载 关于 C++C++C++C++的函数重载,请参考 http://blog.csdn.net/yeming81/archive/2010/05/31/5637736.aspx 9999) 类型安全链接 在C语言中, a.cppa.cppa.cppa.cpp void Func1( int ){} b.cppb.cppb.cppb.cpp void Func1( char ); int main() { Func1( 1 );//C语言中是没有错误的,但 C++不行。 } 因为 C++是类型安全链接的,对于 Func1来说,它是内部链接 的,它看不见 a.cpp 中定义的函数,它调用 Func1_char 函数, 但是找不到实现。 可见:可以利用 C++编译器去编译 C语言程序,找出潜在的 错误。 10101010) C++C++C++C++有异常机制,而 CCCC没有。 关于异常,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/5673070.asphttp://blog.csdn.net/yeming81/archive/2010/06/16/5673070.asphttp://blog.csdn.net/yeming81/archive/2010/06/16/5673070.asphttp://blog.csdn.net/yeming81/archive/2010/06/16/5673070.asp xxxx 头文件 包含方式 �
在编译器设置的目录找,比如就在 VC 目录下查找。 � “header” 先从当前目录找,找不到则和
方式一样。 链接 1111) 启动模块 创建 C/C++程序时,链接器会链接启动模块,这个初始化程序会 建立堆栈,初始化变量。 2222) 库函数 尽量使用 C/C++标准库函数,实在没有,也尽量使用符合 POSIX 可移植性标准的函数。 3333) 内部链接 如果一个名称对于它的编译单元(cpp)来说是局部的,并且在链 接时不会与其它编译单元(cpp)(cpp)(cpp)(cpp)中同样的名称相冲突,那么称这 个名称有内部链接。 内部链接不可能通过 extern变量去访问位于其他编译单元定义 的变量。 (注意:有时也将声明看作是无链接的,这里我们统一看成是内 部链接的)。 � 所有的声明 声明举例请参考 http://blog.csdn.net/yeming81/archive/2010/05/31/5637695.asp x � 命名空间(包括全局空间)中的静态函数、静态友元函数、 静态变量的定义 各个命名空间中的名称不会冲突,所以是内部链接。全局的 静态函数是内部链接,因为 static 控制作用域在本文件(编译 单元)中。 � enumenumenumenum定义 � structstructstructstruct定义 � unionunionunionunion的定义 � inlineinlineinlineinline函数定义 包括全局和类里面定义的。如果有两个同名的函数,会出现 链接错误。加上 inline 就可以了,因为内部链接的缘故。 � 类的定义 也就是说在不同的文件里出现同名的类是不会出现链接错 误的,因为名字不冲突。这也是为什么 include 同一个 class 定义不会报错的原因。 � 命名空间中 const const const const 常量定义 也就是说在不同的文件里出现同名的常量是不会出现链接 错误的,因为名字不冲突。 4444) 外部链接 除了内部链接都属于外部链接,举例如下: � 类的非 inlineinlineinlineinline函数定义 如果在 A.cpp 中有类 A的定义,B.cpp 也有类 A的定义。没 问题,因为类的定义属于内部链接。但是,如果 A类有一个 非 inline函数 Func1,B 中的A 类也有一个同名的函数 Func1, 这时候就出现链接错误。 � 类的静态变量的定义 如果不同文件的同名类含有同样名字的静态变量,链接时出 错。 � 全局函数(非 staticstaticstaticstatic)和变量(非 constconstconstconst) � 命名空间 5555) 无链接 只有那些在链接或装载期间有地址的元素,才有链接。比如类声 明或局部变量就无链接。 命名空间 1111) 命名空间与 class/struct class/struct class/struct class/struct 等的区别 � namespace 是用来解决 C/C++中的名字冲突的; � 只能在全局空间定义,也就是说不能在函数里或类里定义 � 结尾不用加分号 namespaceA { } � 可以在不同文件定义同样名称的名字空间,系统会合并 � 命名空间可以有别名,用于缩短名字,方便书写 namespace bbbbbbbbbbbbbbbbb { } namespace a = bbbbbbbbbbbbbbbbb; 2222) 匿名命名空间的作用 � 每个文件((((编译单元))))可定义一个匿名空间 namespace { int a; } 在编译以后,其实编译器会加一个唯一的名字 _UNIQUE(类似 GUID)。 然后加一个指令 using namespace _UNIQUE; � 用于替代 staticstaticstaticstatic 1)1)1)1) 新C++标准提倡使用匿名命名空间来替代 static,因为 static容易使人混淆(C++和 C 对 static 的定义是不一样的) 2)2)2)2) static 不能用于修饰 class,所以你不能防止在另外一个 文件使用这个类 3333) 命名空间的使用 � 用全名访问 如A::B::a = 1; � 引入一个命名空间的所有名字 using root; � 指定使用某个名字 using root::Func; 4444) 注意事项 � 如果不注意正确使用 using,就会使得 namespace 失去了这 种名字冲突保护。using 指令只是在当前文件引入名字,但 是如果在头文件中,会被很多文件引入。 所以,如果 using using using using 会引入一个空间所有的名字,一般不放在 头文件中。 � 匿名空间的友员函数 namespaceA { Class B { friend void Func(); }; } void A::Func(){} Func Func Func Func 必须属于 namespacenamespacenamespacenamespaceAAAA,即要在里面实现。 调用 CMD 或其他程序 1)1)1)1) system(system(system(system(““““*.exe*.exe*.exe*.exe””””);););); 需头文件 字符串 1)1)1)1) 字符串拼接 cout<< “hello”“ everyone” “how are you”< stringstringstringstring; typedef basic_string wstringwstringwstringwstring; template, class allocator = allocator > class basic_string{}; traitstraitstraitstraits控制多种字符集的比较行为。 allocatorallocatorallocatorallocator控制字符串内存分配策略。 � 无需担心空间的大小,其会动态增加 � 无需担心是否以’/0’结尾 � 无需担心是否越界 � 提供一系列成员函数方法操纵字符串 � 构造函数帮助我们初始化字符串为0(C为随机值) 3)3)3)3) 构造函数////字串 � 默认构造 #include using namespacec std; string str; //长度为0, 容量 capacity为15 � 拷贝构造 string str1; string str2(str1, 开始索引,拷贝个数) string str3(str2.begin(), str2.end()) string str4(个数,‘a’);//用n个a初始化 � 子串 string str1; string str2 = str1.substr(substr(substr(substr(开始索引,个数)))); str2 = str1.substr(); //拷贝整个串 str2 = str1; 4)4)4)4) 长度////容量 � size()/length()size()/length()size()/length()size()/length()获得字符串大小 � capacity()capacity()capacity()capacity()获得目前容量,包括字符串大小在内 � reserve()reserve()reserve()reserve()表示预留多少空间,可提高效率 � resize()resize()resize()resize()默认行为可缩短字符串,或增大字符串(填充空格),用 重载版本可填充别的字符。 注意:resize 缩短字符串并不会改变原有的 capacity()大小。 5)5)5)5) 操作 � 替换 replace(replace(replace(replace(被替换字符串开始处,替换字符数,替换字符串)))); 替换不会增加字符串空间,除非替换开始处+替换字符数超出了 字符空间,那么这种情况跟插入差不多了。 � 查找 1)1)1)1) 找第一个子串 index = find(find(find(find(要查找的字符串,查找开始处)))) 找到了则 index != string:npos。 2)2)2)2) 找第一个子串 index = find_first_of(find_first_of(find_first_of(find_first_of(要查找的字符串,查询开始处)))) 找到了则 index != string:npos。 3)3)3)3) 找最后一个子串 index = find_last_of(find_last_of(find_last_of(find_last_of(要查找的字符串,查询开始处)))) 找到了则 index != string:npos。 4)4)4)4) 找第一个不在字符串里的字符的位置 index = find_first_not_of(find_first_not_of(find_first_not_of(find_first_not_of(要查找的字符串,查询开始处)))) 找到了则 index != string:npos。 比如:string str = “hello, yeqianxun!”; find_first_not_of(“hey, yeming”, 0); l不在字串里,所以返回2 5)5)5)5) 找最后一个不在字符串里的字符的位置 index = find_last_not_of(find_last_not_of(find_last_not_of(find_last_not_of(要查找的字符串,查询开始处)))) 找到了则 index != string:npos。 6)6)6)6) 反向查找 string str = “hello, body, hello, everyone!”; index = rfind(rfind(rfind(rfind(““““hellohellohellohello””””);//);//);//);//返回13131313 如果 rfind(“hello”,0),则和 find(“hello”)一样。 注意:string::npos == 0xFFFF0xFFFF0xFFFF0xFFFFFFFFFFFFFFFFFFFF � 删除 eraseeraseeraseerase();//删除所有 eraseeraseeraseerase(开始删除的位置,删除的个数 = sring::npos); //默认是 npos,所以删除所有。 � 字符定位 1)1)1)1) [index][index][index][index] char c = str[0]; 2)2)2)2) at(index)at(index)at(index)at(index) 如果越界会抛出 out_of_range 异常,比较安全。 关于异常,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/5673070.aspx � 比较 1)1)1)1) 用========比较 string str1; string str2; str1 == str2; ““““hellohellohellohello”””” ======== str1;str1;str1;str1; ////////合法 2)2)2)2) 字符串部分比较 comparecomparecomparecompare comparecomparecomparecompare(本串开始比较位置,比较字符数,要比较的字符串, 其开始比较处,比较字符数) � 其他 insertinsertinsertinsert插入子串、append append append append 附加子串到末尾、c_str c_str c_str c_str 返回以0结尾的 C型字符串。 6)6)6)6) 重载特性 � 忽略大小写的比较 1111) string 类没有包含忽略大小写的比较,因为不同语言对大小写 的定义不一样,很难统一。另外一个原因是,字符集相关的功 能由 traits 模板提供。 2222) 定义自己的 traits traits traits traits 类 struct myTraitsmyTraitsmyTraitsmyTraits : char_traitschar_traitschar_traitschar_traits { static bool eqeqeqeq(char cFirst, char cSecond) { return toupper(cFirst) == toupper(cSecond); } static bool nenenene(char cFirst, char cSecond) { return !eq(cFirst, cSecond); } static bool ltltltlt(char cFirst, char cSecond) { return toupper(cFirst) < toupper(cSecond); } static int comparecomparecomparecompare(const char* str1, const char* str2, size_t count) { //比较 str1和str2的前 count 个字符,忽略大小写 } static const char* findfindfindfind(const char* str1, size_t count, char c) { //找出 c在str1中的位置,忽略大小写 } }; 3333) 定义自己的字符串类 typedef basic_string MyStringMyStringMyStringMyString; 4444) 重载新字符串输出 ostream& operator<<(ostream& os, const MyString& str); 可变参数可变参数可变参数可变参数 1)1)1)1) 典型例子 printf(“%d, %d, %d”, a, b,c ) int Func( int a, …);//C++用法 int Func(); //C用法 2)2)2)2) 用法 � include � va_start(va_list, 前一个参数变量) #define char* va_list #define _ADDRESSOF(v) (&reinterpret_cast(v) ) 取得变量在堆栈中的地址 #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int)- 1) & ~(sizeof(int)- 1) ) 以上使得大小是4的倍数 #define va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) � va_arg(va_list, 要取的参数类型) #define _crt_va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)) -_INTSIZEOF(t)) ) � va_end(va_list) #define _crt_va_end(ap) ( ap = (va_list)0 ) 注意:1111)至少需要指定第一个参数,因为 va_start 需要它定位 堆栈中参数的地址; 2)2)2)2) 一般来说,可以考虑利用多态来替代可变参数。 3)3)3)3) 具体使用 int customarg( int a, ...) { va_list arg_ptr; va_start(arg_ptr, a); int b = va_arg(arg_ptr, int); int c = va_arg(arg_ptr, int); long d = va_arg(arg_ptr, long); char* p = va_arg(arg_ptr, char*); double e = va_arg(arg_ptr,double); cout<<"b = "<Func(1); //利用 p (this)指针找到 VPTR,然后得到真正 函数的地址。 � static 变量与对象大小 class B { public: B(){} const static long a = 10; //常量 const static long b = 10; const static long c = 10; static long d; //静态区 const long e; //只有这个变量占空间 }; sizeof (B) = 4; � 类static static static static 函数的好处 1.1.1.1. 限制全局函数在类里 2.2.2.2. static 函数不能访问成员变量,所以无需带 this 指针参数,提高效率 5555) constconstconstconst � C中,const 是只读的变量,不是常量;C++中它是常量。 区别用例子描述如下: const int MAX = 10; char buffer[MAX]; //C中它是不合法的, 可以用 enum或 define 替代; //C++ 中它是合法 的。 � C中const 变量分配在全局静态区;C++中则在常量区, 编译过程中值就定了。 � C++编译器并不为 const 变量创建存储空间,而是保存在 符号表里。如果用 extern 介入外部链接或者取 const 变量 的地址,编译器必须要分配空间。 1111) 普通 constconstconstconst 变量 const int i = 10; const int j = i+20; //常量折叠,即编译期间将 i代入 int* p = &i; //取址导致内存分配 char buffer[i]; //尽管内存分配,i还是进入了符号表 2222) constconstconstconst 数组 const int intArray [] = {1, 3, 5, 7}; char buffer[intArray[1]]; //编译错误 由于数组过于复杂,编译器不把它放入符号表,而是分 配内存区保存。编译期间不会知道具体值。 � const const const const 指针 � 指向的内容是 const 的 以下两者写法皆可: const int* a; int const* a; � 指针本身是 const 的 int* const a; � 两者都是 const的 以下两种写法皆可: int const* const a; const int* const a; � constconstconstconst 字符串陷阱 char* p = “hello”;//这时,”hello”是常量,p指针是一个 指向常量的指针 所以,严格的写法应该是 const char* p = “hello”。 如果,p[0] = ‘a’;则程序运行崩溃,因为试图去更改常 量区,行为是未定义的。 如果想要改变一个数组,需要这样定义: charcharcharchar p[]p[]p[]p[] ==== ““““hellohellohellohello””””;;;;////////注意 charcharcharchar [][][][] pppp ==== ““““hellohellohellohello””””是错误 的! “hello”将保存在全局静态区,而不是常量区,p指向首 地址。 � const const const const 参数与返回值 � 按值传递的 constconstconstconst参数 void Func( const int i ) { i = 10; //目的就是防止在程序里改变临时变量 i,对于用户而言是没//有影响的 } 所以,按值传递的 const 可以这样写,这样用户接口 更简单。 void Func( int i ) { const int& j = i; } � 按值返回的 constconstconstconst –––– 防止返回值被赋值 按值返回内置类型 int Func1() { return 29; } Func1() = 20; //编译器就阻止了这个操作,因为返回的 是一个值(字面值),而不是变量。 const int Func2() { return 29; } 所以,内置类型 const 返回值没有必要! 按值返回自定义类型(struct(struct(struct(struct,class class class class 等)))) class A { public: void modify(){ a = 10; }; private: int a; }; A Func1() { return A();} Func1() = A();//合法,产生一个临 时的非 const A对象 Func1().modify(); //合法,非const A对象 可以更改数据 const A Func2() { return A();} Func2() = A();//不合法 Func2().modify(); 不合法 void Func3( A& a) {} Func3( Func2() );//不合法,因为 不能 从const 变成一个非 const 的引用 � 类里的 constconstconstconst � const const const const 数据成员 class A { public: A(): i (10) //必须在这初始化因为这是没有调用构 造之前唯一可行的地方 { i = 10; //错误,不能二次初始化 const。 第一次也不能在这里 } private: const int i = 10; //不能在这里初始化 static const int j = 11; //这是合法的,且只能在这 里初始化,因为: //类里的 static 表示类中只有一 份,在类中任何//构造函数之前 就必须存在,这是编译期间常 //量,保存在符号表里 }; � const const const const 函数成员 类的 const对象只能调用 const 函数; 在函数的声明和定义都必须注明是 const的!!! 例子如下: int A::Func() constconstconstconst {}; 在函数内部不能改变变量值。 � const const const const 函数成员与 mutablemutablemutablemutable 如果在 const 函数里想改变某个变量,可将变量声明 为mutable, mutable int i; 这种方法是用来替换老的野蛮法: (A*)this ->i++; //将当前 const 指针转成非 const 指针, 然后更改。 注意:这时候,const 称为是按逻辑 const, 通常的 const 是按位 const。 � constconstconstconst 对象对性能的提升 一个 const 能够放进 ROM 只读存取器,系统访问 ROM 是比 RAM 快的。 能放进 ROM 的条件必须满足: � 按位 constconstconstconst 也就是类里不能存在 const 方法访问 mutable 变量,允 许有非 const 方法。 � clasclasclasclasssss或strucstrucstrucstructttt不能有用户自定义的构造函数或析构 函数,也不能包含有用户自定义的构造函数或析构函 数的对象。 � 不能有基类 因为需要初始化基类。 自动/显式类型转换 1111) 显示转换 用以下转换的好处之一是,容易找出程序中所有的转换,C风格 的转换用()去转换,很难查找。 �static_caststatic_caststatic_caststatic_cast 1)1)1)1) 用于替代 C风格的显式转换如 long b = (int) a; 各种类型指针到 void*的转换。 long b = static_cast(a); 2)2)2)2) 用于父类到子类指针的转换 3)3)3)3) 不能在类层次外转换,所以 static_cast 比C方式转 换安全。 �const_castconst_castconst_castconst_cast 从const 转为非 const,或者 volatile转为非 volatile。 通过 const 指针是不能改变数据的,有时候,如果想改, 就得转为非 const。 �reinterpret_castreinterpret_castreinterpret_castreinterpret_cast 这是最不安全的转换,但很强大,有时候甚至可以将一 段buffer 转成一个类的对象指针,或者反过来。 classA; A a; char* pBuffer = reinterpret_cast (&a); �dynamic_castdynamic_castdynamic_castdynamic_cast 1)1)1)1) 只有类中含有虚函数才能用 dynamic_castdynamic_castdynamic_castdynamic_cast,因为其 是依赖于 VTable 来工作的。 2)2)2)2) 用于多态类型的转换,比如从父类到子类的转换。 3)3)3)3) 当明确知道子类的类型时,可用 static_cast 转换, 其效率高点。 关于虚函数,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/5673139. aspx 2222) 自动转换 自动类型转换只作用于函数调用参数转换中,而不能在函数选择 时。 比如 a.Func1();a不能自动转换成别的类型 A,从而调用 A的函 数Func1。 � 构造函数 class A { public: A(int a){} } void Func(A a); Func(1); //自动转换 好处是只定义一个函数,方便;坏处是容易造 成 混淆。 如果想禁止这种转换,可将构造函数声明为 explicit,定义不用加。 explicit A(int a) ; � (((())))重载 详细请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/5673181. aspx 特殊关键字(sizeof/asm/各种逻辑操作符) 1111) sizeofsizeofsizeofsizeof int a; char buffer[100]; sizeof(a), sizeof buffer, 或者 sizeof(buffer) 2222) asmasmasmasm 用户嵌入汇编代码,改进关键性能代码,但是使得程序不具备移 植性。 3333) 逻辑运算 需要包含头文件 含义 运算符 关键字 与 && and 或 || or 非 ! not 不等于 != not eq 位与 & bitand 位或 | bitor 位与赋值 &= and_eq 位或赋值 |= or_eq 位异或 ^ xor 位异或赋值 ^= xor_eq 补 ~ compl 调试信息 1111) 方便输出变量名和值 #### #define Print(x) cout<< #x <<”=”< � jmp_buf JumpBuffer //跳转的位置结构 � setjmp(JumpBuffer) 保存当前的位置,将来跳转到这里 � longjmp(JumpBuffer, 整数) 这个整数会成为 setjmp 的返回值。 一个例子 jmp_buffer jmpbuf; //用以保存处理器相关的状态信息(栈信息等) void Func() { cout << “ get in ” < void Func() { //出错信息 //也可以在这里回收些内存,然后再次分配 } set_new_hanlder(Func); � newnewnewnew重载 1111) void*void*void*void* operatoroperatoroperatoroperator new(size_tnew(size_tnew(size_tnew(size_t size)size)size)size)throw(bad_alloc)throw(bad_alloc)throw(bad_alloc)throw(bad_alloc) {{{{ 分配内存,返回 p}p}p}p} ////////需要防止里面调用 newnewnewnew,递归调用, 要用全局的::new::new::new::new。 为什么返回 void*而不是具体的对象,因为要通用,而且目 前为止,编译器还没有为其调用构造函数,对象还没初始 化呢。 2222) void*void*void*void* operatoroperatoroperatoroperator new(size_t,new(size_t,new(size_t,new(size_t, constconstconstconstchar*char*char*char* file,file,file,file, intintintint LineNumber);LineNumber);LineNumber);LineNumber); 事实上,参数可以任意多。 调用时: char* p = new(__FILE__, __LINE__) char; 有时候可用于调试内存泄露。 为了简洁,可以用宏定义: #define new new(__FILE__, __LINE__) 3333)可选择类或全局重载,全局重载会影响其他对象的创建。 4444)当重载了 newnewnewnew时,也要注意重载 new[]new[]new[]new[],否则创建对象数 组时,调用的还是默认的 new[]new[]new[]new[]行为。 void* operator new[] (size_t size) { return ::new char[size]; 或者 return operator new(size); } 注意:当创建数组时,编译器传入的 size 比实际上的多4 个字节,这是用来存数组大小的,当释放时,系统需要知 道释放多少。 � newnewnewnew placementplacementplacementplacement 重载 1111) 可将对象构建在特定的内存地址上,例如,内存是从内 存池来的,对象要保存在它那里。 void* pAddress = GetFromPool(); A* p = new (pAddress) A(10); //默认 placement 调用,只有 一个参数。 2222) placement placement placement placement 重载 void* operator new(size_t, void* address, 其他参数) { return address; } 其实,它就是 new的多参数重载的版本。 3333) deletedeletedeletedelete � delete必须与 new配套使用。 � 如果分配用 malloc,而删除用 delete,这个结果是未定义的。 � 如果分配用 new,而删除时用 free,这个结果可能是析构函 数未被调用。 � 在用完一个内存区时,习惯性的将指针置为 NULL,这是因 为:释放 NULL 指针不会有问题,而重复释放同一内存区就可 能会导致程序崩溃。 � deletedeletedeletedelete重载 1111) voidvoidvoidvoid operatoroperatoroperatoroperator delete(void*delete(void*delete(void*delete(void* p){p){p){p){::delete::delete::delete::delete [][][][] p;}p;}p;}p;} 为什么参数是 void*呢,因为这时候已经调用了析构函数, 只能返回内存区的地址。 2222) 可选择类或全局重载,全局重载会影响其他对象的释放。 3333) 当重载了 delete delete delete delete 时,也要注意重载 delete[]delete[]delete[]delete[],否则释放对 象数组时,调用的还是默认的 delete[]delete[]delete[]delete[]行为。 void operator delete[] (void* p) { return ::delete [] p; } � deletedeletedeletedelete void*void*void*void*与内存泄露 delete 一个 void类型的指针时,其只会释放内存,不会调用析 构函数,因为:它不知道 void类型的对象的析构函数在哪里。 但是, int* p = new int[1000]; void* pv = p; delete [] pv; //没问题,反正内置类型不用调析 构函数 � 数组的释放 Object* p = new Object[100]; delete p; //所有内存释放了,但是剩下的析构函数没有被调用 正确的做法是: delete [] p; 或者,delete [100] p; //这种做法效率稍微高一点,不用取内存 大小。但是分配和释放都需指定大小,容易出错,一般不用。 4444) 综合 � 性能 不能保证每次 new/malloc的性能都是一样的,因为每次系统内 存使用状况可能不一样,导致搜索一块符合大小的内存所需的 时间也是不一样的。 程序退出 程序退出 程序退出 程序退出 exit/abortexit/abortexit/abortexit/abort 1) abort � 通过发出一个 SIGABRT信号终止程序的执行 关于信号,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/5673070.aspx � 不会清空缓冲区、调用析构函数;也没有调用用 atexit()函 数注册的清理函数 2222) exitexitexitexit � 在结束程序之前,exit()函数会调用用 atexit()注册过的所有 函数,按照 LIFO 次序调用 � 结束程序,返回一个值给操作系统,告知程序的最后状态。 在调用 exit()函数之后,控制权会移交给操作系统 � 关闭所有打开的文件 � 调用全局或静态对象的析构函数 C/C++异常处理的对比 1111)CCCC异常处理及其优缺点 � 返回值 每次调用都需要检查,导致代码膨胀,难以阅读主要逻辑。 � 全局错误状态 ––––_set_errno()_set_errno()_set_errno()_set_errno() &&&&_get_errno()_get_errno()_get_errno()_get_errno() 1)1)1)1) 当无法用返回值传输错误状态时,则用全局变量 errno。在 errno.h 可查询所有已定义的错误码。 这是线程安全的。 2)2)2)2) 在多线程下,每个线程有自己的 errno(线程局部存储 TLS)。 3)3)3)3) 什么情况无法用返回值返回错误码 比如[]重载: A& operator [] const (int i) { } 不能返回 NULL,因为是引用,也不能返回别的值代表 错误。 � 信号处理 –––– signalsignalsignalsignal &&&& raiseraiseraiseraise 1)1)1)1) 信号处理相对较为复杂,信号只有7777个,不能重定义, 传异常信息也是问题。 2)2)2)2) 使用信号,需包含 3)3)3)3) 在windows 中只有这几个信号 #define SIGINT 2 /* interrupt */ #define SIGILL 4 /* illegal instruction - invalid function image */ #define SIGFPE 8 /* floating point exception */ #define SIGSEGV 11 /* segment violation */ #define SIGTERM 15 /* Software termination signal from kill */ #define SIGBREAK 21 /* Ctrl-Break sequence */ #define SIGABRT 22 /* abnormal termination triggered by abort call */ 4)4)4)4) 用法如下: void Func(int signal) { //处理 } int main() { signal(SIGINT, Func); //逻辑处理 raise(SIGINT);//这时候 Func运行了,然而,如果还有 raise,必 须重新设置 signal } � 非局部跳转 –––– setjmpsetjmpsetjmpsetjmp &&&& longjmplongjmplongjmplongjmp 1)1)1)1) 其不会调用析构函数。 详细情况请参考 http://blog.csdn.net/yeming81/archive/2010/05/31/5637734. aspx 2222) C++C++C++C++异常处理及其优缺点 � 异常优点 1)1)1)1) 将正常逻辑与错误处理分开 在一个 try块里,对于同样的函数调用或不同函数抛出同 样异常,只需要捕获一个异常即可 2)2)2)2) 异常不能被忽略 如果你不处理异常,那么程序就会终止(取决于 terminate 的行为)。 3)3)3)3) 异常发生后,会调用析构函数 (构造函数抛出异常时, 析构函数不会调用) � 异常缺点 1)1)1)1) 性能损失 异常是会影响一点性能的,编译器会插入一些指令来处 理异常出现时所应该有的动作。但是,和异常带来的优 点相比,这点性能损失还是值得的。 2)2)2)2) 一个事实 STL 出于效率考虑,只抛出运行时刻异常,不检查逻辑 错误。 � 异常模型 1)1)1)1) 终止 遇到异常处理后,不会接着再尝试执行失败方法。目前, 大部分都是这种模型。 2)2)2)2) 恢复 和终止不同,它会再次尝试执行失败的方法。 try { while(true) { } } catch(…) { } � 异常捕获时的匹配 1)1)1)1) 数据类型不能转换 char、int、long、double、float 之间不能转换 比如,抛出一个 int 类型的异常,想捕获 long 类型的异 常是捕获不到的。 2)2)2)2) 不支持自动转换 关于自动转换,请参考 http://blog.csdn.net/yeming81/archive/2010/05/31/5637722. aspx 3)3)3)3) 子类异常可以被父类捕获匹配 所以,一般将父类捕获放到最后。 4)4)4)4) …………捕获所有异常 catch(…)一般用于释放资源,然后选择重新抛出异常, 因为无法得到异常参数。 � 重新抛出异常 try { } catch(…或者特定异常) { throw; //无须带异常对象,其会把当前异常对象传到上一 层。 } � 构造函数初始化列表异常的捕获 初始化列表比较特殊,不在函数体里,那么异常如何捕获 呢? 可用函数级的 trytrytrytry: class B: public A { public: B() trytrytrytry:A() { //初始化 } catchcatchcatchcatch(…) { } }; 函数级的 try 不一定要用在初始化列表,但是用在别处也没 啥用啊。 � 异常不能被捕获的情况 1)1)1)1) 全局、静态(全局或局部)对象的构造和析构函数抛出异 常时,是无法被捕获的(因为不知道在哪里捕获),默认将 调用 terminate 函数。 2)2)2)2) 当抛出一个异常时,是先要析构对象,才能处理异常 的。这时候,如果析构函数再抛出一个异常,异常处理 过程被中断,默认将调用 terminate 函数。 try { AAAA a;a;a;a; a.Func(); //假设 Func 会抛出异常,A的析构函数也 会抛出异常 } catch(…) { //处理 } � 重定义异常退出 1)1)1)1) 异常没有在任何一层被捕获处理时,默认将调用 terminate 函数(定义),从而 abort 函数被调用, 程序非正常退出(析构函数未被调用) 关于 abortabortabortabort,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/567305http://blog.csdn.net/yeming81/archive/2010/06/16/567305http://blog.csdn.net/yeming81/archive/2010/06/16/567305http://blog.csdn.net/yeming81/archive/2010/06/16/567305 2.aspx2.aspx2.aspx2.aspx 2)2)2)2) 重定义 terminateterminateterminateterminate void myFunc(){} void (*restore_terminate)() = set_terminate(myFunc); //保 存默认值,以便用完可以恢复 � 标准异常对象 1)1)1)1) 能够用标准异常就不要自己定义异常,其通用性、可 读性较强。特别是编写程序库时,如果抛出自定义异常, 那么将来换别的库时,客户必须要修改代码以支持新的 异常。 2)2)2)2) 所有标准异常的根在于 exceptionexceptionexceptionexception基类。 主要包含 what 方法返回错误描述。基类不包含以 string 为参数的构造函数,所以,不能 throw exception(“Error”); 一般不从这个根类直接继承,从下面的派生类继承。 exceptionexceptionexceptionexception下的直接派生类有如下: 3)3)3)3) 逻辑错误异常 logic_errorlogic_errorlogic_errorlogic_error 一般可以通过检测代码找出异常。以下派生自 logic_error: � domain_errordomain_errordomain_errordomain_error 值不属于这个领域。比如数学计算方面 acos(2.0)。 � invalid_argumentinvalid_argumentinvalid_argumentinvalid_argument 无效参数,参数不一致。 � length_errorlength_errorlength_errorlength_error 超出了值域。比如,给某个字符串附加太多的字符。 � out_of_rangeout_of_rangeout_of_rangeout_of_range 数组越界。 � ios_base::exceptionios_base::exceptionios_base::exceptionios_base::exception IO 操作异常。 4)4)4)4) 运行时异常 runtime_errorruntime_errorruntime_errorruntime_error 运行时刻才暴露出来的异常。以下派生自 runtime_error: � range_errorrange_errorrange_errorrange_error 数学计算方法。 � overflow_erroroverflow_erroroverflow_erroroverflow_error 算术上溢。 � underflow_errorunderflow_errorunderflow_errorunderflow_error 算术下溢。 以下可归为语言特性类异常: 5)5)5)5) bad_castbad_castbad_castbad_cast dymatic_cast 无法转换类型 6)6)6)6) bad_typeidbad_typeidbad_typeidbad_typeid typeid(NULL) 7)7)7)7) bad_allocbad_allocbad_allocbad_alloc 内存分配出错 8)8)8)8) bad_exceptionbad_exceptionbad_exceptionbad_exception 抛出的异常再次不在规格说明里时,详细请看下文。 � 自定义异常 1)1)1)1) 一般不从 exception直接继承 2)2)2)2) 需要实现 what 和构造函数 一个例子 class MyException: public logic_error { public: MyException(string& content): logic_error(content)logic_error(content)logic_error(content)logic_error(content) { } const char* whatwhatwhatwhat() const throw() { return content; } }; � 异常规格说明(Exception(Exception(Exception(Exception Specification)Specification)Specification)Specification) 目前 C++C++C++C++编译器还没有实现这个规格说明,如果用了,编译 器会给出警告。 1)1)1)1) void Func() throw( Exception1, Exception2) 函数只能抛出 Exception1和Exception2。 2)2)2)2) void Func() throw() 函数不能抛出任何异常 3)3)3)3) void Func() 函数可抛出任何异常 4)4)4)4) 异常规格说明是可以继承的 � 因为它作为函数声明的一部分,符合 Is-A的关系, 所以可以被继承。 � 派生类中同一函数的异常不能比基类的多,可以 少。 因为客户使用基类的接口编程,如果你抛出的异常多 的话,那么会出现非期望异常的抛出,请看下文。 � 如果基类抛出一个异常 A,那么派生类可以抛出 A 的子类,但不可以抛出 A的父类。 5)5)5)5) 非期望异常抛出 � 后果 如果抛出的异常未列在异常规格说明中,那么 unexpected()函数(定义)将被调用,它默认 调用 terminate()函数,也就是 abort()将被调用。 关于 abortabortabortabort,请参考 http://blog.csdn.net/yeming81/archive/2010/06/16/56http://blog.csdn.net/yeming81/archive/2010/06/16/56http://blog.csdn.net/yeming81/archive/2010/06/16/56http://blog.csdn.net/yeming81/archive/2010/06/16/56 73052.aspx73052.aspx73052.aspx73052.aspx � 如果 bad_exception 在异常规格说明中,并且所抛 出的异常不在规格说明中,那么默认 unexpected 函 数会将异常转换为 bad_exception,这样就可以处理 了。 � 更改 unexpectedunexpectedunexpectedunexpected默认行为 void myFunc(){} void (*restore_unexpected)() = set_unexpected(myFunc); //保存默认值,以便用完可 以恢复 � 在自定义的 unexpected 函数中,可选择重抛出异 常或抛出另一个异常 void myFunc() { throw; //重抛异常 throw anotherException(); //抛出 另 一个异常 } 1)1)1)1) 如果抛出的异常还是不符合规格说明时,当异 常规格说明包含 bad_exception,异常对象被转换 为bad_exception,然后匹配成功,异常得以处理。 2)2)2)2) 如果不包含 bad_exception,程序会调用 terminate()函数。 � 使用异常时的注意事项 1)1)1)1) 捕获异常时参数用引用 防止对象拷贝构造、子对象被切成父对象。 2)2)2)2) 析构函数不要抛出异常 3)3)3)3) 全局或静态对象的构造函数和析构函数都不要抛出异 常 4)4)4)4) 如果构造函数要抛出异常,需要在构造函数而不是析 构函数里释放资源,因为析构函数不会被调用 5)5)5)5) 当不知道可能抛出的异常时(使用模板时),不要使用规 格说明。比如 STL,因为类型是不确定的,无法知道里 面的构造、拷贝构造函数是否会抛出异常(传参的时候)。 6)6)6)6) 当确定要规格说明时,一般把 bad_exception 加在里面, 这样当抛出别的异常时也可以处理。否则,就会异常退 出程序。 多态(virtual)– 真正的面向对象编程 1111) 多态的重要性 –––– 真正的面向对象编程 C++的编程模式: � C++代替 C,面向过程的编程 � 基于对象的编程,利用了封装和继承重用代码 � 面向对象的编程,利用多态实现接口编程 2222) 多态的本质 � 早捆绑 编译时刻就在函数调用处决定了函数所在的地址。没有利用 多态的 C/C++函数调用都是早捆绑。 � 晚捆绑 编译时刻在函数调用时插入一些代码,能根据对象的类型去 调用它的函数。这是通过虚函数表 VTable 来实现的。 � 每个包含 virtual 函数的类含有一个 VTable,在这个表里放 置这个类的所有 virtual 函数的地址。每个函数地址只占2222个 字节,这一点比较特殊,也就是说,一个类中至多只能有 65536655366553665536个虚函数。 注意:每个类只有一个 VTable,供所有本类对象使用。 � 每个类对象包含一个指针 VPTR(一般为首地址 this),指 向VTable,晚捆绑时,就是通过 VPTR 找到 VTable,将查 找函数的代码插入调用处。 � 所以,虚函数会比普通函数多两到三条指令,会影响效率, 且每个对象多出4个字节指针。 一个提示:为了提高效率,寻找系统中不需定义虚函数的地 方。 � 一个 virtual 函数编译时刻的处理例子 A* p; p->Func(); 编译时,变成: push si; //寄存器保存 p mov bx, word ptr[si]; //取出 this指针放入 bx call word ptr[bx+n]; //n是0,1,2…第n个virtual 函数。 � inline 函数不能是 virtual 的,原因是没有函数地址。 3333) 多态的使用 � 在函数的声明前加上 virtualvirtualvirtualvirtual,没有必要在函数定义时加、 也没有必要在类前加、或者每一个派生类函数前加。 � 纯虚函数 1)1)1)1) virtual void Func() = 0; 如果不实现,则只是告诉编译器在 VTable 里预留一个位 置,但是不放函数地址。所以 VTable 是不完全的,不能 创建类实例 2)2)2)2) 纯虚函数也是可以实现的,但是,这时 VTable 也是不 完全的,子类必须实现它,否则 VTable 也是不完全的。 在父类实现纯虚函数的作用在于为所有子类共享代码, 仅此而已。 class A { public: virtual void FuncFuncFuncFunc() = 0; //不能在这实现,因为 inlineinlineinlineinline函数不能是 虚的。 }; void A::Func() { //逻辑 } class B: public A { public: void Func(){A::Func()A::Func()A::Func()A::Func();} //必须实现,否则 BBBB也是不能实例化的。 }; 3)3)3)3) 一般把析构函数声明为纯虚的就可实现不让创建对象 的目的。 � 抽象类 如果一个类所有函数都是纯虚函数,那么称这个类为抽象 类。 � 纯虚函数////抽象类的作用 只能通过引用或指针访问抽象类的方法,接口编程的一个体 现。 � 只能通过指针或引用实现多态 假设 B继承于 A, A a; B b; a = b; a.Func(); //这时候调用的是 A的函数,因为,a = b时,调用 的是 A的拷贝构造函数,它会初始化 VPTR 为a的地址。 � 派生类实现基类的虚函数时,必须保证参数和返回值都一 致 如果参数不一样,那么就是自定义的重载函数;如果参数和 函数名一样,返回值不一样,则编译出错。编译器必须保证 用基类的指针可以访问到子类的同一函数。 � 多态在构造函数里不起作用 1)1)1)1) 如果在构造函数里调用虚函数,调用的是本类的版本 所以,不要在构造函数里调用虚函数。 2)2)2)2) 原因 构造函数是用来构建对象的,如果调用的是后面子类的 方法,这时候它还没构建呢。 � 多态在析构函数里也不起作用 1)1)1)1) 如果在析构函数里调用虚函数,调用的是本类的版本 所以,不要在析构函数里调用虚函数。 2)2)2)2) 原因 构造函数不行是因为对象没构建完,类型信息还不可用; 而析构函数是为什么呢?是因为所要调用的对象方法可 能已经释放了(比如,调用子类的虚方法)。 � 析构函数一般都是虚的((((前提是基类有虚函数)))) 析构函数不像构造函数,这时候,VPTR 已经构建 起来了。 如果析构函数不是虚的,那么通过 delete基类指针不能析构 子类的对象。 运算符重载 1111) 运算符重载的原则 � 不要出现歧义或误解 如果别人用这个重载会误解它的本来意义,那么放弃这个重 载 � 当重载可以使得代码更易读或易写时 � 不能重载不存在的运算符,比如**(求幂),这涉及到定 义优先级 � 不能改变运算符的优先级和参数个数,防止用起来很混乱 � 优先考虑成员重载,实在不行才用非成员重载 2222) 成员重载 � 成员重载要求左参数一定是本类对象 Integer& operator + (Integer& a) { return Integer( this.value + a.value); } � 建议所有的一元运算符都是成员重载 原因是,它只有一个参数,参数肯定是类的对象。如果参数 是另外一个类的对象,那是另外一个类的重载了。 比如,+ -(正负号),++,--,~,*,&等都是可重载的一元 运算符。 � 建议所有数学及逻辑运算符都是成员重载 原因是,如果左右参数不是同一个类型的对象时,容易出现 理解的混乱,使用者难以正确的使用。 比如,+-*/,+=,-=,*=,/=,<,>,>>, <<等。 � =,=,=,=, ()()()(),[][][][],->->->->,->*->*->*->*必须是成员重载 3333) 非成员重载 当第一个参数和其他参数可以是不同类型的数据时,可以考虑非 成员重载。 比如:ostream& operator << (ostream& os, const A& a); 4444) 不能重载的运算符 � .和.* 这两个运算符对于所有类型都有意义,如果重载了它们,导 致不能正常访问成员数据 � C/C++不存在的运算符不能重载 5555) 特殊的运算符重载 � 赋值 ==== 1)1)1)1) 为了实现 a=b=c,必须返回引用; 2)2)2)2) 为了(a=b).Func1(),这个引用不能是 const 的,否则只 能调用 const 函数。 A& operator = ( constA& a) { if( this == &a) { return *this; } //拷贝 return *this; } � 自增自减 ++++++++,-------- 如果区别 a++或++a呢,用哑数据表示 Integer& operator++(){this.value++; return *this;}; //++a Integer operator++(int){Integer tmp; //++操作; return tmp;} //a++ 可以看见,a++返回的是非引用对象,原因是返回值是一个 临时对象 � 括号 (((()))) 1111) 自动类型转换 没有写返回值,但是,会返回(((())))前面定义的类型。 class B{}; class A { public: operator B() const {return B();};//B必须是存在 的类型 } void Func(B& b); A a; Func(a); //会调用 A类的()重载,转换成 B的对 象 2222) 一般(((())))重载 class A { public: bool operator ()( int a, int b ) { return true; } }; A a; bool b = a(3,9); 提示:一般用于函数对象,当做函数来调用。 一般和->*一起用。 � ->->->-> 一般用于返回一个对象指针(类里的指针成员)。它必须返回 一个对象或指针。 class Object { public: void Func(); } class A { Object* operator -> () const{return pObj;} }; A a; a->Func(); //调用 Object 类的函数 � ->*->*->*->* 这个是二元操作符!一般用于函数对象,它必须返回一个对 象。 class FunctionObject { public: Object* pObj; typedef int (*FuncPointer)(int, float); FuncPointer fPointer; int operator() (int a, float b) { return (pObj->*fPointer)(a,b); //普通函数指针函数调用 } }; class Object { public: int Add(int, float); FunctionObject operator->*(FuncPointer p) { return FunctionObject( this, p); } } Object obj; (obj->*Add)(1, 1.3f); //obj 是->*的左参数,Add 是有参数,一个函数指针 //返回值是一个 FunctionObject对象,然后调用()重载函 数。 inline 内联函数—macro 宏的替代 1111) 宏macro macro macro macro 在C++C++C++C++的特殊(唯一)用途 � 方便输出变量名和值 #### #define Print(x) cout<< #x <<”=”<> age; 如果输入 1981 02 21 则输出是 1981 0 0,因为流出错了,会忽略所有其他流操作。 � 异常处理 –––– 推荐的处理方式 在任何状态标志设置了都可以抛出异常,一般只在错误状态 设置了才抛出异常。 如,cin.exceptions(ios::badbit); cin.exceptions(ios::failbit); try { Date myday; cout<> myday; cout<,同时最好也 include,为了可移植 性,因为 fstream不一定会 include。 � ios_base ios_base ios_base ios_base 基类 :public iosiosiosios 定义了所有流共有的内容,不依赖于流所处理的字符类型。 � basic_iosbasic_iosbasic_iosbasic_ios :public ios_baseios_baseios_baseios_base 定义跟字符相关的输入输出共有的类,一般很少使用。 � basic_istreambasic_istreambasic_istreambasic_istream ::::public basic_iosbasic_iosbasic_iosbasic_ios typedef basic_istream istreamistreamistreamistream; typedef basic_istream wistreamwistreamwistreamwistream; istream cincincincin; � basic_ifstreambasic_ifstreambasic_ifstreambasic_ifstream :::: public basic_istreambasic_istreambasic_istreambasic_istream typedef basic_ifstream ifstreamifstreamifstreamifstream; typedef basic_ifstream wifstreamwifstreamwifstreamwifstream; � basic_ostreambasic_ostreambasic_ostreambasic_ostream :::: public basic_iosbasic_iosbasic_iosbasic_ios typedef basic_ostream ostreamostreamostreamostream; typedef basic_ostream wostreamwostreamwostreamwostream; ostream coutcoutcoutcout; ostream cerrcerrcerrcerr; cerrcerrcerrcerr与cout cout cout cout 的不同是 cerrcerrcerrcerr不带缓冲区,比较快。 � basic_ofstreambasic_ofstreambasic_ofstreambasic_ofstream ::::public basic_ostreambasic_ostreambasic_ostreambasic_ostream typedef basic_ofstream ofstreamofstreamofstreamofstream; typedef basic_ofstream wofstreamofstreamofstreamofstream; � basic_iostreambasic_iostreambasic_iostreambasic_iostream :::: publicpublicpublicpublicbasic_istream,basic_istream,basic_istream,basic_istream, basic_ostreambasic_ostreambasic_ostreambasic_ostream typedef basic_iostream iostreamiostreamiostreamiostream; � basic_fstreambasic_fstreambasic_fstreambasic_fstream :::: public basic_iostreambasic_iostreambasic_iostreambasic_iostream typedef basic_fstream fstreamfstreamfstreamfstream; typedef basic_fstream wfstreamwfstreamwfstreamwfstream; 2222) C++C++C++C++文件处理 � 文件打开模式 ios::inios::inios::inios::in 可用于读取文件;防止更改或截断文件; 当用 ofstream 时,以这种模式打开的文件, 可往文件中间覆盖存在的内容。 ios::outios::outios::outios::out 当用 ifstream 时,以这种模式打开也可以写入文件,参看下 文。 ios::appios::appios::appios::app 仅用于追加文件内容 以这个方式打开文件,无论怎么移动文件指针,都只能往文 件末尾追加。 ios::ateios::ateios::ateios::ate 如果文件存在,文件指针默认为文件末尾。 当用 ofstream 时,以这种模式打开和 trunc 是一样的效果, 文件内容被清空。 ios::truncios::truncios::truncios::trunc 如果文件存在,则清空文件内容 默认输出是这样方式,如果没有指定 app 或in。 ios::binaryios::binaryios::binaryios::binary 文件以二进制打开,默认为文本方式。 只在 windows 上存在的方式:文件系统用回车(13131313)/换行(10101010) 来表示行的结束符。当写入’/n’(10101010)时,系统写入回车换行符。 读取时做相反处理。 而用二进制方式读写时,系统会忽略这种转换,完全按字节 存取。 例子: ios::in | ios::out | ios::binary � 优雅的文件流缓冲 –––– 推荐的文件存取方式 结构定义 basic_streambuffer basic_filebuf: public basic_streambuffer basic_stringbuf: public basic_streambuffer 流接口 任何流对象都可以通过 rdbuf()获取流缓冲 使用例子 ifstream inFile(“a.txt”); cout< � 字符串流结构与文件流一样 basic_istringstream 替换 basic_ifstream basic_ostringstream 替换 basic_ofstream basic_stringstream 替换 basic_fstream � 输入流用法 字符串转换 istringstream strStream(“345 45 1.54 hello”); int i = 0; //345 int j = 0; //45 float k = 0; //1.54 string str; //hello strStream >> i>>j>>k>>str; 如果改成 istringstreamistringstreamistringstreamistringstream strStream(strStream(strStream(strStream(““““345s345s345s345s 45454545 1.541.541.541.54 hellohellohellohello””””);););); 输出为:345345345345 00000000 用这个可将小数点前后的变成整数。 类型转换 void DateInput(string& str) { istringstream strStream(str); Date date; strStreamstrStreamstrStreamstrStream >>>>>>>> date;date;date;date; if(if(if(if( strStreamstrStreamstrStreamstrStream ))))//bool //bool //bool //bool 返回值 { //date 正确 } else { //输出错误 } } � 输出流用法 格式化字符串 ostringstream strStream; int a = 10; float b = 1.5; string str = “yeming”; strStream << a<>>>>>>>后都得重新设置宽度,否则默认为0000。 填充字符 fill(); //返回当前填充符,默认为空格 fill(int n); //设置并返回之前的填充符 精度 precision(); //返回当前的精度,默认为小数点后6位 precision(int n); //设置并返回之前的精度 5555) 输出流格式化2222 –––– 操纵算子 用户实现跟前面一样的功能,只不过不通过显式的函数调用。 一直管用,直到有人显式的更改它。 比如,cout< showbase noshowbase showpos noshowpos uppercase nouppercase showpoint noshowpoint skipws // cin< 用法: cout<::max(); unsigned long bit = ~(MAX >> 1); while(bit) { os<<(b.n & bit ?‘1’:’0’); bit >>=1; } return os; } }; ostringstream str; str< std::basic_ostream& operator << (std::basic_ostream& os, const Data& date) { //实现 } � 区域 LocaleLocaleLocaleLocale 更换区域 默认的区域名为 C,为英美地区使用。 可以针对输入输出换区域: locale loc; cout< class thousands_sep_facet:public numpunct { public: explicit thousands_sep_facet( size_t r=0 ): numpunct(r) { } protected: string do_grouping() const { return "/003"; } }; locale loc( locale(), new thousands_sep_facet ); cout.imbue( loc ); cout<<10000000< >(loc).curr_symbol(); cout<
还剩115页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

TD_garden

贡献于2012-08-15

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf