1.复合类型
一条声明语句由一个基本数据类型(Base Type)
和紧随其后的一个声明符(Declarator)
列表组成。
每个声明符命名了一个变量并指定该变量为与基本数据类型有关的某种类型。
声明符即变量名,变量类型即声明的基本数据类型。之所以被称为复合类型,是因为其在变量名的基础上多出了一个类型修饰符(*或&)。
int i=1, *j=&i, &k=i; //注意类型修饰符
//变量类型都是int,但这里却定义了三种不同的声明符,int、指向int的指针、int类型的引用
2.引用
引用并非对象,它只是为已经存在的对象所起的另外一个名字。
因为引用不是一个对象,所以无法定义引用的引用。
我们无法将引用重新绑定到另外一个对象,因此引用必须初始化。
引用一旦完成了初始化,将会和它的初始值对象一直绑定在一起。
"引用"的使用遵循以下规则:
1.引用必须被初始化,即引用声明的同时进行初始化。
2. 引用的类型要和与之绑定的对象严格匹配。
3.引用只能绑定在对象上,而不能与字面值或者某个表达式的计算结果绑定在一起。
3.const "限定符"
const 在书中被称为“限定符”。既然是限定符,那么它的功能就是对所修饰的对象(类型)进行“限定”。
对象的类型决定了其能进行的操作。与非const类型的所能参与的操作相比,const类型对象主要的限制在于其无法执行可以改变对象内容的操作。
"const"的使用遵循以下规则:
1.初始化一个const对象并不会改变const对象本身,因此使用一个对象初始化另外一个对象,他们是不是const无关紧要。
int i=1;
const int j=i;
int k=j;
2.默认状态下,const对象仅在当前文件内有效
4.const引用
可以把引用绑定在const对象上,称之为对常量的引用。
严格来说,并不存在对常量的引用,因为引用不是对象,所以我们没法让引用本身严格不变。
"const +引用"的使用遵循以下规则:
1.初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可。
2.允许为常量引用绑定非常量的对象、字面值、乃至一个一般的表达式。
int i=1;
const int &j=i;//正确,允许将const int&绑定到一个普通的int对象上
const int &k=2;//正确,k是一个常量引用
const int &m=k*2;//同上
int &n=k*2;//错误,非const引用
区别于引用的使用规则,这里可以被这样解释:既然引用绑定的对象已经被修饰为const的,那么我们就可以直接使用隐式的临时对象来进行初始化。
5.const指针
指针是对象,因此允许使用const修饰符对指针进行修饰,我们成为常量指针。
"const +指针"的使用遵循以下规则:
1.常量指针必须进行初始化,初始化规则与指针初始化规则基本相同。
6.“const指针”的衍生概念——顶层const以及底层const
顶层const:表示指针本身(或者说作用于对象本身)是一个常量。
底层const:表示指针所指的对象(或者说作用于所引用的对象)是一个常量。
int i=0;
int *const p1=&i;//顶层const,const直接限定指针本身
const int ci=42;//顶层const,同上
const int *p2=&ci;//底层const, const限定的是指针指向的对象
const int *const p3=p2;//底层const+顶层const
//特别注意
const int &r=ci;//用于声明引用的const都是底层const
执行对象的拷贝操作时,顶层const和底层const遵循以下规则:
1.对于顶层const,执行拷贝操作并不会改变被拷贝对象的值,因此拷入和拷出的对象是否为const无关紧要。
i=ci;//拷贝ci的值,ci是一个顶层const,无影响
p2=p3;//p2和p3指向的对象类型相同,p3的顶层const部分不受影响
2.对于底层const,拷入和拷出对象必须具备相同的底层const资格,或者二者的数据类型可以进行转换(非const转向const,反之不成立)。
int *p=p3;//错误,p3包含底层const定义,p1没有
p2=p3;//正确
p2=&i;//int*可以被转换为const int*
int &r=ci;//int&不能被转换为const int
const int &r2=i;//const int&可以绑定到一个普通int上,参见“const+引用”规则
7.以上规则除了用于变量定义、初始化之外,同样也用于函数的参数传递过程
函数形参初始化的机制与变量初始化一样,函数的参数传递包括值传递与引用传递
引用传递:引用形参是其所绑定对象(即实参)的别名。
值传递:实参的值被拷贝至形参,形参与实参之间相互独立。
8.const形参和实参
规则:
1.形参的顶层const被忽略
当使用实参初始化形参时会忽略掉形参的顶层const,即当形参有顶层const时,传递给其常量对象或者非常量对象都是允许的。
void foo(const int );//初始化形参时顶层const被忽略,但foo里面仍然不能修改该形参的值
void foo(int);//重复定义
2.形参的初始化规则与变量的初始化规则是一致的。
1> 可以使用非常量初始化一个底层const对象,反之不允许。
2> 普通的引用必须使用同类型的对象初始化。
int i=0;
const int ci=i;
string::size_type ctr=0;
//函数原型1 reset(int *ip)
//函数原型2 reset(int &ip)
reset(&i);//正确,指针传递
reset(&ci);//错误,不能使用const int的指针初始化int(const无法向非const转换)
reset(i);//正确,调用类型为int&(int引用)
reset(ci);//错误,不符合引用初始化规则(普通引用无法绑定到const对象上)
reset(42);//错误,同上(普通引用不能使用字面值初始化)
reset(ctr);//错误,类型不匹配
//函数原型 find_char(const string &s, char c, string::size_type &occurs)
find_char("hello",'o',ctr);//正确,符合“const引用”初始化规则
3.尽量使用常量引用
理由1:当我们确定函数不会修改参数中的值的时候,使用const限定符可以保证数据安全。或者说,const限定符告诉程序员函数不会对形参所引用的数据进行修改。
理由2 :使用引用避免不必要的拷贝。
理由3:我们不能把const对象,字面值,或者需要类型转换的对象传递给普通引用形参(参见引用的初始化规则),但“const引用”没有这样的限制。
9.constexpr关键字
概念:常量表达式——常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。
一个对象是不是常量表达式取决于两点:数据类型+处初值。
const int max_files=2200;
//数据类型为const int,并且初始值为字面常量,所以是常量表达式
const int max_limit=max_files+1000;
//数据类型为const int,初始值为字面常量,同上
int staff_size=27;
//不是常量表达式,因为数据类型是int
const int sz= get_size();
//不是常量表达式,因为get_size()不是constexpr函数
编译时而不是运行时获得数据的值有很多好处,但我们在用const的时候并不都是如此。为此引入constexpr关键字,借助编译器来验证(或者说强制要求)变量的值是否是一个常量表达式。
constexpr int max_files=2200;//数据类型为const int,并且初始值为字面常量,所以是常量表达式
constexpr int max_limit=max_files+1000;//数据类型为const int,初始值为字面常量,同上
int staff_size=27;//编译器将会报错
constexpr int sz= get_size();//如果get_size()不是constexpr函数,编译器会报错
constexpr规则:
1.constexpr改变对象的顶层指针状态。
2.constexpr不影响底层const。
const int *p=nullptr;//底层const,p是一个指向const int的指针
constexpr int *q=nullptr;//顶层const,q是一个指向int的常量指针
3.一个constexpr指针的初始值必须为nullptr,0或者存放于某个固定地址的对象。特别地,函数内定义的非静态的变量不能用于初始化constexpr指针。
4.遵循前面所述的规则,一个constexpr指针可以指向一个常量或者非常量
constexpr int *np=nullptr;
int j=0;
constexpr int i=42;
constexpr int *p=&j;//常量指针,指向const int
constexpr int *p1=&j;//常量指针,指向整数
10.constexpr函数
constexpr函数指的是能用于常量表达式的函数,其返回值以及形参类型必须是字面值类型,且函数体中必须有且只一个return语句。
所谓的字面值类型是指:算术类型,引用,指针都属于字面值类型。自定义类,IO库,string等则不属于字面值类型。
constexpr函数规则:
1.执行constexpr初始化时,编译器把constexpr函数的调用替换为结果值。constexpr函数被隐式地定义为内联函数。
2.constexpr不一定返回常量表达式,此时编译器会报错。
constexpr size_t scale(size_t cnt)
{
return new_sz()*cnt;
}
int arr[scale(2)];//正确,scale返回常量表达式
int i=2;//i不是常量表达式
int a2[scale(i)];//编译器将报错