c++综述/C++在C基础上进行了类型增强以及C++的输入和输出控制
1.综述C++
1.1 作者
1982年,美国AT&T公司贝尔实验室的Bjarne Stroustrup博士在c语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与c语言的渊源关系,它被取名为C++,而Bjarne Stroustrup(本贾尼.斯特劳斯特卢普)博士被尊称为C++语言之父。
1.1.1 历史背景
C语言作为结构化和模块化的语言,在处理小规模的程序时,比较得心应手。但是当问题比较复杂,程序的规模较大的时候,需要高度的抽象和建模时,c语言显得力不从心。
1.1.2 应“运”而生?运为何?
为了解决软件危机,20世纪80年代,计算机界提出了OOP(object oriented programming)思想,这需要设计出支持面向对象的程序设计语言。Smalltalk就是当时问世的一种面向对象的语言。而在实践中,人们发现c是如此深入人心,使用如此广泛,以至于最好的方法,不是发明一种语言去取代它,而是在原有的基础上发展它。这种情况下c++应运而生,最初这门语言并不叫c++而是c with class(带类的c)。
1.1.3 C++发展大计事
- 在“C with class”阶段,研制者在C语言的基础上加进去的特征主要有:类及派生类、共有和私有成员的区分、类的构造函数和析构函数、友元、内联函数、赋值运算符的重载等。
- 1985年公布的C++语言1.0版的内容中又添加了一些重要特征:虚函数的概念、函数和运算符的重载、引用、常量(constant)等。
- 1989年推出的2.0版形成了更加完善的支持面向对象程序设计的C++语言,新增加的内容包括:类的保护成员、多重继承、对象的初始化与赋值的递归机制、抽象类、静态成员函数、const成员函数等。
- 1993年的C++标准3.0版本是C++语言的进一步完善,其中最重要的新特征是模板(template),此外解决了多重继承产生的二义性问题和相应的构造函数与析构函数的处理等。
- 1998年C++标准(ISO/IEC14882 Standard for the C++ Programming Language)得到了国际化组织(ISO)和美国标准化协会(ANSI)的批准,标准C++语言及其标准库更体现了C++语言设计的初衷。名字空间的概念、标准模板库(STL)中增加的标准容器类、通用算法类和字符串类型等使得C++语言更为实用。此后C++是具有国际标准的编程语言,该标准通常简称ANSI C++或ISO C++ 98标准,以后每5年视实际需要更新一次标准。
- 后来又在2003年通过了C++标准第二版(ISO/IEC 14882:2003):这个新版本是一次技术性修订,对第一版进行了整理——修订错误、减少多义性,但没有改变语言特性。这个版本常被称为C++03。
- 此后,新的标准草案叫做C++0x。对于C++ 0x标准草案的最终国际投票已于2011年8月10日结束,并且所有国家都投出了赞成票,C++ 0x已经毫无异义地成为正式国际标准。先前被临时命名为C++ 0x的新标准正式定名为ISO/IEC 14882:2011,简称ISO C++11标准。C++ 11标准将取代现行的C++标准C++98和C++03。国际标准化组织于2011年9月1日出版发布《ISO/IEC 14882:2011》,名称是:Information technology – Programming languages – C++ Edition:3。
1.1.4 现今地位
1.2 应用领域
1.2.1 系统层软件开发
C++的语言本身的高效和面向对象,使其成为系统层开发的不二之选。比如我们现在用的window桌面,GNOME桌面系统,KDE桌面系统。
1.2.2 服务器程序开发
面向对象,具有较强的抽象和建模能力。使其在电信、金融、电商、通信、媒体、交换路由等方面中不可或缺。
1.2.3 游戏、网络、分布式、云计算
1.2.4 基础类库/科学计算
1.3 内容
C++语言的名字,如果看作c的基本语法,是由操作数c和运算符后++构成,C++是本身这门语言先是c,然后在此基础上++,这个++包含三大部分,c++对c的基础语法的扩展,面向对象(继承,封装,多态),STL等。
1.4 书籍推荐
2.C++对C的扩展(Externsion)
曾有人戏谑的说,C++作为一种面向对象的语言,名字起的不好,为什么呢?用c的语法看,++操作符是post++。
2.1 类型增强
2.1.1 类型检查更严格
2.1.1.1 比如,把一个const类型的指针赋给非const类型的指针。c语言中可以通过,但是在c++中则编不过去。
#include <stdio.h>
int main()
{
const int a = 8;
printf("a = %d\n",a);
int *p = &a;
*p = 6;
printf("a = %d\n",a);
return 0;
}
- 在上述代码中声明了const变量a,但是在c中可以通过指针这种“明修栈道,暗渡陈仓”,去修改const变量。但是在下述c++代码中,在执行int *P = &a是会报编译错误的,由此便可看出c++比c的类型检查要更为严格。
下面为示例代码:
int main()
{
const int a = 8;
int *p = &a;
return 0;
}
2.1.1.2 const 变量初始化
- const int a 在C语言中声明的时候可以不赋值初始化,但是在C++中声明的时候必须初始化,否则编译不通过
以上两点说明了C++实现了真正的const
2.1.1.3 使用malloc不会赋值给void*之外的类型
示例代码如下:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *p = malloc(100);
return 0;
}
#include<iostream>
int main()
{
char *p = (char*)malloc(100);
return 0;
}
如上图所示因为malloc返回void指针,在C语言中void可以赋值给char*,但是在c++中不可以,会编译错误,必须通过强制转化后才可以赋值给char*
2.1.2 布尔类型(bool)
C语言的逻辑真假用0和非0来判断,而C++中有了具体的类型。
- 在C++中bool类型也可以用整数来赋值,判断true和false的逻辑和C语言相同,0的时候表示false,非0表示true
示例代码如下:
#include<iostream>
enum BOOL{
FALSE,
TRUE
};
int main()
{
BOOL b = FALSE;
if( b ) {
printf("b value is TRUE\n");
} else {
printf("b value is FALSE\n");
}
return 0;
}
#include<stdio.h>
enum BOOL{
FALSE,
TRUE
};
int main()
{
enum BOOL b = FALSE;
if(b) {
printf("b value is true\n");
}else {
printf("b value is false\n");
}
return 0;
}
从上述C和C++代码可以看出,布尔类型其实是用枚举类型来实现的
2.1.3 真正的枚举(enum)
- 1.C语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而C++中枚举变量,只能用被枚举出来的元素初始化。代码如下所示:
#include<iostream>
enum DAY{
MON,TUS,WEN
};
int main()
{
DAY day = MON;
//day = 100; 编译不通过,C++中不允许将int类型转换为枚举类型
return 0;
}
#include<stdio.h>
enum DAY{
MON,
TUE,
WEN
};
int main()
{
enum DAY day = MON;
day = 100;
return 0;
}
- 2.在C++中能用enmu实现的尽量不要用宏定义
- 3.在C++中能用const常量实现的尽量不要用宏定义
示例代码如下所示:
#include <iostream>
// #define Spr 1
// #define Sum 2
// #define Autu 3
// #define Win 4
enum {
Spr = 1, Sum,Autu,Win
};
int main()
{
printf("Spr = %d\n",Spr);
printf("Sum = %d\n",Sum);
printf("Autu = %d\n",Autu);
printf("Win = %d\n",Win);
return 0;
}
2.1.4 表达式的值可被赋值
- C语言中表达式不可以被赋值,但是C++中可以
根本原因就是C++中新增的运算符重载机制
见下代码演示:
#include<stdio.h>
int main()
{
int a, b = 6;
a = b = 66;
printf("a=%d, b=%d\n", a, b);
int c, d, e;
e = (c = 10) + (d = 30);
printf("c=%d, d=%d, e=%d\n", c, d, e);
//(a = b) = 666; 编译报错,在C编译器中表达式不能作为左值,所以不能给表达式赋值
printf("a=%d, b=%d\n", a, b);
/**
(a != b ? a : b) = 6666;
C语言编译器同样不会编译通过,还是因为C编译器中表达式不能作为左值,同样就不能给表达式赋值
**/
printf("a=%d, b=%d\n", a, b);
return 0;
}
#include<iostream>
int main()
{
int a, b = 6;
a = b = 66;
printf("a=%d, b=%d\n", a ,b);
int c, d, e;
e = (c = 10) + (d = 30);
printf("c=%d, d=%d, e=%d\n", c, d, e);
(a = b) = 666; // C++中会处理该表达式,先将b的值赋值给a,然后a可以作为一个左值,然后666赋值给左值。
printf("a=%d, b=%d\n", a, b);
(a != b ? a: b) = 6666;
printf("a=%d, b=%d\n", a, b);
return 0;
}
2.2 输入与输出(cin cout)
第一个真正意义上的C++程序,C++程序的后缀名为cpp。假设程序名叫xxx则应该写成xxx.cpp
2.2.1 cin&&cout
cin和cout是C++的标准输入流和输出流。他们在头文件iostream中定义。
流名 | 含义 | 隐含设备 |
---|---|---|
cin | 标准输入 | 键盘 |
cout | 标准输出 | 屏幕 |
cerr | 标准错误输出 | 屏幕 |
clog | cerr的缓冲输出 | 屏幕 |
- cin cout是类对象,与scanf sprintf具备相同的功能,完成输入和输出,但是scanf sprintf是函数
- cin调用方法:cin>> >>是右移操作符,这里是流输入运算符,它变为了另外一种运算符,这里是对右移操作符的重载,重载的运算符表示的含义由它所处的语境所决定,cin表示键盘,表示键盘中的数据流入
- cout调用方法:cout<< <<是左移操作符,这里是流输出运算符,它也变为了另外一种运算符,这里是对左移操作符的重载,重载运算符表示的含义由它所处的语境所决定,cout表示屏幕,表示数据流出到屏幕
- endl相当于C语言中的\n
- 安全性分析
- scanf不安全,这种用法是不安全的用户输入超出字符数组长度后,程序崩溃
- gets不安全,这种用法是不安全的用户输入超出字符数组长度后,程序崩溃
- cin不安全,这种用法是不安全的,用户输入超出字符数组长度后,程序崩溃
- fgets这种用法可以实现一定的安全,第二参数传入一个比第一参数的size大的数字,这里依旧不安全
示例代码如下:
#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
int main()
{
//char s[30]; //这种在输入中也是不安全的
string s;
cin>>s; // string类型的输入只能由cin完成
//scanf("%s",s); // 这种用法是不安全的用户输入超出字符数组长度后,程序崩溃
//cin>>s; // 这种用法是不安全的,用户输入超出字符数组长度后,程序崩溃
//fscanf(stdin, "%s", 30);
//fgets(s, 30, stdin); 这种用法可以实现一定的安全,从键盘中最多读取29个,字符串的最后一位一定是\0如果第二参数传入一个比30大的数字,这里依旧不安全
cout<<"s="<<s<<endl; // 先把“s=”流到cout中,然后再将s流入到cout中,最后再将endl流入到cout中
/**
* 与
* cout<<"s=";
* cout<<s;
* cout<<endl;
* 是等效的
* **/
cout<<s.max_size()<<endl;
return 0;
}
- C++中的三个好习惯:
- 不要用字符数组
- 不要用指针
- 不要用强转
- C++中cin处理了scanf输入时候处理空格,tab,回车的小问题
具体见下代码:
#include<iostream>
using namespace std;
int main()
{
//int a, b;
//cin>>a>>b; // 等价与 cin>>a; cin>>b;
//cout<<a<<" "<<b<<endl;
int a; char b;
//scanf("%d%c",&a,&b);
scanf("%d", &a);
getchar();
scanf("%c", &b);
printf("%d,%c\n", a, b);
//cin>>a>>b;
//printf("%d,%c\n", a, b);
return 0;
}
2.2.2 格式化
c语言中printf拥有强大的格式化控制。c++亦可实现,略复杂。
- 1.对于实型,cout默认输出六位有效数据,setprecision(2),可以设置有效位数,setprecision(n)<<setiosflag(ios::fixed)合用,可以设置小数点右边的位数。
- setw()流算子,默认靠右对齐,头文件#include < iomanip >
- setiosflags(ios::left) 设置左对齐
示例代码如下:
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
int a = 12345;
float b = 1234.567;
printf("%-8d--\n", a); // 靠右对齐,占8位
printf("%10.2f\n",b); // 保留两位小数
printf("%f\n",b); // 默认输出6位小数
//cout<<setprecision(8)<<a<<endl;
cout<<setw(8)<<a<<endl; //setw流算子,默认靠右对齐,占8位
cout<<setiosflags(ios::left)<<setw(8)<<a<<endl;
cout<<"xxxxxxxxxx"<<endl;
cout<<b<<endl; // 默认情况下,浮点数输出两位小数,6个有效位数
cout<<setiosflags(ios::right)<<setw(10)<<b<<endl; // 设置域宽为10,默认右对齐
b = 1.234567;
cout<<setw(10)<<setprecision(2)<<setiosflags(ios::fixed)<<b<<endl; // 保留两位小数
int d = 256;
printf("%x\n", d); // 输出十六进制
printf("%d\n", d); // 输出十进制
printf("%o\n", d); // 输出八进制
cout<<hex<<d<<endl; // 输出十六进制
cout<<oct<<d<<endl; // 输出八进制
cout<<dec<<d<<endl; // 输出十进制 decfault ?
cout<<setfill('x')<<setiosflags(ios::left)<<setw(8)<<a<<endl;
int hour = 0, min = 11, sec = 0; // 00:00:00
cout<<setfill('0')<<setw(2)<<hour<<":"<<setw(2)<<min<<":"<<setw(2)<<sec<<endl;
return 0;
}
- 2.输出十进制,十六进制,八进制。默认输出十进制的数据。
示例代码如下:
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
int i = 123;
cout<<i<<endl;
cout<<dec<<i<<endl; // 输出十进制
cout<<oct<<i<<endl; // 输出八进制
cout<<hex<<i<<endl; // 输出十六进制
cout<<setbase(8)<<i<<endl; // 输出八进制
return 0;
}
- 3.还可以在设置域宽的同时,设置左右对齐及填充字符。
示例代码如下:
#include<iostream>
#include<iomanip>
using namespace std;
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;
}