2.1 基本内置类型
2.2 字面值常量
2.3 变量
变量的类型--->决定了分配给变量的存储空间大小(含义决定大小)和可以在其上执行的操作(操作)。
初始化--->C++支持两种方式初始化(变量)的形式:
(1)复制初始化:使用等号(=);
(2)直接初始化:将初始化式放在圆括号中。
下面来区分两个概念:初始化VS赋值
(1)二者对于基本数据类型来说,差别不大
初始化:指创建变量并给它赋值;
赋值:先擦去对象的当前值,然后再复制其新的值。
(2)然后当用于类类型对象时,二者存在效率的高低。
初始化:指创建一个新的对象,并调相应的构造函数(直接初始化:直接调用与实参匹配的构造函数;复制初始化:总是调用复制构造函数。)。
赋值:指讲一个对象(假设为Object1)的值代替另一个对象(Object2)的值(Object1,Object2均已存在),并调用相应的赋值操作符函数(亦可对比为:直接赋值:直接调用与实参相匹配的赋值操作符函数;复制赋值:总是调用形参为类类型的赋值操作符函数)。
下面是它们的代码比较:以string类说明(其中m_str为类的成员变量,类型为:char* ,即字符串指针)
与实参相匹配的构造函数:
string::string(const char *str)
{
m_str=new char[strlen(str)+1];//内存分配
strcpy(m_str,str);//成员复制
}
复制构造函数:内存分配+成员复制
string::string(const string & s )
{
m_str=new char[strlen(s.m_str)+1];//内存分配
strcpy(m_str,s.m_str);//成员复制
}
总结:初始化=内存分配+成员复制
重载的赋值操作符:
string& string::string(const char* str)
{
if(!strcmp(this->m_str,str))return *this;
delete [] m_str;//内存释放
m_str=new char[strlen(str)+1];//(重新)内存分配
stycpy(m_st,str);//成员复制
return *this;
}
string& string::string(const string& s)
{
if(this==&s)return *this;//防止自身赋值
delete [] m_str;//内存释放
m_str=new char[strlen(s.m_str)+1];//(重新)内存分配
strcpy(m_str,s.m_str);//成员复制
return *this;
}
总结:赋值=内存释放+(重新)内存分配+成员复制
通过对比可以看出:初始化效率要高于赋值操作。
故,从效率上考虑:在条件允许的情况下,最好在初始化的时候赋值,而尽量避免使用等号(“=”)。
2.3.4 变量初始化规则
(1)内置变量自动初始化,取决于变量定义位置,如在函数体外定义的变量均初始化为0,其他函数内部则不进行自动初始化。
(2)类类型变量的初始化,系统默认构造函数或用户自己显示定义。
2.3.5 声明和定义
注:为了让多个文件能访问想通的变量,C++区分声明和定义
(变量)定义:为变量分配存储空间,还可为变量指定初始值。
声明:向程序表明变量的类型和名字。另外通过使用extern关键字声明变量名而不定义它,但亦可给出变量初值,即同时定义。从这个意义上将,定义也是一种声明。
2.3.6 名字的作用域
作用域:指程序的一段区域,用于区分名字的不同意义的上下文。有:全局作用域(main函数外部),局部作用域,语句作用域。
2.3.7 在变量使用处定义变量
即在对象第一次被使用的地方定义对象,可提高程序的可读性。
2.4 const 限定符
(1)定义const对象
const限定符:把一个对象转换成一个常量,由于常量在定以后即不能修改,故定义时必须初始化。
(2)const对象默认为文件的局部变量
之所以这样定义语法, 原因在于:C++允许const变量定义在头文件中。
问题1:先分析为什么允许const变量定义在头文件中:
答:一般,常量表达式是编译器在编译时就能计算出结果的表达式。所以const变量成为常量表达式后,编译器就要知其初始化时式(明显:这是全局变量所不能提供的)。根据编译器的按文件编译原则,同时为了能够让多个文件使用相同的常量值,const变量和它的初始化式必须是每个文件都可见的。所以,把一些这样的const变量定义在头文件中,此时,无论该const变量何时使用,编译器都能看见其初始化式。
问题2:const对象为什么默认为文件的局部变量
答:即,现已知C++变量可定义在头文件中,而头文件一般可被多个源文件包含,所以每个包含该头文件的源文件都会有自己的const变量,其名称和值都一样。但是由于C++中的全局变量只能定义一次,否则链接时会出现变量重定义错误,所以const对象默认为文件的局部变量。
虽然全局作用域声明的const变量是单个文件作用域,不能在其他文件中使用,但是通过在定义时前面用extern加以修饰,则可消除const文件作用域,就可以在整个程序中访问const对象。
2.5 引用
定义:是一种复合类型,即指需要其他类型定义的类型。
在引用的情况下,每一种引用类型都”关联到“一其他类型,且引用必须用与该引用同类型的对象进行初始化(初始化,指明引用关联哪个对象的唯一方法)。
(1)引用是别名。
(2)定义多个引用
(3)const引用:即指向const对象的引用。
I、非const引用:即普通引用,只能绑定到与该引用同类型的对象。
II、const引用:可以绑定到不同但相关的类型的对象或绑定到右值。
to be continued......
2.6 typedef 名字----------定义类型同义词
typedef通常用于三种目的:1.隐藏实现:隐藏特定类型 的实现,强调使用类型的目的;2.化繁为简:简化复杂的类型定义,使其更易于理解;3.明确目的:允许一种类型用于多个目的,同时使得每次使用该类型的目的明确
2.7 枚举 ---------不但定义整数常量集(第一个成员默认为0,当然亦可自定义,往后自增1),而且聚集成组。
(1) 定义和初始化枚举--------关键字:enum
(2)枚举成员是常量--------即不能改变枚举成员的值
(3) 每个enum都定义一种唯一的类型-------即只能与同类型对象“交流”
2.8 类类型
C++中通过定义类类型来自定义数据类型
(1)从操作开始设计类---------先考虑操作,再考虑数据
每个类都定义了一个接口和一个实现。通常,先定义该类的接口,即该类所提供的操作。通过这些操作,可决定该完成其功能所需要数据,以及是否需要定义一些函数来支持该类实现。接口:由使用该类的代码需要执行的操作组成; 实现:一般包括该类所需的数据。
(2)定义Sale_item类
(3)类的数据成员
定义变量与定义数据成员区别:
一般不能把类成员的初始化作为其定义的一部分。因为类不是在类里面 定义数据成员初始化数据成员,而是通过构造函数进行初始化。
(4)访问标号-------控制使用该类的代码(在类外)是否可以使用给定的成员
(5)使用关键字struct
用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。
2.9 编写自己的头文件
注:一般类定义都会放在头文件中。
由多个文件组成的程序需要一种方法连接名字的使用和声明,在C++中,这是通过头文件实现的。1.为了允许吧程序分成独立的逻辑块,C++支持分别编译;2.为了减少处理头文件的编译时间,有些C++的实现支持预编译(头文件)。
注:预编译又称预处理,做一些代码文本的替换工作。主要处理以#开头的预处理指令。
2.9.1 设计自己的头文件
头文件一般包含:类的定义、extern变量的声明以及函数的声明。
好处:1.保证所有文件使用给定实体的同一声明(即共享);2.当声明需要修改时,只需更新头文件。
注:头文件中所做的声明在逻辑上应该是适于放在一起的。编译头文件需要一定的时间,若头文件过大,程序员可能不愿承受包含该头文件所带来的编译代价------->预编译。
(1)头文件用于声明而不是定义
由于头文件包含在多个原文件中,故不应该含有变量或函数的定义。但是对于头文件不应含有定义有三个例外:1.可以定类;2.可定义值在编译时就应知的const对象;3.可定义incline函数。(这些均源于编译器需要更多的解释信息)
注:通常,头文件中应该只定义确实必要的东西。
(2)一些const对象定义在头文件中
由于const对象默认为文件的局部变量,而当我们在头文件中定义了const变量后,每个包含该头文件的源文件都有了自己的const变量,其名和值都一样。1.当该const变量是常量表达式初始化时,可保证所有的变量都有相同的值。但在实践中大部分编译器在编译时,都会用相应的常量表达式替换const变量的使用,故实践中,不会有任何存储空间用于存储常量表达式初始化的const变量。2.当const变量不是用常量表达式初始化时,那么它就不应该在头文件定而是和其他的变量一样,该const变量应在一个源文件中定义并初始化。应在头文件为它添加extern声明,以便多个文件共享。
2.9.2 预处理器的简单介绍
预处理器:用指定的头文件内容替代每个#include.
(1)头文件经常需要其他文件
(2)避免多重包含--------->预处理器变量
为了避免名字冲突,预处理器变量经常用全大写字母表示。
#define:接受一个名字,并定义该名字为预处理器变量
#ifndef:检测指定的预处理器是否未定义,并以#endif结束
(3)使用自定义的头文件