目录
一、C++发展的历史
80年代 贝尔实验室 本贾尼
83年 正式命名为C++
87年 GNU制定了C++标准
92年 微软和IBM分别指定了C++标准
98年 ansi ISO制定了C++标准(C++编译器默认C++98)
03年 ISO C++03
11年 ISO C++0x
无论C++是否出现新标准,其实对程序员的影响不大,因为C++编译器默认C++98。
使用各标准的方法:
gcc ***.c -std = c99(这是C语言使用c99标准的方法,同样C++也是用这个语句)
二、 C和C++的关系
C++包含整个了C,C是建立C++的基础;
C++对类型检查更加严格;
C++扩展了C:
C++提供了面向对象的编程机制(以类的方式组织代码)
运算符重载(一种函数的特殊表现形式)
异常处理机制(新的错误处理方式)
泛型 和 模板(STL) (类型通用编程)
三、第一个C++程序
3.1 头文件
#include <iostream>//相当于C中的stdio.h
using namespace std;
int main(){
cout << "hallo c++!" << endl;//endl相当于C中的换行符; << 输出的意思
cout << "请输入年龄" << endl;
int age = 0;
cin >> age;// >> 输入的意思
cout << "你输入的年龄是" << age << endl;
}
选编译器:
方法一:
gcc "文件名" -lstdc++
(-lstdc++的意思是添加一个标准C++库)
方法二:
g++ "文件名"
标准C++头文件不再以.h结尾
标准c++头文件在usr/include/c++/4.6/
如:#include <iostream>
c++可以使用c语言的头文件
使用标准c头文件,建议去尾加头的方式去使用
如:#include <stdio.h> ---> #include <cstdio>
#include <string.h> ---> #include <cstring>
使用非标准c头文件
如Unix的:#include <pthread.h>
3.2源文件
建议使用.cpp结尾
但也可以是.c .C .cc .c++ .cxx
3.3输入输出
使用#include <iostream>
cout <<
cin >>
特点是自动处理格式
如C语言中输入:
scanf("%d",&age);
而c++输入:
cin >> age;
3.4编译器
建议使用g++
如果使用gcc,则需要加一个链接库 -lstdc++
g++的编译选项和gcc相同
-c 编译
-o 输出名
-On 优化(n是数字)
-S 生成汇编
-E 预处理
-I 指定头文件的位置
-L 指定库的位置
-l 指定库名
-std 指定编译标准
-g 生成调试信息
3.5 using namespace std;
使用标准的命名(名字)空间 std
四、命名空间
4.1命名空间就是把一组逻辑上相关的数据组织到一起的逻辑名。
作用是:
便于模块化
防止命名冲突
4.2语法
namespace 空间名{
//数据
int age;
void doo(){
}
}//注:无分号
4.3如何使用命名空间
方法一:在数据前加命名空间名::即可
方法一程序举例:
#include <iostream>
using namespace std;
namespace IBM{
int age = 99;
double salary = 8866;
void show(){
cout << "IBM age is" << age << endl;
}
}
namespace tarena{
int age = 14;
void show(){
cout << "tarena age is" << age << endl;
}
}
int main(){
IBM::show();//调用IBM中的show()
//改变IBM的age的值,并调用IBM中的show()
IBM::age = 88;
IBM::show();
tarena::age++;
tarena::show();
}
你会发现,方法一每次都得使用IBM::,太长了,很麻烦。我们来看方法二
方法二:使用using声明
using空间名::数据名;
方法二程序举例:
#include <iostream>
using namespace std;
namespace IBM{
int age = 99;
double salary = 8866;
void show(){
cout << "IBM age is" << age << endl;
}
}
namespace tarena{
int age = 14;
void show(){
cout << "tarena age is" << age << endl;
}
}
using IBM::age;//作用是把IBM::age简化为了age
using IBM::show;//作用是把IBM::show简化为了show
int main(){
age = 52;//代表的是IBM::age = 52;
show();//代表的是IBM::show();
tarena::show();/*注意,因为tarena和IBM的show()函数名一样,因此
不可以再使用方法二的using tarena::show;了。
因此,当两个空间中的两个变量名(或函数名)一样
的话(如tarena和IBM的show()函数名以及age一样)
,还是建议你使用方法一。
方法二的优点就是,把IBM::age简化为了age
*/
}
方法三:
using namespace 指令
using namespace 空间名;
程序举例:
#include <iostream>
using namespace std;
namespace IBM{
int age = 99;
double salary = 8866;
void show(){
cout << "IBM age is" << age << endl;
}
}
namespace tarena{
int age = 14;
void show(){
cout << "tarena age is" << age << endl;
}
}
using namespace IBM;/*作用是,在以下的作用域,可以直接
使用age、show()、salary
注意,这时如果你再 同时 使用using namespace tarena;
那么使用age/show时会报错,因为tarena和IBM的
age/show名一样。当然,使用salary时是没事的
*/
int main(){
age = 11;
salary = 9988;
show();
}
看到方法三,你就明白了using namespace std的作用:
即在命名空间std的作用域中,cout、cin、endl等就可以使用了。
从这里你也能看出方法三的好处,否则你在程序前面没有using namespace std
这一句话时,你每次使用cout时都得用std::cout。很麻烦
综上,三种方法各有长处,用什么方法取决于具体情况。第一种方法是最
不容易出错的。
4.4 无名命名空间
如果一个数据没有定义在任何命名空间,则这个数据属于无名命名空间
第一种无名命名空间:一个全局变量是无名命名空间。
第二种无名命名空间:
namespace {
int b = 100;
}
也是无名命名空间,只是这种空间里的变量只能在本文件使用,相
当于C语言中的static的作用。
程序举例:
#include <iostream>
using namespace std;
int a = 99;
namespace {
int b = 100;
}
int main(){
cout << a << endl;
cout << b << endl;
//上面的写法固然对,但更规范的是下面的写法
cout << ::a << endl;
cout << ::b << endl;
}
4.5 命名空间嵌套
#include <iostream>
using namespace std;
namespace ns1{
int a = 1;
namespace ns2{
int a = 2;
void show(){
cout << a << endl;
}
namespace ns3{
void show(){
cout << "this is ns3" << endl;
}
}
}
}
namespace ns4=ns1::ns2::ns3;
int main(){
ns1::ns2::show();//调用ns2下的show,输出 2
//ns1::ns2::ns3::show();显然,这样写太长了,我们用换一种简单的方式:
ns4::show();
}
4.6 命名空间重命名
4.5已经讲过了,即namespace ns4=ns1::ns2::ns3;
五、C++中的结构、联合、枚举
5.1结构体
结构体的定义和C中的完全相同
不同的是:C++中使用结构体作为类型时,可以省略关键
字struct,当然也可以不省略。
不同的是:C++中的结构体是可以定义函数的(C中只是
可以定义函数指针)。
程序举例一:
#include <iostream>
#include <cstdio>
using namespace std;
/*定义一个结构体表达日期*/
struct Data{
int day;
int month;
int year;
};//是有分号的
/*设计一个函数,可以表示日期的变量的数据*/
void showData(Data data){
//我们暂且用C中的printf函数(cout格式输出我们还没学)
printf("%4d-%02d-%02d\n",data.year,data.month,data.day);
}
int main(){
Data data = {
4,6,2012};
data.year = 2020;
showData(data);
}
但是,C++可以在结构体中定义函数,因此我们可以把上例改为下:
#include <iostream>
#include <cstdio>
using namespace std;
/*定义一个结构体表达日期*/
struct Data{
int day;
int month;
int year;
void show(){
printf("%4d-%02d-%02d\n",year,month,day);
}
};//是有分号的
/*设计一个函数,可以表示日期的变量的数据*/
void showData(Data data){
//我们暂且用C中的printf函数(cout格式输出我们还没学)
printf("%4d-%02d-%02d\n",data.year,data.month,data.day);
}
int main(){
Data data = {
4,6,2012};
data.year = 2020;
showData(data);
data.show();
}
5.2联合体
联合体定义 和 C中完全相同
C++表达这个类型时,可以省略union
c++中支持匿名联合
程序举例:
#include <iostream>
using namespace std;
int main(){
union {
/*这里故意让data与x占的字节数一样*/
char data[4];
int x;
};
/*16进制的31是十进制的49,即ASCII的数字1*/
x = 0x31323334;
for(int i = 0; i<4; i++){
cout << data[i] << " ";
}
cout << endl;
}
上例值得你注意的是,打印的结果是4321而不是1234,
这是因为在你的系统上编译时0x31323334按照从右往左
的顺序排的(即小字节序)
5.3枚举类型
枚举定义和C语言一样
C++表达枚举类型时,可以省略关键字enum
可以把枚举赋值给整数;
可以把枚举赋值给整数,但整数不可以赋值给枚举(C可以)
(这里体现了C++对类型检查的严格)
举例程序:
#include <iostream>
using namespace std;
enum Direcction{
D_UP,D_DOWN,D_LEFT,D_RIGHT};
/*注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,
第二个就为 2,以此类推。
当然你也可以在这里直接改变其值(后一元素在前一元素加一),比如:
enum Direcction{D_UP,D_DOWN = 88,D_LEFT,D_RIGHT};
则D_UP为0,D_DOWN为88,D_LEFT为89,D_RIGHT为90
*/
int main(){
Direcction dire = D_LEFT;
//dire = 2;C++中这句话是错的,不可以向枚举赋值整数。体现了C++的强类型
int x = dire;
cout << x << endl;//输出X的值的2
}
六、C++中的布尔人类型 bool
取值有true和false
C中需要导入一个头文件叫#include <stdbool.h>,而C++不用
导入这个头文件
定义一个变量,除下面的四个值之外,布尔都是真:
0 '\0' NULL false
有时候用bool类型表达整数,真就是1,假就是0
举例程序:
#include <iostream>
using namespace std;
int main(){
bool flag = true;
/*用法一:用布尔做判断*/
if(flag){
cout << "flag is true" << endl;
}
else{
cout << "flag is flase" << endl;
}
/*用法二:用布尔做整数运算*/
int x = 1;
x = x + flag;//即x = 2
cout << x << endl;
/*输出布尔值*/
cout << flag << endl;//输出1
cout << boolalpha << flag << endl;//输出true
}
七、c++中的符号替换(不用会,没用)
if(a && b){
}
if(a and b){
}
这两种写法是一样的
各种符号替换如下图所示:
之所以有符号替换这个麻烦的东西,是因为早期欧洲人键盘
没有|{等符号。
因此,不建议用c++的符号替换,很麻烦。
八、c++中的函数
8.1c++中函数的参数列表严格匹配,无参代表没有任何参数,void依然可用。
c中函数无参:int foo(void){}
c++中无参:int foo(){}
8.2c++中不再支持函数的隐式声明,调用函数必须前置声明或者定义
程序举例:
#include <iostream>
using namespace std;
void show();//c++中,被调用函数如果定义在了调用函数的后面,就得先声明此函数
int main(){
show();
//show(1);这样写是错的,因为在c++中,你的show()既然定义为无参,则不可以有参
}
void show(){
}
8.3C++函数的返回值 类型 不能省略,如:
int show(){
double a = 10;
}
中的int不能省略
九、c++中的函数重载
9.1函数重载概念
在同一作用域中 函数名相同,而参数列表不同 的函数,构成重载(overload)关系。
参数列表不同:参数的个数、类型、顺序不同
9.2函数重载程序举例:
(调用函数重载、使用函数指针)
#include <iostream>
#include <cstdio>
using namespace std;
int add(int x,int y){
cout << "add(int,int)" << endl;
return x + y;
}
double add(int x,double y){
cout << "add(int,double)" << endl;
return x + y;
}
double add(double x,double y){
cout << "add(double,double)" << endl;
return x + y;
}
int main(){
add(1,1);//打印字符字样:add(int,int)
add(1,1.5);//打印字符字样:add(int,double)
add(1.5,1.5);//打印字符字样:add(double,double
cout << add(1,1) << endl;//打印2
/*一个小任务:定义一个函数指针,调用第二个add()*/
/*方法是把该函数的声明抄写来,把函数名add改成(*padd)*/
double (*padd)(int x,double y);
padd = add;/*这里注意,虽然有三个add(),但是指针padd会
自动地选取合适的(即第二个add())*/
double t = 0;
double m = 0;
t = padd(100,12.5);
m = (*padd)(100,12.5);
printf("t = %f,i = %f\n",t,m);/*t = 112.500000,i = 112.500000
即说明,padd(100,12.5)与
(*padd)(100,12.5)是一样的效果*/
}
9.3函数重载的原理
C语言编译器生成函数调用名时,只考虑函数名;
C++编译生成调用函数名时,不但考虑函数名,而且考虑参数列表
下图,左面是C++编译的函数名,右面是C语言的:
9.4跨编译器调用
通过9.3也知道了,C++和C通过编译生成的函数名是不同的。
我们写一个例子,即.c文件的函数可以被.cpp文件调用
先写一个.c文件:
#include <stdio.h>
int getmax(int x, int y){
printf("getmax is c function\n");
return x>y?x:y;
}
再写一个.cpp文件
#include <iostream>
using namespace std;
extern "C" int getmax(int x, int y);/*加的extern "C"是为了
C++可以调用C中的getmax*/
int main(){
getmax(1, 2);/*在C++中,getmax的汇编为_Zgetmaxii,而C的
getmax会变为getmax,因此要在声明中加extern "C",
从而把C++中的_Zgetmaxii改为getmax*/
}
然后.c文件和.cpp文件都编译为.o文件,然后链接为a.out,运行a.out发现可以调用
getmax()。