您的位置:首页 > 编程语言 > C语言/C++

C++学习笔记——变量和基本类型

2016-10-28 10:52 585 查看
string是在命名空间里定义的

输入输出:

cout << "result:" << x;
<=>
(cout.operator << ("result:")).operator << (x);


C++选用位移运算符<< 和 >>重载是因为这两个运算符的优先级在没有括号时低于所有算术运算符,但是如果表达式中使用了优先级比<<和>>还低的运算符,如位运算符|,则必须使用括号。

C++中,初始化和赋值是两个完全不同的操作

列表初始化:

int a = 0;
int a = {0};
int a{0};
int a(0);


当使用列表初始化为内置类型变量初始化时,如果初始值存在丢失信息的风险,编译器会报错:

long double ld = 3.141592653;
int a{ld};//错误


内置类型的变量未被显式初始化,则其值由定义的位置决定。定义在函数体内部的内置类型变量将不被初始化,其值是未定义的。类的对象如果没有显式初始化,其值由类确定。

C++将定义和声明区分开。声明使得名字为程序所知,定义则负责创建与名字关联的实体。

声明而不想定义一个变量时,变量前加关键字extern,且不显式初始化变量,若包含初始值则是定义:

extern int i;//声明变量i
int i;//定义i
extern double pi = 3.1415;//定义pi


函数体内部试图初始化一个由extern关键字标记的变量会引发错误。

变量只能被定义一次,但是可以被多次声明。

作用域:全局作用域、块作用域

嵌套作用域:内层作用域(被嵌套的作用域)、外层作用域(包含别的作用域的作用域)

内层作用域可以重新定义外层作用域已有的名字,但是会覆盖同名变量,若是想使用外层作用域的同名变量,可以在变量前加作用域操作符::来覆盖默认的作用域规则,向全局作用域发出请求获取作用域操作符右侧名字对应的变量。

复合类型:引用和指针等

引用:

引用一般多指左值引用

引用为已经存在的对象起了一个别名,必须被初始化(定义引用时,将引用和初始值绑定,而不是将初始值拷贝给引用):

int ival = 1024;
int &refVal = ival;//refVal指向ival,即ival的一个别名
int &refVal3 = refVal;//refVal3绑定到了与refVal绑定的对象ival上


定义引用后,对引用的所有操作都是在与之绑定的对象上进行的

由于引用本身不是对象,所以不能定义引用的引用

除了两种例外情况,其它所有引用类型必须匹配它所指向的对象类型,且引用只能绑定对象

指针:

指针本身是一个对象,允许对指针赋值和拷贝,可以先后指向几个不同的对象,指针无须在定义时赋值

在块作用域内,指针如果未被初始化,则其值不确定

指针存放某个对象的地址,使用取地址符获取对象地址

由于引用不是对象,没有实际地址,因此不能定义指向引用的指针

除了两种例外情况,其它所有指针类型必须匹配它所指向的对象类型,因为声明语句中指针类型实际上被用于指定它所指向的对象

如果指针指向了一个对象,可以使用解引用符*来访问该对象

空指针不指向任何对象,使用未经初始化的指针是引发运行时错误的一大原因

void*指针是一种特殊的指针类型,可以用于存放任意对象的地址。不能直接操作void*指针所指的对象,因为不知道对象的类型

复合类型声明:

int* p1,p2;//p1是指向int的指针,p2是int,*与变量名之间有无空格并无影响


指针是对象,所以存在对指针的引用

int i = 0;
int *p;
int *&r = p;//r是对指针p的引用


要理解r的类型到底是什么,最简单的方法是从右到左阅读r的定义,离变量最近的符号(此处为&)对变量的类型有最直接的影响,因此r为一个引用。


const限定符:

const对象一旦创建后其值不再改变,因此const对象必须初始化

const的引用:

将引用绑定到const对象上,称之为对常量的引用(reference to const),对常量的引用不能修改它所绑定的对象

const int ci = 1024;
const int &r1 = ci;//正确,引用及其对应的对象都是常量
r1 = 42;//错误,r1是对常量的引用
int &r2 = ci;//错误,试图让一个非常量引用指向一个常量对象


引用类型必须和其所引用的对象类型一致的第一种例外:

初始化常量引用时允许任意表达式作为初始值,只要该表达式结果能转换成引用类型即可(甚至为常量引用绑定非常量对象、字面值或者一般表达式):

int i = 42;
const int &r1 = i;//正确,允许将const int&绑定到一个普通int对象上
const int &r2 = 42;//正确
const int &r3 = r1*2;//正确
int &r4 = r1*2;//错误,r4是一个普通非常量引用


对const的引用可能引用一个并非const的对象,不可以通过引用来修改对象的值,但此时可以通过其它途径修改对象的值:

int i = 42;
const int &r1 = i;
int &r2 = i;
r1 = 0;//错误,r1是一个常量引用
r2 = 0;//r2并非常量,i值被修改为0


指针和const:

指向常量的指针不能用于改变其所指向对象的值,要想存放常量对象的地址,只能使用指向常量的指针,(但是指向常量的指针可以存放非常量对象的地址):

const double pi = 3.14;//常量
double *ptr = π//错误,ptr是一个普通指针,不能指向常量对象
const double *cptr = π//正确
*cptr = 42;//错误,不能给*cptr赋值,即不能改变常量对象的值


指针类型必须与其所指对象的类型一致的第一种例外情况:

允许令一个指向常量的指针指向一个非常量对象

double dval = 3.14;
const double *cptr = &dval;//正确,但是不能通过cptr改变dval的值


但是dval的值可以通过其它途径改变


const指针:指针是对象而引用不是,因此可以像其它对象类型一样把指针本身定为常量。常量指针必须初始化,一旦初始化完成,其值(即存放在指针中的地址)不可以再改变。指针不可以改变,但是指针指向的对象的值可以改变(若该对象不是常量)

int errNumb = 0;
int *const curErr = &errNumb;//curErr必须一直指向errNumb
const double pi = 3.1415;
const double *const pip = π//pip是一个指向常量对象的常量指针


从右向左阅读声明含义。


顶层const(top-level const)表示指针本身是一个常量(顶层const可以表示任意的对象是常量)

底层const(low-level const)表示指针所指对象是一个常量(指针类型既可以是顶层const也可以是底层const)

常量表达式:值不会改变,并且在编译过程就能得到计算结果的表达式

const int sz = get_size();//sz不是常量表达式,尽管它本身是一个常量,
//但它的具体值直到运行时才能获取到


constexpr变量:声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化

尽管指针和引用都能定义成constexpr,但它们的初始值受到严格限制。

一个constexpr指针的初始值必须是nullptr或者0,或者是某个存储于固定地址中的对象。

函数体内定义的变量一般并非存放固定地址中,因此constexpr指针不能指向这样的变量。

在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关。

与其它常量指针类似,constexpr指针既可以指向常量也可以指向一个非常量。

类型别名:某种类型的同义词。

两种定义类型别名方法:

1.使用关键字typedef:含有typedef的声明语句定义的不再是变量而是类型别名

typedef double wages;//wages是double的同义词
typedef wages base, *p;//base是double的同义词,p是double*的同义词


2.使用别名声明:将等号左侧的名字规定成等号右侧类型的别名

using SI = Sales_item;//SI是Sales_item的同义词(Sales_item是一个类)

指针、常量和类型别名:

typedef char *pstring;//pstring是类型char*的别名
const pstring cstr = 0;//cstr是一个指向char的常量指针
const pstring *ps;//ps是一个指针,它的对象是一个指向char的常量指针


上述声明的基本数据类型为const pstring,const是对给定类型的修饰,pstring是指向char的指针,因此const pstring是指向char的常量指针,而不是指向常量字符的指针(不要把类型别名替换成它本来的样子,会发生理解错误)。


auto类型说明符:让编译器替我们分析表达式所属类型

atuo让编译器通过初始值来推断变量类型,因此auto定义的变量必须初始化:

auto item = val1 + val2;//item初始化为val1和val2相加的结果


使用auto在一条语句中声明多个变量:由于一条声明语句中只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须一致。

auto i = 0, *p = &i;//正确,i是整数,p是整型指针
auto sz = 0, pi = 3.14;//错误,sz和pi类型不一致


编译器推断出来的auto类型有时候和初始值不完全一样,编译器会适当改变结果类型使其更符合初始化规则,auto一般会忽略顶层const,保留底层const

decltype类型指示符:从表达式的类型推断出要定义的变量类型,而不用表达式的值初始化变量,作用是选择并返回操作数的数据类型:

decltype(f()) sum = x;//sum的类型就是函数f的返回类型


编译器并不实际调用函数f,而是使用当调用发生时f的返回值类型作为sum的类型。

如果decltype使用的表达式是一个变量,decltype返回该变量的类型(包括顶层const和引用)。

decltype((variable))的结果永远是引用,decltype(variable)的结果只有当variable本身是引用是才是引用。

如果decltype使用的是一个不加括号的变量,得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器将其当作是一个表达式。

自定义数据结构:

struct Sales_data {/*...*/}  accum, trans, *salesptr;//struct关键字,类名,类体,类体后加分号
<===>
struct Sales_data {/*...*/};
Sales_data accum, trans, *salesptr;//等价,但是这样分开定义类和变量更好


编写自己的头文件:

头文件一旦改变,相关的源文件必须重新编译以获取更新过的声明。

从C语言继承来的预处理器看到#include标记时就会用指定的头文件的内容代替#include。

预定义变量:两种状态-已定义和未定义;#define指令把一个名字设定为预处理变量,#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真,一旦检查结果为真,则执行后续操作直至玉带#endif指令为止。

预处理变量无视C++中关于作用域的规则。

为避免冲突,一般预处理变量的名字全部大写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++