C++编程--语音特性(1)
一、基本类型
1.内置类型
C++基本内置类型有bool(1个字节)、char(1个字节)、wchar_t(2个字节)、short(2个字节)、int(4个字节)、long(4个字节)、float(4个字节)、double(8个字节)、long double(8个字节)。基本内置类型的存储空间依机器而定。c++标准规定了每个算术类型的最小存储空间。
2.变量名
变量名,即变量的标识符,由字母、数字和下划线组成变量名必顺以字母或下划线开头,并且区分大小写字母:C++的
的标识符都是大小写敏感的。
3.初始化
默认的全局变量有默认值,局部变量则被赋予任意值,为安全起见,无论全局或者局部变量都要初始化。
4.const
const int MaxSize = 100; //定义一个常量 MaxSize = 59;//试图修改MaxSize常量,这一句在编译的时候就要出错
const对象在文件默认为局部变量,也就是说,如果你想在其它文件里使用这个const变量,你必需在定义的时候前面加
//file1.cpp extern const int MAX_COUNT = 20; //定义和初始化并声明为extern //file2.cpp extern const int MAX_COUNT; //使用MAX_COUNT常量从file1.cpp文件中
注:非const变量默认是全局变量(在头文件中)。因此不需要在变量前面添加extern符号
5.引用
引用(reference)就是对象的另一个名字。在实际程序中,引用主要用作函数的形式参数。引用必需用与该引用同类型的对象初始化,如:
int ival = 1024;//ok int &refval = ival;//ok int &refVal2; //错误,引用必需初始化。 Int &refVal3 = 10; //错误,初始化必需是一个对象
前面说过,引用其实就是对象的别名,因此在对一个引用操作时,实质是在对引用的那个变量进行操作。
二、变量
typedef
typedef可以用来定义类型的同义词,如:
typedef double WAGES; WAGES hourly;
枚举
默认情况下,枚举的第一个成员赋值为0,后面的每个枚举成员赋的值比前面的值大1。
enum Week{ Sun,Mon=5,Tue,Wed};
在Week枚举中,Sun默认为0,Mon = 5,Tue=6,Wed=7。
三、数组
1.数组定义和初始化
定义
int a[10]; char ch[10]; double dArray[3];
任何数组,不论是静态声明的还是动态创建的,其所有元素在内存中都是连续字节存放的,也就是说保存在一大块连续的内存区中。
初始化
int a[4]={8,9,5,3}; int b[]={7,9,2};
或者
int elem[3]; for(int i=0; i<3; i++) { elem[i]=i+1; }
数组不允许直接赋值变量,因此在数组copy时要for循环数组重的每一个元素。如:
int b[]=a;//这里是不允许的
四、指针
初始化
指针进行初始化或赋值只能使用以下四种类型的值:
1)0常量表达式(请用NULL值初始化)
2)类型匹配的对象的地址
3)另一个对象之后的下一地址
4)同类型的另一个有效指针
int *p = 0; //把p指针初始化为0 int ival=2; p = &ival;//把p初始化为类型匹配的对象的地址 int* p2 = p; //把p2初始化为同类型的另一个有效指针
注意:避免使用未初始化的指针,很多运行时错误都源于使用了未初始化的指针。
void*指针
C++提供了一种特殊的指针类型void*,它可以保存任何类型对象的地址,如:
double obj = 3.24; double *pd = &obj; void *pv = &obj; pv = pd;
void*指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void*指针或从函数返回void*指针;给另一个void*指针赋值。不允许使用void*指针操纵它所指向的对象。
指针和引用的比较
使用引用(reference)和指针都可间接访问另一个值,但它们之间有两个重要区别。
1)引用总是指向某个对象:定义引用时没有初始化是错误的。
2)赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始引用一经初始化,就始终指向同一个特定对象
指针的算术运算
#include <string> #include <iostream> using namespace std; //main函数 void main() { int a[6] = {36, 15, 345, 63, 43, 344}; int *pStart = a; int *pEnd = pStart + 6;//指向该数组末端的下一单元 //计算元素的个数 int n = pEnd - pStart; //打印数组 for(int i=0; i<n; i++) { cout << *pStart++ <<" "; } cout << endl; }
指针和const限定符
1)指向const对象的指针
#include <string> #include <iostream> using namespace std; //main函数 void main() { double pi = 3.14; const double *cptr = π //*cptr = 5;//指针不能改变其对象的值,因此是错误的 }
2)const指针
#include <string> #include <iostream> using namespace std; //main函数 void main() { double val = 1.4; double *const cptrVal = &val; double val2 = 2.5; //cptrVal = &val2;//const指针不能修改 }
五、创建动态数组
在有些情况,编译无法知道数组的大小;比如:读一个文件,这个时候我们开多大的buf来装数据呢?答案是不知道的。
因此我们需要在知道一个文件的大小后,在分配一个数组来装数据,创建动态数组就能解决这种情况。
我们可以像这样去定义,如:
int *pBuf = new int[1024];
创建动态数组,不是没有代价的,你new了一个新的数组,却没有delete掉数组,那么你就有内存泄漏的风险,因此在
不需要的时候要调用delete[] 删除数组,如:
if(pBuf)
{
delete[] pBuf
pBuf = NULL;
}
六、多维数组
严格地说,C++中没有多维数组,通常所指的多维数组其实就是数组的数组。
先来看看二维数组的定义和初始化,我们来看一段代码
int a[2][3];//数组的定义
int b[2][3] = {{0,1,2},{4,5,6}}; //数组的初始化
int c[2][3] = {0,1,2,3,4,5};//数组的初始化
上面的代码演示了二维数组的定义和初始化
注意,我们在使用二维数组的时候要按照“先行后列”的方式来访问,而不是先列后行。这是因为我们假设一个内存页为4096字节,并且定义一个数组b[128][1024],其
列数为1024(即每一行的元素恰好占用一页),当你用“先行后列”方式时,外层循环走过每一行,正好走完一页,没
有发生页面调度。当循环进入下一行时操作系统从外存调入下一页,因此,我们可以知道,遍历完后次数最多为128次。但是你用“先列后行”方式来遍历的话,可能在遍历完后页面调度交数变成了1024*128。这将大大降低效率
七、new和delete表达式
注意:
1)对分配的数组进行delete[]删除,不能误用成delete删除,如果用的是delete,那么只会删除数组的第1个元素内存,
后面的内存就丢失了,容易造成内存泄漏。
2)读写已删除的对象。如果删除指针后,没有把指针置为0,那么就会造成野指针。
3)对同一个内存空间使用两个delete表达式。 当第一次删除时,对象的内存空间返还给自由存储区,当第二次删除时,此时自由存储区可能会被破坏。
八、强制类型转换
在编写程序中,难免会遇到类型转换,在C语言中用()括号来转换,C++继承了这种用法,但是它有一个明显的一个缺陷
,就是不会进行类型检测,不能保证满足转换。在C++里还提供了四种类型转换:static_cast、const_cast、
dynamic_cast以及reinterpret_cast。我们应该避免使用强制类型转换,但如果一定要用的话,推荐使用C++里的那四
种类型转换,因为它们会执行类型检测。
static_cast
可以使用static_cast显式地执行C++语言直接支持的转换。当需要将一个较大的算术赋值给较小的类型时,如:
double d = 7.8;
int n = static_cast<int>(d);
const_cast
const_cast最为直接,是将转换掉表达式的const性质。似乎从理论上讲,一个变量是const,那么就应该一直是const。
然而实际上,有时候你会发现有这种情况,比如我们在使用第三方的库时候,示例:
extern void fun(char* str); //第三方的函数
void f(const char *str)
{
fun(const_cast<char*>(str));
}
dynamic_cast和reinterpret_cast
dynamic为继承层次结构内的类型转换提供运行时检测
reinterpret_cast通常为操作数的位模式提供较低层次的重新解释。reinterpret_cast本质上依赖于机器。为了安全地使
用reinterpret_cast,要求程序员完全理解所涉及的数据类型,以及编译器实现强制类型转换的细节
reinterpret_cast用于指针和引用转换,不可以对对象本身。