接第1章
2. C++对 C 的扩展(Externsion)
曾有人戏谑的说,C++作为一种面向对象的语言,名字起的不好,为什么呢?用 c 的
语法来看,++ 操作符是 post ++ 。
2.1. 类型增强
2.1.1. 类型检查更严格
比如,把一个 const 类型的指针赋给非 const 类型的指针。c 语言中可以通的过,但是
在 c++中则编不过去。
int main() { const int a = 100; int b = a; const int *pa = &a; int *pb = pa; return 0; }
2.1.2. 布尔类型(bool)
c语言的逻辑真假用 0 和非 0 来表示。而 c++中有了具体的类型。
int main() { bool flag = true; if(flag != false) { printf("i know bool type nown"); } printf("bool size = %dn",sizeof(bool)); return 0; }
2.1.3. 真正的枚举(enum)
c 语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而 c++中枚举变量, 只能
用被枚举出来的元素初始化。
enum season {SPR,SUM,AUT,WIN}; int main() { enum season s = SPR; s = 0; return 0; }
error
D:WorkSpaceqtcpp1main.cpp:10: error: invalid conversion from 'int' to 'season' [-fpermissive]
2.2. 输入与输出(cin cout)
第一个真正意义上的 c++程序,c++程序的后缀名为 cpp。假设程序名叫 xxx 则应该写成 xxx.cpp。
2.2.1. cin && cout
cin 和 cout 是 C++的标准输入流和输出流。他们在头文件 iostream 中定义。
int main() { char name[30]; int age; cout<<"pls input name and age:"<<endl; cin>>name; cin>>age; // cin>>name>>age; cout<<"your name is: "<<name<<endl; cout<<"your age is: "<<age<<endl; return 0; }
2.2.2. 格式化
c 语言中 printf 拥有强大的格式化控制。c++亦可以实现,略复杂。
(1)对于实型,cout 默认输出六位有效数据,setprecision(2) 可以设置有效位数, setprecision(n)<<setiosflags(ios::fixed)合用,可以设置小数点右边的位数。
#include #include using namespace std; int main() { printf("%cn%dn%fn",'a',100,120.00); printf("%5cn%5dn%6.2fn",'a',100,120.00); printf("%5cn%5dn%6.2fn",'a',100,120.00); cout<<setw(5)<<'a'<<endl<<setw(5)<<100<
(2)输出十进制,十六进制,八进制。默认输出十进制的数据
int i = 123; cout<<i<<endl; cout<<dec<<i<<endl; cout<<hex<<i<<endl; cout<<oct<<i<<endl; cout<<setbase(16)<<i<<endl;
(3)还可以设置域宽的同时,设置左右对齐及填充字符。
int main() { cout<<setw(10)<<1234<<endl; cout<<setw(10)<<setfill('0')<<1234<<endl; cout<<setw(10)<<setfill('0')<<setiosflags(ios::left)<<1234<<endl; cout<<setw(10)<<setfill('-')<<setiosflags(ios::right)<<1234<<endl; return 0; }
2.3. 函数重载(function overload)
2.3.1. 引例
如下函数分别求出整理数据和浮点型数据的绝对值:
int iabs(int a) { return a>0? a:-a; } double fabs(double a) { return a>0? a:-a; }
C++ 致力于简化编程,能过函数重名来达到简化编程的目的。
int abs(int a) { return a>0? a:-a; } double abs(double a) { return a>0? a:-a; }
2.3.2. 重载规则与调用匹配(overload&match)
重载规则:
1,函数名相同。
2,参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。 3,返回值类型不同则不可以构成重载。
如下:
void func(int a); //ok void func(char a); //ok void func(char a,int b); //ok void func(int a, char b); //ok char func(int a); //与第一个函数有冲突
有的函数虽然有返回值类型,但不参数表达式运算,而作一条单独的语句。
匹配原则:
1,严格匹配,找到则调用。
2,通过隐式转换寻求一个匹配,找到则调用。
#include using namespace std; void print(double a){ cout<<a<<endl; } void print(int a){ cout<<a<<endl; } int main(){ print(1); // print(int) print(1.1); // print(double) print('a'); // print(int) print(1.11f); // print(double) return 0; }
注:
C++ 允许,int 到 long 和 double 隐式类型转换。遇到这种情型,则会引起二义性。例:将上题上的 print(int a)中的类型 int 改为 double。
error: call of overloaded 'print(int)' is ambiguous print(1); // print(int) error: call of overloaded 'print(char)' is ambiguous print('a'); // print(int)
解决方法,在调用时强转。
2.3.3. 重载底层实现(name mangling)
C++利用 name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
实现原理:用 v c i f l d 表示 void char int float long double 及其引用。
void func(char a); // func_c(char a) void func(char a, int b, double c); //func_cid(char a, int b, double c)
C++的编译方式同 C 语言
2.3.4. extern “C”
name mangling 发生在两个阶段,.cpp 编译阶段,和.h 的声明阶段。
只有两个阶段同时进行,才能匹配调用。
mystring.h extern "C" { int myStrlen(char *str); } mystring.cpp int myStrlen(char *str) //#include "mystring.h" int myStrlen(char *str) { int len = 0; while(*str++) len++; return len; } main.cpp #include #include "mystring.h" using namespace std; int main() { char *p = "china"; int len; len = myStrlen(p); return 0; }
c++ 完全兼容 c 语言,那就面临着,完全兼容c 的类库。由.c 文件的类库文件中函数名, 并没有发生 name manling 行为,而我们在包含.c 文件所对应的.h 文件时,.h 文件要发生 name manling 行为,因而会发生在链节的时候的错误。
C++为了避免上述错误的发生,重载了关键字 extern。只需要要避免 name manling 的函数前,加 extern “C” 如有多个,则 extern “C”{}
我们看一个系统是怎么处理的:
sting.h extern "C" { char * __cdecl _strset(char *_Str,int _Val) __MINGW_ATTRIB_DEPRECATE D_SEC_WARN; char * __cdecl _strset_l(char *_Str,int _Val,_locale_t _Locale) __MI NGW_ATTRIB_DEPRECATED_SEC_WARN; char * __cdecl strcpy(char * __restrict__ _Dest,const char * __restri ct__ _Source); char * __cdecl strcat(char * __restrict__ _Dest,const char * __restri ct__ _Source); int __cdecl strcmp(const char *_Str1,const char *_Str2); size_t __cdecl strlen(const char *_Str); size_t __cdecl strnlen(const char *_Str,size_t _MaxCount); void *__cdecl memmove(void *_Dst,const void *_Src,size_t _Size) __MI NGW_ATTRIB_DEPRECATED_SEC_WARN; }
2.4. 操作符重载(operator overload)
前面用到的<<本身在 c 语言中是位操作中的左移运算符。现在又用用流插入运算符, 这种一个字符多种用处的现像叫作重载。在 c 语中本身就用重载的现像,比如 & 既表示取
地址,又表示位操作中的与。*既表示解引用,又表示乘法运算符。只不过 c 语言并没有开放重载机制。
C++提供了运算符重载机制。可以为自定义数据类型重载运算符。实现构造数据类型也可以像基本数据类型一样的运算特性。
using namespace std; struct COMP { float real; float image; }; COMP operator+(COMP one, COMP another) { one.real += another.real; one.image += another.image; return one; } int main() { COMP c1 = {1,2}; COMP c2 = {3,4}; COMP sum = operator+(c1,c2); //c1+c2; cout<<sum.real<<" "<<sum.image<<endl; return 0; }
示例中重载了一个全局的操作符+号用于实现将两个自定义结构体类型相加。本质其是是函数的调用。
当然这个 COMP operator+(COMP one, COMP another),也可以定义为 COMP add(COMP one, COMP another),但这样的话,就只能 COMP sum = add(c1,c2);
而不能实现 COMP sum = c1 +c2 了;
后序我们在学习完成类以后,重点讲解重载。