第二章 变量和基本类型
引用
-
引用定义的时候必须初始化。
-
引用初始化之后无法重新绑定到其它对象上。
-
引用本身并不是对象,所以没有指向引用的引用(不管如何多层引用,引用的还是源对象)
下面用一个简单的例子说明:
int a=1;
int& b=a;
cout<<&a;//00D4FE0C
cout<<&b;//00D4FE0C
可以看出对源对象a
进行取地址和对b
进行取地址的结果是一样的(实际上,引用的本质就是一个常量指针,即 int * const ptr
类型)
指针
- 指针本身就是个对象,允许对指针本身进行复制和拷贝操作。
- 指针定义时可以不赋初值,也可以指向
nullptr
,同时,其值也可以为未确定的值。 - 指针在其生命周期内可以指向不同的对象。
下面用一个简单例子说明:
int a=1;
int& b=a;
cout<<&a;//012FFD64
cout<<&b;//012FFD58
可以看出指针本身的地址和源对象的地址是不一样的,这是指针和引用的一个重要区别。
nullptr和NULL
nullptr
是C++ 11 引入的新的关键字,而NULL是一个值为0的预处理变量,即:
#define NULL 0
在程序中最好使用nullptr
关键字而非NULL,例如下面的例子:
#include <iostream>
void go(int num)
{
std::cout << "number" << std::endl;
}
void go(void *p)
{
std::cout << "ptr" << std::endl;
}
void main()
{
void *p = NULL;
go(p);//ptr
go(NULL);//number 得到的很有可能不是想要的答案
go(nullptr);//ptr
system("pause");
}
void*指针
void*
是一种特殊的指针类型,可以存放任意对象的地址,但是我们不了解其中存放的对象到底是什么类型的,我们可以用此指针来保存对象,而void表明我们我们用什么类型来解释这段存储空间中的对象(包括对象的长度),但我们无法对这个对象进行操作,因此在进行对象操作之前我们需要先对该指针进行类型转换。
int a=1;
void* b=&a;
cout << *(int*)b << endl;// 1
const限定符
const
关键字用来表示一个常量,例如:
const int a=100;
编译器会在编译过程中把所有用到这个变量的地方全部替换成常数100。
默认情况下,const对象仅仅在当前文件内有效,如果多个文件中出现了同名的const变量,那么等于在不同文件中分别定义了独立的变量。(如果需要多个文件之间共享,那么我们需要使用
extern
关键字)
- const结合指针
通过下面的代码区分const
结合指针的区别:
int a=10;
const int * ptr=&a;// ptr本身可以指向别的对象,但不能通过ptr修改a的值
int const * ptr=&a;// 同上
int * const ptr=&a;// ptr本身不能改变,但是可以通过ptr修改a的值
类型别名
- typedef
我们可以使用typedef来定义类型别名,这样我们就可以把越写越长的变量类型用短的单词代替,例如:
typedef int in;//in就是int的别名
typedef in i,*ptr;//i就是int的别名,ptr是int*的别名
#include <iostream>
using namespace std;
class A
{
public :
void test() { cout << 1; }
};
int main()
{
auto i=new A();
typedef void (A::*ptr)(); //ptr为指向A成员函数的指针类型,指向的函数的参数和返回值都为空
ptr a= &A::test;
(i->*a)();//输出1
}
同时我们需要注意typedef与define的一个重要区别:
typedef char* type;
#define def char*
type i1, i2; // i1 和 i2 均为指向 char 的指针
def j1, j2; // j1 为指向 char 的指针, 但 j2 为char 型变量
- using
C++11新标准提出了新的方法,使用using来取别名:
using newInt= int;
newInt a=10;
auto类型说明符
自动分析表达式类型。
auto i=1;//int类型
- 一般会忽略顶层const,保留底层const
顶层const:指针本身是常量;
底层const:指针指的对象是常量;
int i=0;
int * const p1=&i; //这是顶层const,不能改变p1的值
const int ci=42; //顶层const,不能改变ci的值
const int *p2=&ci; //底层const,不能改变p2的值
const tint * const p3=p2;//左边是底层const,右边是顶层const
const int & r=ci; //用于声明引用的都是底层const
个人理解:定义的变量本身不能改变,则为顶层const,反之则为底层const(引用除外,引用都是底层const)
再看auto推断规则:
int i=1;
const int ci=i;
const int &cr=ci;
auto b=ci;//b是一个整数,顶层const忽略
auto c=cr;//c是一个整数,cr是ci别名,ci是顶层const
auto d=&i; //d是一个整型指针
auto e=&ci; //e是一个整型常量指针,对常量对象取地址是一种底层const,也就是const int* e=&ci;
再看一个例子:
int a=1;
const int* ptr1=&a;
auto res1=ptr1;//底层const保留,res1类型为const int*
int* const ptr2=&a;
auto res2=ptr2;//顶层const忽略,res2类型为int*
如果希望推到出的auto类型是一个顶层const,那么需要明确指出:
const auto f=ci;//这时候才会带上const,f类型为const int
decltype
decltpye
用于选择并返回操作数的数据类型,在这个过程中编译器分析表达式得到类型,但是不计算表达式实际的值。
decltype(function()) sum=x; //sum的类型就是function函数返回值的类型
- decltype和auto的区别
decltype返回该变量的类型,包括顶层const和引用在内。
const int ci=0,&cj=ci;
decltype(ci) x=0; // x的类型是const int
decltype(cj) y=x; // y的类型是const int&,y绑定到变量x
decltype(cj) z; // 错误,z是一个引用,必须初始化
需要指出,引用从来都是作为其所指对象的同义词出现,只有用在decltype处是个例外。
此外,对于decltype所用的表达式来说,多加了一对括号和不加括号时会有区别,如果给变量加上了一层或者多层的括号,那么编译器会把它当作一个表达式,变量时一种可以作为赋值语句左值的特殊表达式,所以这样的decltype会得到引用类型。
decltype((i)) d; // 错误,d是int&,必须初始化
decltype(i) e; // 正确,e是一个int
decltype((variable))
的结果永远是引用,而decltype(variable)
的结果只有在variable本身是一个引用时才是引用。
预处理器
C++程序可以使用#define
指令把一个名称设定为预处理变量,用#ifdef
表示在变量定义的时候为真,#ifndef
为当变量未定义时为真,一旦检查结果为真,则执行后续操作到#endif
为止。
#ifndef HEAD_H
#define HEAD_H
... your code...
#endif
注意在VS中也可以使用
#pragma once
来表示只编译一次,但是只在windows下生效,无法跨平台。