目录
1.1 数据的输入与输出
1.1.1 cin和析取运算符>>
C++常用cin输入数据。
cin>>x>>y;
程序执行到cin时,等待键盘输入,>>后面只能出现变量名。
1.1.2 cout和插入运算符
cout<<x<<y<<end1;
C++一般用cout输出数据。
依次将x,y的值输出到显示器,end1相当于换行"\n",将光标移动到下一行开头处。
1.2 指针
指针用于存放一个对象在内存中的地址,通过指针能够间接操作这个对象,指针的典型应是建立链接的数据结构,如树tree和链表list,并管理在程序运行中动态分配的对象。或者用域函数参数,以便传递数组或者大型类的对象。
对于类型T,T*是T的指针,即一个类型为T*的变量能够保存类型T的对象的地址。
int i=10;
int *p;
p = &i;
可以理解为指针代表了两个变量,一个是指针变量本身,另一个是它所指向的变量。对于int *p;
可以理解为它定义了两个变量p和*p,p是一个地址变量,只能存放整形变量在内存中的地址。
*p是一个整形变量,真能存放一个整数。
指针有两个属性,地址和长度,地址大小固定,与类型无关。长度则与指针的类型相关,这个类型指示编译器怎样解释它所指定内存区域的内容,以及该内存内容应该跨越多少个内存单元。鉴于此,C++提供了、无类型指针void*,任何类型的指针都是大小相同的地址,而void*指针仅表示与之相关的值是个地址。因此能够接收任何数据类型(除函数指针外)的指针。
例如:
void* pv;
如果想访问*p所指向内存的内容,则需要进行强制类型转换。
count<<*(double*)pv;
void*最重要的作用是作为函数的参数,以便向函数传递一个类型可变的对象,另一种用途就是从函数返回一个五类型对象。
1.3 new和delete
指针经常与堆(heap)空间分配有关,所谓的堆就是一块内存区域,它允许程序在运行时以指针的方式从其中申请一定数量的存储单元(其他存储空间的分配是在编译时完成的),用域程序数据的处理,堆内存也称为动态内存。在使用堆内存时,可以使用malloc()从堆中分配指定大小的存储区域,用完之后需要用free()将之归还系统,如果不归还容易造成内存泄漏,也就是自己不用了,其他程序也无法使用,因此malloc()和free()是成对出现的。
例如:
上述两个函数在如下头文件中定义,
#include<stdlib.h>
main()
{
int *p;
//从堆分配一个int对象需要的内存,并将之转换为int类型。
p = (int*)malloc(sizeof(int));
*p = 23;
free(p);//释放堆内存
}
但malloc()的使用比较麻烦,除了要计算需求内存的大小,还必须对获得的内存区域进行类型转换才能使用。
因此C++提供了new和delete两个运算符进行堆内存的分配与释放。
new 的用法:
new用于从堆内存中分配指定大小的内存区域,并返回获得内存区域的首地址。
用法1:p=new type;
用法2:p=new type(x)
用法3:p=new type[n]
其中p是指针变量,type是数据类型。用法1只分配内存,用法2将分配到的内存初始化为x,用法3分配具有n个元素的数组,new能够根据type自动计算分配内存的大小,不需要sizeof()进行计算。如果分配成功将会得到的内存首地址存放在指针变量p中,如果分配不成功 ,则返回空指针(0)。
delete 的用法:
p = new int[10];
用法1:delete p;
用法2:delete[]p;
其中p是用new分配的堆空间的指针变量。用法1是用于释放动态分配的单个指针变量。用法2用于释放动态分配的数组存储区域。
其中 delete p 和 delete[]p的区别是,前者只释放了第一个数组元素,即p[0],而没有释放其他数组元素p[1]~p[9]会造成内存泄漏;后者则
将p所指向的数组区域全部归还系统。
使用new和delete的好处:
new能够自动计算要分配内存的大小,不需要用sizeof计算所要分配内存的字节数,减小出错率;
new不需要进行类型转换,能够自动返回正确指针类型;
new可以对分配的内存进行初始化;
new和delete可以被重载。
1.4 引用
引用是某个对象(即变量)的别名,即某个对象的替代名称。是C++引用的新概念,引用由符号&引导定义,形式入下:
类型 &引用名 = 变量名;
例如:
int i = 9;
int &ir = i;
int *p = &ir;
p实际指向的是i,因为ir的别名是i,所以&ir将获得i的内存地址。
其中ir为i的别名,相当于i还有一个名字叫ir,对ir的操作就是对i的操作。
使用引用时应该注意:引用符&在类型和引用名之间的位置是灵活的;在变量声明时出现的&才是引用运算符(包括函数声明和函数返回类型声明),其他地方出现&都是地址操作符;引用必须定义在初始化,不能在定义完成后再给它赋值,为引用提供的初值,可以是一个变量,可以是另一个引用名,同一个变量可以定义多个引用。
使用引用时应当注意,不能建立引用的引用,不能建立引用数组,不能建立数组的引用,可以建立指针的引用,但不能创建指向引用的指针。
1.5 const常量
变量实质上是在程序运行过程中其值可以改变的内存单元的名字,常量就是在程序执行过程中其值固定不变的内存单元的名字。const用于修饰定义的常量。
const 常量类型 常量名=常量值
例如:
const i = 10;
const int i = 10;
上述两者等价C++把没有定义指定类型的常量定义默认为int类型。
const char c = "A"; //字符常量
const char s[] = "C++ CONST" //数组常量
注意:常量必须定义时初始化,且常量一旦定义就不能修改。
const int n;//错误,常量n未进行初始化。
const int n = 10;
i = 10;//错误,修改常量
i++;//错误,修改常量
在C++中,表达式可以出现在常量定义语句中。
int j,k = 9;
const il = 10+k+6;
在C++中,可以采用#define定义常量,但它是采用宏代换的方式进行常量处理,不具有类型检查机制,存在不安全性。
1.5.1 const与指针
const与指针结合有如下三种情况
type *const p // p【const】---*p【 】
type const *p
const type *p // p【 】---*p【const】
const type const *p // p【const】---*p【const】
type代表C++中的任意数据类型,p是指针变量。
1.5.2 const与引用
在定义引用时可以用const进行限制,使其成为不允许被修改的常量引用。
int i =9;
int &rr = i;
const int &ir = i;
rr = 8;
ir =7;//错误,ir是const的引用,不允许通过它修改对应的常量i。
1.6 类型转换
类型转换是将一种数据类型转换为另一种数据类型,在同一个算术表达式中,若出现了两种以上的不同类型的数据类型,就会先进行数据类型转换,再计算表达式的值。
C++中类型转换常用在算数表达式计算、函数的参数传递、函数返回值及赋值语句中。
C++有两种类型转换方式,隐式类型转换,显示类型转换。
1.7 函数
函数原型就是常说的函数声明,只有一条语句,由函数返回类型、函数名和形式参数表三部分构成,参数表中包含的所有参数类型和参数名,参数之间用逗号隔开;形式如下:
rtype f_name(typle p1,typl2 p2,....);
rtype是函数返回类型,f_name是函数名,type1是参数1的类型,p1、p2是形式参数。其中函数原型中的形式参数名是可以省略的。
如果函数原型中没有指出函数的返回类型,C++将默认函数的返回类型是int类型;如果一个函数中没有返回类型,则必须指明它的返回类型void。在函数体中不需要return语句。
1.8 函数重载
函数重载就是允许在同一个程序中定义多个同名的函数,这些函数可以有不同的返回类型,参数类型,参数个数,以及不同的函数功能。
1.9 内联函数
在函数声明或定义时,将inline关键字加在返回类型前面的函数就是内联函数,每次调用函数inline时都会插入它的代码,故会使代码增加,占用存储空间。
注意:内联函数的声明和定义必须要在函数调用之前完成;一般只有几行的程序(最好1~5行)经常被调用的简单函数才用作内联函数。
以下函数不能作为内联函数:递归函数、函数体内含有循环,switch,goto语句之类的复杂结构函数,或具有较多程序代码的大函数。
1.10 typedef
typedef 可以给已有类型定义一个容易阅读的描述性名称,提高程序代码的可读性。但要注意,typedef并没有建立新的数据类型,只是为已有的类型引入一个助记符。
typedef type newname;
其中type是已存在的数据类型,newname是为type指定的新类型名,新类型名newname并未取代原来的类型type,即type和newname在程序中都是可用的。
typedef float house_price;
house_price x,y;
float a,b;
1.11 命名空间
命名空间的引入是为了防止大型软件中函数和变量名产生重名冲突。因此命名空间有以下特点:在一个命名空间里,可以定义许多不同的对象,并将这些对象的有效范围局限在命名空间内。但在不同的命名空间中,可以定义同名的对象,只要两个同名对象不在同一命名空间中,就不会引起冲突。
命名空间的定义:关键字为 namespace;
语法如下:
namespace namespace_name{
members;
}
【例如】
namespace ABC{
int count;
typedef float house_price;
struct student{
char *name;
int age;
};
double add(int a,int b){
return (double)a+b;
inline int min(int a,int b);
}
int ABC::min(int a,int, b){
return a>b? a:b;
};
}
这个命名空间有5个成员,count,house_price,student,add(),min()。有变量,结构,类型以及函数的声明或定义。
函数的定义有两种方法:
1.在命名空间中定义,如函数的add(),也可以在命名空间以外进行定义,如min()。当在命名空间以外进行定义时,要用"命名空间::"作为函数的前缀,表示该函数是属于某个命名空间的成员,它的有效范围仅在此命名空间内。
命名空间的应用:
【例如】
void main()
{
ABC::count = 1;
int count =9;//main函数中的count,与ABC中的count无关。
ABC::student s; //用ABC中的student定义s。
s.age = 9;
int x = ABC::min(4,5); //调用ABC中的min()计算两个数的最小值。
}
另外,标准C++提供了using命令,它可以将命名空间中的成员引入当前程序,简化了命名空间的使用。使用方式有如下两种:
1.引用空间名中的单个成员
using namespace_name::identifier
【例如】
void main()
{
using ABC::count;
count = 2;
//int count= 9;
count += 2;
}
第三行是错误的,因为main中已经有从ABC命名空间引入的count了,如果不使用using的话,第二和四行会写成:
ABC::count = 2;
ABC::count += 2;
2.引用命名空间的全部成员
using namespace_name
【例如】
using namespace ABC;
void main(){
int count = 9
student s;
count 5;
s.age = min(4,6)
}
在main函数中直接使用命名空间中所有成员。
1.12 std命名空间
标准C++将新格式头文件的内容全部放在了std命名空间中,如果程序要引用标准C++新格式头文件的函数,就要在程序中使用下面的语句std命名空间中的名称引用到全局命名空间中。
using namespace std;
【例如】
#include <string>
string s1 = "ddd",s2; //string是字符串,定义域<string>
s2 = si; //字符串赋值不需要strcpy。
#include <cmath>
int s=sin(30); //sin函数在cmath库中;
#include <cstdio>
int i
scanf("%d",&i);
printf("i=%d\n",i)
#include <iostream>
cin>>i;
cout<<s2<<endl;
如果只是用到std命名空间的个别标识符,可以在要使用的标识符前面加上前缀srd::,不用“using namespace std”,将std的全部名称引用到程序中来。
【例如】
std::cout <<"not use using std"<<std::endl;
std::cin<<xx;
1.13 预处理器
C++预处理器提供了一些预处理命令,这些命令在正式编译之前执行,所有的预处理命令都以“#”开头,独占一行,语句结束时不需要分号。
1.#define 和 undef
#define 常用于定义一个标识符常量或者带参数的宏。
【例如】
#define pi 3.1415926
#define MAX(a,b) ((a)>(b)?(a):(b))
在对程序进行预编译时,C++会将#define定义的标识符常量的值替代常量标识符,用宏代替宏名。
#undef
用于删除由#define定义的宏,使之不再起作用。
#undef MAX
此命令之后MAX不再有意义。
2.条件编译
条件编译指示编译器只对满足条件的语句或者语句块进行编译,使同一程序再不同的编译条件下能够得到不同的目标代码。常用的条件编译有以下两种形式:
a)
#ifdef 标识符
语句组1
[#else
语句组2]//[]内是可选项。
#endif
b)
#ifndef 标识符
语句组1
[#else
语句组2]//[]内是可选项。
#endif
1.14 变量初始化
变量初始化的基本原则:
如果定义变量时提供了初始值表达式,系统就用这个表达式的值作为变量的初值;
如果定义变量时没有为它提供初值,则全局数据区中的变量将被系统自动初始化为0,栈和堆中的变量不被初始化。
int n; //初始化为0。
1.15 局部变量声明
在C语言中,局部变量应当在函数可执行语句之前;
在C++中,变量可以在任何语句中定义。例如:
【例1-1】C++允许在for循环中定义变量
#include<iostream.h>
void main(){
int n=1;
for(int i = 1; i <= 10; i++)
{
int n = 1;
n = n*i;
k = i;
}
cout<<n<<i<<end1;
}
变量在包含它最近的{}内有效,成为块作用域。
故n在main()函数内有效,i和k只在for循环中有效。