因为自己记忆力减退的关系(手动狗头),到了看书要做笔记的年纪了,所以重读一些编程笔记的时候,有很多概念需要进行一下笔记记录,同时重读有一些新的体会
c++ Primer 第五版 第6页:
在c++中,一个表达式通常产生一个计算结果,它由一个或多个运算对象和(通常是)一个运算符组成。
输出运算符(<<)接受两个运算对象,左侧的对象必须是一个ostream对象,右侧的对象是要打印的值。此运算符将给定的值写到给定的ostream对象里面。输出运算符的结果就是其左侧运算对象,即,计算结果就是我们写入给定值的ostream对象。
对于如下的表达式,
std::cout<<"ENTER"<<std::endl;
我们使用了两次<<运算符,第一个运算符的结果成为了第二个运算符的左侧运算对象,因此表达式可以等价于
(std::cout<<"EnTER")<<std::endl;
c++ Primer 第五版 第39页:
列表初始化
以下四个语句都可以将一个int型初始化
int unit = 0;
int unit = {0};
int unit{0};//用花括号初始化 在c++11里被称为列表初始化
int unit(0);
对于内置类型变量,使用列表初始化时,如果初始值存在丢失信息风险,会报错
long double ld = 3.14159;
int a{ld};//报错,信息丢失风险
int c(ld),d = ld;//正确,信息丢失
c++ Primer 第五版 第45页:
变量的声明语句是由一个基本数据类型(base type)加一个紧随其后的声明符列表组成
c++ Primer 第五版 第50页:
void * 指针是一种特殊的指针类型,可以存放任意对象的地址,我们对这个地址存放的对象是什么类型并不了解
void* 指针的用途比较有限,主要是用于指针间相互比较,用于函数的输入或者输出参数,或者赋值给另外一个void*指针,而由于我们对其存放的对象并不了解,所以并不能直接对其指向的对象进行操作
c++ Primer 第五版 第51页:
理解复合对象的定义,如前所述,变量的声明是基本数据类型+声明符列表,因此基本数据类型只有一个!通过关注声明符的不同形式,可以定义出不同的变量
而要理解一个变量到底是什么类型 重点是从右往左读 离他最近的符号对他的类型有直接的影响
``` int *p ;// p是一个int型指针```
int int *&r = p; //r 是一个引用,是对指针p的引用
c++ Primer 第五版 第55页 :
变量初始化以及对const的引用,前面提到引用需要和变量的类型一致,但存在两个例外情况,其一就是对const (常量引用)的初始化,初始化常量引用时允许使用任意表达式,只要该表达式返回的结果可以转换为相应的引用类型即可。尤其的,允许一个引用绑定非常量的对象、字面值甚至是表达式
int i = 42;
const int &r1 = i;//允许将const int &绑定到一个普通int对象上
const int &r2 = 42;// 正确:常量引用
const int & r3 = r1*2 //正确:常量引用
int &r4 = r1 * 2//错误:r4是一个普通的非常量引用
要理解这种情况,最简单的方法就是想办法弄清楚一个常量引用被绑定到另一个类型上时究竟发生了什么
double dval = 3.14;
const int &r1 = dval;
此处r1引用了一个int型的数,而dval却是一个double类型,所以为了确保r1绑定到int类型上,编译器进行了如下形式:
const int temp = dval;//用double生成一个临时变量
const int &r1 = temp;//r1绑定这个临时变量
这样r1就绑定了一个临时变量
而当r1不是常量引用时会发生什么呢,如果r1不是常量,那么就允许r1对绑定的变量进行赋值,注意r1现在绑定的是一个临时变量而不是dval,和程序员最初的想要改变dval的想法不一致,这时编译器就会报错。
同样的我们必须注意到 常量引用 只是对引用做出了限制,并没有限制其引用的对象,因此常量引用才可以绑定到普通Int类型上,
如 const int &r1 = i;//通过r1并不能对i进行修改,但可以对i直接修改,或通过普通的引用对i进行修改,可以注意到这时r1的值发生了同步的变化,也就是说仅仅是限制了r1的操作,并没有限制r1不能同步修改,毕竟引用是进行了绑定
也可以这么说,无论是指针还是引用,常量指针或引用可以指向或者绑定非常量,常量只能用常量引用或指针来指向或绑定。
书里提供了一个小思路,所谓指向常量,只不过是指针或者引用一厢情愿,所以他们不愿意做出改变,至于那个量是否改变,仍然取决于它本身。
c++ Primer 第五版 习题集 2.19 说明指针和引用的区别
区别有二:1.指针本身是一个对象。允许对其赋值、拷贝,在其生命周期中可以指向不同 的对象,而引用不是对象,无法重新绑定
2.指针无需在定义时赋值,引用则必须在定义时赋初值
c++ Primer 第五版 第56页:
指针是对象,因此可以和其他类型一样,将其定义为const,注意const要求其初始化时赋值,这样其保存的地址就不再改变,其生命周期中不得再次赋值
int errNum = 0;
int *const curErr = &errNum;//curErr一直指向errNum
const double pi = 3.14159
const double *const pip = &pi//pip 是指向常量对象的常量指针
将* 放在const前面,说明指针是一个常量,不变的是指针,而不是指向的那个值
c++ Primer 第五版 第59页:
c++11新标准,允许将变量声明为constexpr类型,以便编译器验证变量的值是否是一个常量表达式,注意,constexpr变量一定是一个常量,且必须用常量表达式初始化
如下两个表达式区别很大
const int * p =nullptr;// p是指向整型常量的指针
constexpr int * q = nullptr;//q是一个常量指针 指向整数
注意到q的constexpr将其置为顶层const
constexpr const int * p1 = &i;
c++ Primer 第五版 第60页:
类型别名的两种方式,传统方式为typedef
typedef double wages;//wages与double同义
typedef wages base,*p;//base与double同义,p与double*同义
新标准可以用别名声明来定义:
using SI= Sales_item;//SI是Sales_item同义词
注意 typedef char * pstring;//pstring 是char* 的别名
const pstring cstr=0;//cstr是指向char的常量指针,而不是指向常量的指针,如果将类型别名替换成本来样子去理解,就会产生错误的理解结果
const pstring *ps;//ps是一个指针 他的对象是指向char的常量指针
所以在类型复合起来之后 一定要注意const带来的意想不到的效果
C++ Primer 第五版 第60页:
auto类型变量必须有初始值
编译器推断出来的auto类型不一定和初始化值完全一直,会进行适当的修改
例如
int i=1,&r=i;
auto a = r;//a 是一个int类型,而非int的引用
其次,auto一般会忽略顶层的const,保留底层const,也就是说
如果希望推断出的auto带有顶层const,那就必须明确指定
const auto p = &i;//p是一个常量指针,指向i
也可以将引用设为auto
auto &r = i;//
auto &h = 42;//错误,不能为非常量引用绑定字面值(literal value)
要在一个语句中定义多个变量,一定要注意,初始化的类型一定要一致,也就是auto只能变成一种类型,两种类型就会出错
auto &n =i,*p=&ci;//由于i是int类型,ci是const int类型,auto会因为不一致报错
C++ Primer 第五版 第61页:
新类型decltype:
c++ 11中引入了decltype,用于返回操作数的数据类型。在此过程中,编译器分析表达式的返回类型,但并不实际计算表达式:
decltype(f()) sum = x;//sum就是函数f的返回类型
C++ Primer 第五版 第63页:
decltype和引用
如果decltype使用的表达式不是一个变量,decltype返回表达式对应 的类型。有些表达式将向decltype返回一个引用
int i=42,*p=&i,&r=i;
decltype(r+0) b;//加法的结果是int,所以b是一个未初始化的int
decltype(*p)c;//错误,c是int&,必须初始化
解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此decltype(*p)结果的类型就是int&(由返回对象的类型决定的),而非int
decltype和auto 的另一个重要区别就是,decltype和表达式形式密切相关,有一种特殊情况要切记
decltype((variable))双侧括号返回的结果永远是引用,而decltype(variable)单层括号只有variable本身是 引用时才是引用。
赋值是产生引用的一个很典型的表达式
decltype(a=b) d=a;//d为 int&