头文件
#include<iostream> //预编译处理命令,使用此命令必须加上下面的语句
using namespace std; //使用命名空间std
由于C++是从C语言发展而来的,为了与C兼容,C++保留了C语言中的一些规定。例如,在C语言中头文件用“. h”作为后缀,如stdio. h、math.h等。为了与C语言兼容,许多C++早期版本(例如VC++ 4.1以前的版本)的编译系统头文件都是“*.h”形式,如iostream.h等。但后来ANSI C++建议头文件不带后缀“h”。近年推出的C++编译系统新版本则采用了C++的新方法,头文件名不再有“h”扩展名,如采用iostream、cmath等。但为了使原来编写的C++程序能够运行,在程序中,既可以选择使用旧版本的带后缀“.h”的头文件,也可以使用新的不带后缀“.h”的头文件。(Visual C++6.0可以,有的编译器不可以)。由于在这个程序中采用了带后缀“.h”的头文件,这时就不需要用“using namespacestd;"作声明了。虽然两种头文件的说明方法同时并存,但是一定要注意,两种头文件不能混用。比如,若已经包含头文件iostream,那么就不能再包含一个math, h,而要代之以新的头文件cmath。
一、C++中的几个标准流对象
cin :标准输入流对象
cout:标准输出流对象
cerr:标准错误输出流对象
clog:标准日志输出流对象
1.输入流对象cin
cin :可以直接输入基本数据类型的数据,包括整数、实数、字符和字符串。
cin>>变量名1 [>>变量名2>>…>>变量名n]
eg: int a;//a必须是基本数据类型,不能是void类型
char s;
cin >> a >>s;
2. 输出流对象cout
cout<<表达式1[<<表达式2<<…<<表达式n];
其中"<<"称为插入运算符,它将紧跟其后的表达式的值输出到显示器当前光标位置。
说明:若接收变量类型为字符串,则以回车、Tab、空格为字符串结束标志。
cin >> s >> b >> c;
若输入流为“abcdefg1234hijk 123 234”
则s,b,c的值分别为:“abcdefg1234hijk”,123,234。
如:cout<<"\"; a="; 其输出为 "; a=
4.行结束符 endl
如:cout << "line 1;" << endl << "line 2;" << endl;
则其输出为:
line 1;
line 2;
5.输入输出格式控制设置:
系统默认十进制
表示设置转换基数的操纵符:
hex(16进制)、dec(10进制)、oct(8进制)
用控制符setw(int)进行宽度设置。
注意:要使用setw必须在程序的开头增加一行:
#include<iomanip>
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{ int a,b,c;
cout<<"input two numbers:"<<endl;
cin>>a>>b;
if (a>b) c=a;
else c=b;
cout<<"max number:"<<setw(5)<<hex<<c;
return 0;
}
6.cout、cerr与clog的区别:
cout经过缓冲后输出,默认情况下是显示器。
cerr不经过缓冲而直接输出,是标准错误,默认情况下被关联到标准输出流,但不被缓冲。
clog流也是标准错误流,作用和cerr一样,区别在于clog中的信息存放在缓冲区,缓冲区满或者遇到endl时才输出。
二、const 定义常变量
#define 只是简单的替换
在C++中使用const定义常变量。
优点:1>确保一些不变量不会被误修改,保证数据的安全性。
2>代码更容易维护。
说明:在C++中定义常量的时候不建议采用define,因为define只做简单的宏替换,并不提供类型检查。
常量的分类:
常变量、常引用、常对象、常数组、常指针
常变量的定义
1、定义基本数据类型
格式: const 类型 常量名=表达式;
如: const float pi=3.14159 ;
或:类型const 常量名=表达式;
如: int const a=100; ;
说明:
所定义的变量是常量,不可修改。
由于常量定义后不可修改,所以在定义时需要初始化
2、定义常量数组
格式: const 类型 数组名[元素个数]={初值表};
或:类型 const 数组名[元素个数]={初值表};
说明:定义常量数组后,数组元素的值不能改变。
如: const int a[10]={1.2,3,4,5,6,7,8,9,10};
int const a[10]={1,2,3,4,5,6,7,8,9,10} ;
3.常量常与指针一起使用,指向常量的指针
如:const char*name="hello;
表示定义一个指向常量”hello”*的指针变量。
即:由于name是一变量,它可以指向另一常量。
如: name ="stu”;正确,可以指向其他值
但常量的值不能改变。
错误如: name[2]='i‘;
常指针
如: char *const name=”hello;
表示指针name是一个常量,它不能指向另一个字符串。
name= ”stu”;错误
name[2]='1’; 正确
三、函数的声明
在C++中函数声明的形式:采用函数原型
如: int max(int a, int b);
int max(int , int );
四、函数的重载
依据:名字粉碎技术
在C语言函数的定义中,函数名必须为它们加以区别的,完成同一个任务的函数常常需要根据调用参数个数的不同或类型的不同而定义多个不同的函数名。
在C++中,提供了函数的重载机制,对功能相同但参数个数不同或类型不同的函数可以使用相同的函数名,由此,在调用时无需定义多个函数名,而由编译器根据参数类型选择被调函数。
C语言区分函数的不同通过函数名区分,c++通过函数原型(函数返回值+函数名称+参数列表(参数的类型和个数))区分。
仅依靠函数返回值不同不能区分不同函数,虽然生成的符号不同,但是程序调用的时候都可以,会出现调用的二义性。
仅依靠函数参数个数判断时,若函数参数给出默认值,则调用的时候可能出现二义性
函数调用约定不同。
优点:方便使用,提高程序可读性。
利用重载,无需为他们的命名不同的函数名
定义时用参数类型(或参数个数)的不同使它们加以区别。
五、函数模板
在C++中提供了函数模板,如果多个函数的函数体相同,只是形参的类型不同,则可以定义函数模板来实现函数的重载。
函数模板:实际上就是建立一个通用函数,函数类型和形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就称为函数模板。
优点:支持多种不同的形参,避免函数体的重复设计
在编译时期,利用重命名规则(不是替换),生成代码的代码
函数模板的声明形式为 :
template < typename / class 类型参数标识符 >
把max函数用函数模板来定义和使用。
template <typename T>
T max(T a, T b)
{ return a > b ? a : b; }
#include<iostream>
using namespace std;
template<typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
int main()
{
int a, b;
cin >> a >> b;
cout << max(a, b) << endl;
float c, d;
cin >> c >> d;
cout << max(c, d);
return 0;
}
typename可以用class代替,即函数模板的类型参数可以为一般数据类型,也可以是类,或者混合。
template < typename T1,typename T2 >
template < class C1 >
template < typename T1, class C1 >
函数模板适用于函数的参数个数相同而类型不同, 且函数体相同的情况。若参数个数不同,则不能用函数模板。
六、有默认参数的函数(缺省参数)
在C语言中,一般实参个数与形参个数相同,但在多次调用同一函数且使用相同的实参时,C++中函数参数允许使用缺省值,即给形参一个默认值。
当函数调用时,若给出的参数个数少于函数表中参数的总数时,则所缺参数自动取函数形参表中设置的默认值。
优点:
使函数的调用更加灵活。
带有默认值参数函数的定义
如:int max(int x, int y=10)
{ …… }
当调用该函数时:
max(15,30);—— 调用时 x=15 ,y=30
max(15) ; —— 调用时 x=15 ,y=10,
相当于 max(15,10)
带默认值参数函数的使用说明
1)定义时:函数有多个默认值参数时,从右往左写;
如:int f1(int x, int y=0, int z=0); —— 正确
int f2(int x, int y=0, int z); —— 错误
2)当函数既有声明又有定义,只需在声明中指定默认值。
3)函数调用时,只有带默认值的参数可以省略,且只能从左往右依次指定参数,不能跳跃。
七、变量的引用
1.什么是引用?
一个与指针密切相关的特殊数据类型。
引用是一个变量的别名,定义引用类型变量,实质上是给一个已定义的变量起一个别名。
引用类型变量与其相关的变量使用同一个内存空间。
定义引用类型变量的一般格式为:
类型符 & 引用名=变量名(& 为引用声明符)
如: int a=3;
int &b= a;
注意区分:引用声明运算符和取地址运算符
int *b= &a;
2.引用使用说明
1>定义引用时,必须立即对其初始化,不能先定义后赋值。
2> 引用实际上是一隐式指针,是对变量的间接引用,但不必加运算符“ * ”;
3>引用不可重新赋值,即引用不能作为另一变量的别名
4>引用不同于其它变量
不能定义引用数组
如: int a[10];
int &b[10] =a;错误
int &b= a[0];正确
不能建立指向引用的指针
如:int *&p ;—— 错误
int i; int &j = i; int *p = &j; —— 正确
int i; int &j = i ; int &k = j; —— 正确
3. 引用作为函数参数:
#include <iostream.h>
using namespace std;
void swap( int &p , int &q )
{ int t;
t=p;p=q;q=t;
}
void main()
{ int x,y;
x=20;y=30;
cout <<"x="<<x<<"y="<<y;
cout<<endl;
swap( x , y ) ;
cout <<"x="<<x<<"y="<<y;
}
通过引用传递,不用指针也能够改变实参的值。
八、内联函数
内联函数(也称内置函数或inline函数):在编译时将被调函数的代码嵌入主调函数中,这种嵌入主调函数中的函数称为内联函数.
内联函数的处理方式是在函数的调用点直接代码展开。在计算机系统下,假如频繁的调用就会造成较大的时间开销。内联函数的引入减少了函数调用过程中开栈和清栈的开销。
优点:可以节省运行时间,提高程序的执行效率。
内置函数的定义
inline 数据类型 函数名(形参说明)
{ … }
例:inline void swap ( int &a , int &b )
{ int t;
t = a;
a = b;
b = t;
}
注意:
内置函数必须在函数第一次出现时由inline指定;
内置函数代码不宜太大,原则上适宜于1~5行代码的小函数;
不能含有复杂的分支或循环等语句;
递归调用的函数一定不能定义为内置函数。
内联函数和普通函数的区别
内联函数在调用点代码直接展开,没有开栈和清栈的开销。普通函数有开栈和清栈的开销。内联函数体要求代码简单,不能包含复杂的结构控制语句。
如果内联函数函数体过于复杂,编译器将自动把内联函数当成普通函数来执行。
内联函数和static修饰的函数的区别
static修饰的函数处理机制只是将函数的符号变成局部符号,函数的处理机制和普通函数相同,都有函数的开栈和清栈开销。内联函数没有函数的开栈和清栈。
inline函数是因为代码在调用点直接展开导致函数本文件可见。而static修饰的函数是因为函数符号从全局符号变成局部符号导致函数本文件可见。
内联函数和宏的区别
inline函数的处理时机是在编译阶段处理的,有安全检查和类型检查。
而宏的处理是在预编译阶段处理的。没有任何检查机制,只是简单的文本替换。
inline 函数是一种更安全的宏。
其实inline函数也不是十全十美的。如果一个项目下存在N多个inline函数的调用点。每个调用点都要将代码直接展开。会导致目标文件变得非常庞大。
所有内联函数是以代码膨胀为代价的。典型的以空间换时间的概念来设计的。
哪什么情况下采用inline处理合适,什么情况下以普通函数形式处理合适呢?
如果函数的执行开销小于开栈清栈开销,使用inline处理。这种情况下函数体较小,处理效率高。
如果函数的执行开销大于开栈清栈开销,使用普通函数方式处理。这种情况下函数体较大,空间利用率高。特别是函数的执行开销远远大于开栈清栈开销。这种情况下函数开栈和清栈的开销就可以忽略不记。这种情况采用普通函数处理最合适。
九、作用域
作用域:指包括变量在内的标识符的有效性范围,即标识符的作用空间。
作用域运算符 ::
当全局变量与局部变量同名,而需要在函数内使用全局变量时,可以用作用域运算符::使用全局变量。
注意:不能用 :: 访问函数中的局部变量。
十、字符串变量
在C++中,可用字符串类string定义字符串变量,要使用string时必须在程序的开头增加一行:
#include<string>
1.字符串变量的定义: string 变量名
如:string str1;
2.字符串变量的初始化:
如: string str2=”Hello”;
注意:用字符串变量存放字符串常量时,只存放字符串本身而不包括“\0"。
3. 字符串变量的赋值
如:str1 = ”Yes”;str2 = str1 ;
str1[0] =y;
4. 字符串变量的输入输出
如:string str1;
cin>>str1;
cout<<str1;
5. 字符串变量的运算
字符串的复制
如: str1 = str2;
5. 字符串变量的运算
字符串的连接
如:str1 = ”Hello”;
str2 = “world” ;
str3=str1+str2;
字符串的比较
如:str1 >str2
6. 字符串数组
定义形式:string 数组名[数组长度]
如:string str1[6];
string str2[3]={“China”,”English”,”Japan”};
十一、动态分配/撤销内存运算符
在C语言中,库函数malloc:动态内存的分配
库函数free:动态内存的释放
如: int *p;
p = (int * )malloc (sizeof(int ));
free(p);
在c++中,运算符 new :动态内存的分配
运算符 delete:动态内存的释放
1.new 运算符
格式1:指针变量 = new 数据类型
功能:动态分配一块大小为“sizeof(数据类型)”字节大小的内存,new返回的是内存地址,可赋值给一个指针变量。
如:申请一个存放整数的内存空间
new int;
int *p= new int;
p= new int (8 );
格式2:指针变量 = new 数据类型[n]
功能:动态分配一块大小为“n*sizeof(数据类型)”字节大小的内存,并将其首地址赋值给指针变量。
如:申请一个字符数组
new char[20];
char * str = new char[20];
注意:分配数值空间时不能指定初值。
new申请空间不能拿if判断是否申请成功,new申请失败抛出异常,if检测不到
但是可以 ip = new(nothrow) int [n]
if(NULL==ip) exit(1)
这样就可以了
2. delete运算符
单个元素空间释放: delete 指针变量名
数组元素空间释放: delete [ ]指针变量名
功能:释放指针变量所指向的内存空间(不是将指针变量删了)。
如:int * p = new int;
delete p;
3. C++动态内存应用注意事项
为什么用new和delete而不用malloc和free?
new不但申请对象所需的内存空间,同时引发构造函数的执行。
delete不但释放申请的空间,而且会自动调用析构函数。
释放一个已释放的内存可能导致程序崩溃。
float *p=new float;
delete p;
delete p; 危险
运算符delete必须用于先前new分配的有效指针。如果使用了未定义的其它类型的指针,就会带来严重后果,如系统崩溃。