对象类型决定了对该对象能构进行的操作,Python等语言在程序运行时检查数据类型,与之相反,C++是一种静态数据类型的语言,在编译时检查数据类型。所有C++编译器必须知道每一个变量对应的数据类型。
C++中最重要的语法特征就是类,class type,程序员可以自定义新类型,既可以包含数据成员,也可以包含函数成员,标准C++库提供了丰富的类和函数。
2.1 基本的内置类型
包括算术类型(arithmetic type)和空类型(void)
2.1.1 算术类型:
分为整型和浮点型,其size(number of bits)因电脑而异,其中char的大小占一个机器字节。(注意字,字节,字长的关系大多数1字节=8bit,一个word由4或8字节也就是32或者64bit).规定int至少和short一样大,一个long至少和int一样大,一个longlong至少和一个long一样大,longlong是在c++ 11中的新定义。
一般来说,float,double分别有有7和16个有效位(关于float和double看过来http://www.cnblogs.com/BradMiller/archive/2010/11/25/1887945.html, float 1bit(符号位) 8bits(指数位) 23bits(尾数位)double:1bit(符号位) 11bits(指数位) 52bits(尾数位)),char分为unsigned char /signed char和char三种,char在不同计算机上可能为有符号的也可能为无符号的所以在算数表达式中尽量不要使用char。
2.1.2 类型转换
unsigned char c=-1//char如果占8位,c为对256取模之后的余数,关于负数的取模http://www.cnblogs.com/ppboy_dxh/archive/2011/02/18/1958175.html ,这里值为255
singed char=256//超出范围,c2的值为未定义
- 如果一个算数表达式中既有有符号数又有无符号数,会将有符号数转换为无符号数。
- 无符号数不可能小于0,所以循环条件时要注意,如果有–u,得到的结果会不断循环
2.1.3 字面值常量 literal
- 整型与浮点型
以0(零)开头 的整数代表8进制,以0x或0X开头的为16进制。
十进制的字面值是带符号数,但是负号并不在字面值之内只是取负而异。
浮点型字面值为一个小数或者以科学计数法e表示,默认是double - 字符和字符串面值
单引号-》char字符字面值
双引号-》字符串字面值,为数组arry,结尾处要+1个‘\0’,而且如过两个字符串换行写,中间除了空格/缩进换行啥的,那么还是一个字符串。 - 转义序列 escape sequence
以反斜线开始 比如换行/引号啥的 - 指定字面值类型
使用前缀或者后缀可以改变默认类型,后缀不去分大小写。但是大写L比小写l比较容易与1区分.
2.2 变量 variables
2.2.1 变量的定义
int sum=0,value;
std::string book ("0-201-x");//book 通过一个string字面值初始化。string是一种库类型,表示一个可变长的字符序列。
这里的等于号表示初始化initialization,不是赋值assignment。
- 列表初始化
初始化有很多方式:
int a=0;
int a={0};//{}表示列表初始化 list initialization
int a{0};// ↑
int a(0);
列表初始化在转化信息丢失时会报错,如folate-> int,小数部分丢失则报错。
建议初始化每一个内置类型的变量,以免出错。
2.2.2 变量的声明与定义 declaration definition
extern int i;//声明 i 但是未定义
int j;//声明并定义j
extern int h=2;定义h,函数体内部初始化一个extern 标记的变量将引发错误
//声明可以多次,但是只能定义一次,如果引用其他文件中定义的变量,不能重复定义,只能声明。
2.2.3 标识符 identifier
- 标识符由字母/数字/下划线组成,必须以字母或者下划线开头。
- 一些名字如int/try等用于语言本身,不能被用作标识符。
- 自定义的标识符不能连续出现两个下划线,也不能以下划线紧连大写字母开头。
- 函数体外的标识符不能以下划线开头。
- 自定义的变量名一般用小写字母。
- 自定义的 类一般以大写字母开头
2.2.4 作用域 scope
名字的有效作用域起始与其声明的语句,以声明语句所在作用域末端为结束,作用域大部分都是以{}表明,main函数定义于任何{}之外因此拥有全局作用域global scope,在main之内的定义只能在main之内使用,有block scop。
//建议:第一次使用变量时在定义它
嵌套的作用域:
#include <iostream>
int resued=42;
int main(){
std::cout<<resued<<std::endl;//输出为42
int resued=0;
std::cout<<resued<<std::endl;//重新定义了resued 输出为0
std::cout<<::resued<<std::endl;//域作用符来更改作用域,其左侧没有指定作用域,从全局获取,此时输出为42
}
2.3 compound type复合类型
2.3.1 引用 reference
引用个人认为可以称为变量新命名:
引用本身不是一个对象
int ival=1024;
int &refval=ival;//ival的另一个名字
int &refval;//错误!,必须被初始化
int &rerefval=refval;//错误!不能定义引用的引用,也不能定义指向应用的指针。
int i=1,i2=2;
int &i3=i,&i4=i2;//正确,一个语句可以多个引用但是必须都以&开头
int &i5=10;//错误!引用初始值必须是对象
double &i6=i;//错误!引用类型必须与对象类型严格对应
2.3.2 指针 pointer
指针本身就是一个对象
指针的定义是将声明符写成*变量名的形式,可以不初始化(此时为不确定值)
获取对象的地址taking the address of an object
指针存放对象的地址,获取时使用取地址符&
int ival=42;
int *p=&ival;//p存放的是地址
int *p2=p;//指向指针地址的指针
double *p3=p2;//错误!,指针类型要和对象对应(除个别情况)
利用指针访问对象 Using a pointer to access an object
使用解引用符*
来通过地址访问对象
int ival=42;
int *p=&ival;
cout<<*p;//输出42
解引用只适用于有效指针
空指针 Null pointer
生成方法:
int *p=nullptr;
int *p=0;
int *p=NULL;
int zero=0;p=zero;/错误!!!不能将int赋值给指针,恰好为0也不行
建议初始化所有指针,如果不知道指针指向何处可以初始化为nullptr或者0
注意:
int *pi=0;//pi被初始化,但是没有指向任何对象
pi=&ival;//pi的值改变,指向ival
*pi=0;//ival
void*指针
void*可以存放任何类型对象的地址,但是不能进行访问,因为我们不知道对象的类型。
指向指针的指针 pointers to pointers
指针也是对象,也有自己的地址,指针也可以被指针。使用‘*’的个数来表示指针的等级。
//**代表指向指针的指针
//***代表指向指针的指针的指针
int ival=1024;
int *pi=&ival;//
int **pi=π//指向一个int指针
//解引用时 需要添加相同个数的解引用符*来或者原始值。
**指向指针的引用**reference to pointer
引用不能被指针,但是指针可以被引用。
#注意前方高能
int i=42;
int *p; //定义一个指针p
int *&r=p; //指向p的引用
r=&i; //r指向p,&i,相当于使p获取i的地址,指向i
*r=0; //对r解引用,得到i,赋值的结果是i被赋值为0
//r是指针的引用,相当于他是指针的另外一个名字
//从右向←读,有助于弄清楚声明语句的含义:
/////////////////*&r=p,右边的&表明r是一个应用,左边的*,表明是指向指针的引用。//////////////
2.4 const qualifier const 限定符
const的限定作用可以保证变量不能被改变,如果有试图改变的动作发生,就会报错。const必须被初始化。
如果想在多个文件中共享const对象,需要在定义变量之前使用extern 关键字。
2.4.1 const的引用
其引用也必须是const对象
const int ci=1024;
const int &i=ci;//正确
int &r2=ci;//错误,必须是const常量引用
r1=43;//错误,不能对常量引用改变赋值
const的初始化和引用
常量引用其初始化可以使用表达式/字面值/非常量对象:
int i=1;
const int &r1=1;//字面值
const int &r2=i;//非常量对象
const int &r3=r1*2;//表达式
int &r4=r1*2;//错误,普通引用不可以使用表达式
double deval=3.14;
const int &ri=dval;//正确,此时编译器是先将dval转化为const int temp=dval;然后再使用temp绑定引用。
//也就是类型可以不一样偶
2.4.2 指针和const
要想存放常量对象的地址,只能使用指向常量的指针,但是指向常量的指针可以存放一个非常量,比较绕。
const double pi=3.14;
const double *cptr=π
const 指针int *const xx=&xx;
const指针一旦初始化,它的值就不再改变,也就是他存放的地址不能改变了,但是要注意,要注意,不能改变的是地址,而非地址中的值。
#高能,从右往左理解声明的含义
int errNumb=0;
int *cons curerr=&errNumbe;//curerr将一直指向errNube,
//...其指向值可以被改变
const double pi=3.14;
const double *const pip=π//pip是一个指向const double的常量指针
//...指向值不能被改变
2.4.3 顶层const
top-level const 顶层const表示指针本身是一个const
low-level const 底层const表示其所指对象是一个const
int i=0;
int *const p1=&i;//不能改变p1的值,顶层const
const int ci=42;
const int *p2=&ci;//可以改变p2的值,也就是说 指向的对象和指针没有什么关系
const int *const p3=p2;//及时顶层也是底层
p2=p3;//p2是底层,可以拷贝操作,p3的顶层不受硬性
int *p=p3;//错误,p不是底层const,不能指向const
2.4.4 常量表达式 const expression
常量表达式是指,值不会改变并且在编译过程中就能得到结果的表达式:
const int a=20;//是
const int b=a+1;//是
int c=27;//不是
const int d=get_size();//不是,运行时才会得到结果
为了便于编译器来验证变量是否是一个常量表达式,可以使用constexpr来声明:
constexpr int mf=20;
constexpr int limit=mf+1;
constexpr int sz=size();//只有size是constexpr函数时才是一条正确的语句。
指针的常量表达式初始值只能是nullptr和0,或者储存与某个固定值地址的对象
constexpr定义的指针与其内容无关:
const int *p=nullptr;//指向常量
constexpr int *q =nullptr;//不一定指向常量,但是一个常量指针
constexpr int i=42;
constexpr const int *p=&i;//常量指针,指向常量
2.5.1 类型别名
typedef char *p;
const p a=0;//a是指向char的常量指针
const char * a=0// a 是指向const char的指针
2.5.2 auto 类型
有时变量的类型不容易得到,可以使用auto类型,通过初始值来自动推断类型,但是一个与剧中只能声明一个基本数据类型,所以auto的多个变量时其类型必须一样:
auto a=0,pi=3.14;//错误a和pi的类型不一致
复合类型/常量/auto
int i=0,&r=i;
auto a=r;//这时候会使用引用变量(i)的类型作为auto类型
#另外一般会忽略顶层const,保留底层const
const int ci =i,&cr=ci;
auto b =ci;//b是一个整数,忽略顶层 】
auto c=cr;//c是一个整数
auto d=&i;//d是一个整形指针,,,,整数的地址就是指向整数的指针
auto e=&ci;//e 是一个指向整数常量的指针,对常量对象取地址,是底层const!!!!!
//如果希望推断出顶层const,需要指出:
const auto f=ci;//ci是int,f是const int
auto &g=ci;//g是一个整形常量应用,绑定到ci
auto &h=42;//错误!,不能为非常量引用绑定字面值!!!!!!!!!!!
const auto &j=42;//正确
#==========
auto k=ci,&l=i;//k是整数,l是整形引用
auto &m=ci,*p=&ci;//m是对整形常量的引用,p是指向整形常量的指针
auto &n=i,*p2=&ci;//错误!!!!!i是int,&ci是const int
2.5.3 decltype
选择并返回类型,不用于初始化
const int ci=0,&cj=ci;
decltype(cj)z;//错误,z是一个引用必须初始化,一般来说引用都是使用其引用对象,这里是一个例外!!!
decltype(cj+1) b;//正确,这里使用的不是引用,是其对象与1相加得的结果,b是一个为初始化的int
int i=42,*p=&i;
decltype(*p) c;//错误,这里得到的类型是int&,而不是int,需要初始化
//注意decltype(())双层括号的意思是引用,加括号之后将其视为表达式
,引用必须初始化
2.6 自定义数据结构
这里定义的数据结构以struct开始,紧跟类名和类体。后面也会学习class。
struct Sales_data{//自定义类一般首字母大写
std::string bookNo;//没有初始化的成员将被默认初始化,此处为空字符串
unsigned units_sold=0;
double revenue=0.0;//类内的名字要唯一
};