接第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 了;
后序我们在学习完成类以后,重点讲解重载。