C++ primer(第4、5、6章)阅读笔记

运算符

赋值运算符

int ival, jval ;
ival = jval = 0; //先赋值jval,然后赋值ival

递增递减运算符
递增运算符(++)和递减预算符(–)为对象加1或减1操作提供了一种简洁的书写方式

int i=0, j ;
j = ++i;  //前置版本得到递增之后的值,j=1, i=1
j = i ++; //后置版本得到递增之前的值,j=1, i=2

注意代码的简介的书写方式:

cout << *iter++ <<endl;

//等价于下面的形式
cout << *iter << endl;
++iter ;

上面形式更加简洁。
条件运算符
条件运算符(?:)为一种简洁的if-else逻辑嵌入到单个表达式中。

string finalgrade = (grade < 60) ? "fail" : "pass" ;

嵌套条件运算符

finalgrade = (grade > 90) "high pass" : (grade < 60) ? "fail" : "pass" ;

算术转换

整型提升,负责将小整数类型转换成较大的整数类型。

3.141L + 'a' ; //将'a'提升为int,然后提升为long double
double dval ;
int ival ;
dval + ival ; // ival转换成double

隐式类型转换
数组转换为指针:

int ia[10];  //
int *p = ia; //ia转换为指向数组首元素的指针

指针的转换

  • 常量数值0或者字面值nullptr能转换为任意的指针类型;
  • 指向任意非常量的指针可以转换为void * ;
  • 指向任意对象的指针可以转换为const void*;
int i;
const int &j = i ; //非常亮转换为常量引用
const int *p = &i ;  //非常量的地址转化为const地址
int &r = j, *q = p; // 错误,const不能转化为常量 

显示转换

  • static_cas: 任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
double slop = static_cast<double>(j) / i ; //进行强制类型转换,以便进行浮点除法

const_cast,只能改变底层const。底层cosnt代表指针所指的对象是一个常量,顶层const代表指针本身是个常量。

const char *pc ; // 
char *p = const_cast<char*>(pc) ; //正确,但是通过p写值式未定义的。

语句

switch中,要注意不要漏泄break,这样容易引发缺陷。
范围for语句,器语法形式是:

for (declaration: expression)
	statement
//example

vector<int> v = { 1,2,3,4 };
for (auto &r : v)
{
	r *= 2;
}

cout << v[1]<< endl;

for (auto beg = v.begin(); beg != v.end(); beg++)
{
	auto &r = *beg;  //引用
	r *= 2;
}
cout << v[1] << endl;

try语句块与异常
定义try语句块的语法形式是

try{
 program-statement;
}
catch(exception-declaration){
}
catch(exception-declaration){
}

函数

形参和实参:实参是形参的初始值。
局部对象
在c++语言中,名字有作用域,对象有生命周期。

  • 名字的作用域是程序文本的一部分,名字在其中可见;
  • 对象的生命周期是程序执行过程中该对象存在的一段时间。

局部静态对象:零局部变量的生命周期贯穿函数调用以及之后的时间。这可以通过将局部变量定义为static类型,局部静态对象在程序执行路径第一次经过对象定义语句时初始化,并知道程序终止被销毁。

int count_calls(){
	static int ctn = 0;  //调用结束后这个值仍然有效
	return ++ctn ;
}
int main(){
	for(int i=0; i !=10; i++){
		cout << count<calls() << endl ;
		}
}

如果局部静态变量没有显示的初始值,那么它将执行值初始化,内置类型的局部静态变量初始化为0。

函数声明
函数声明和变量声明一样,可以声明多次,但是只能定义依次。函数声明也称为函数原型。函数的三要素:返回类型、函数名、形参类型。

分离式编译:将程序分割到几个文件中去。

参数传递:每次调用函数时会重新创建它的形参,并用传入的实参对器进行初始化。

当形参是引用类型时,其对应的实参引用传递,引用形参是它绑定的兑现的别名,即它对应的实参的别名。
传值参数:当初始化一个非引类型的变量时,初始值被拷贝给变量。函数对形参的所有操作都不会影响实参。
指针形参:执行指针拷贝时,拷贝的是指针的值,那么两个指针为不同的指针,我们可以通过指针访问其对象。

void reset(int *p){
 *ip = 0 ; //改变指针所指对象的值
 ip = 0;  //只改变了ip的局部拷贝,实参没有改变。
}

传递引用参数。通过引用形参,允许函数改变一个或多个实参的值

void reset(int &i){
	i = 0; //改变了i所指对象的值
}

使用引用避免了拷贝。拷贝大的类型的对象或者容器对象比较低效,有的类类型不支持拷贝操作,那么只能通过形参访问该类型的对象。
使用形参返回额外信息可以将返回值作为传参的引用类型。
一个函数只能返回一个值,然而有时候需要同时返回多个值,我们可以定义一个新的数据类型来包含这些需要返回的值,当然,引用形参为我们一次返回多个结果提供了另一种有效的途径。看下面的这个例子,我们给函数多传入了一个引用实参。

string::size_type find_char(const sting &s, char c, string::size_type &occurs}{
	auto res = s.size();
	occurs = 0;
	for(decltype(ret) i=0; i!=s.size(); ++i){
	if(s[i] == c){
		if(ret == s.size()) ret = i;  //返回第一次出现的位置
		 occur ++;  //返回总共出现的次数}
	}
	return ret;
}

const形参和实参
当形参是const类型时,要主要关于顶层const和底层const的区别。
所谓顶层const,就是指向的变量是一个常量;底层const,是一个常量地址。看例子:

const int ci = 42;  //顶层,ci无法改变
int i = ci;  //正确,拷贝ci是可以的,此时忽略其顶层const
int * const p = &i;  //顶层的const
*p = 0; //正确,通过p改变对象的内容

指针或引用形参的const

int i = 42;
const int *cp = &i; //正确,但是cp不能改变i
const int &r = i; //正确但是r不能改变r
const int &r2 = 42; //正确
int *p = cp; //错误,类型不匹配;
int &r3 = r; //错,类型不匹配
int &r4 = 42//错,不能用字面值初始化一个非常量的引用

可见规则为:

  • 可以使用非常量的对象初始化一个常量对象,但是常量对象不能初始化非常量对象。
  • 一个普通类型的引用必须用同类型的对象进行初始化。

将上面的初始化规则应用到参数传递中,见:

int i = 0;
const int ci = i;
const int ci = i;
string::size_type ctr = 0;
reset(&i) ; //调用的是int*
reset(&ci); //错误:不能用指向const int的指针初始化int *;
rest(i) ; //调用int &
reset(ci) ; //不能讲普通引用绑定到const 对象上
reset(42); //错误,普通引用不能绑定到字面值(常量)

注意尽量使用常量引用
当函数的形参不会改变时,定义为普通的调用时一种常见的错误,这样会使得函数嫩egg修改它的实参的值。
此外,使用引用而非常量引用也会极大的限制函数所能接收的实参类型。

函数的返回

函数的值时如何被返回的?
返回一个值的方式和初始化一个变量或者形参的方式完全一样,返回的值用于用于初始化调用点的一个临时量。

函数返回一个引用

const string &shortString(const string &s1, const string &s2){
	return s1.size()<s2.size()  ? s1:s2 ;
}

  • 不要返回局部变量的引用或者指针

返回数组指针。数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或者引用。

using arrT = = int[10];
arrT* func(int i);  //func返回一个指向含有10个整数数组的指针

\\要想在声明func时不适用类型别名,我们要牢记被定义的名字后面数组的维度
int arr[3] = {1,3,5 };
int (*p)[3] = &arr; 
//这句其实也等价于
int *P = arr ; //数组名会由编译器换为数组首指针

如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在has名字之后。其形式如下

\\ 形式Type (*function(par_list)[dimension)
int  (*func(int i))[10];  //这是一个例子

const_cast和重载
在函数的重载中,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。也就是顶层const在参数传递时会被忽视掉(有点抽象这)。

首先回忆一下const_cast
const_cast只能改变运算对象的底层const。
这里我就要bb几句这个const了:

  • 顶层英文为top-level,底层为low-level,字面意思很难理解,反而从英文的角度有一种优先级的感觉,就是说,top-level级别很高,不能动;low-level的是可以改变值的。
  • 引用一句书上的“顶层const表示指针本身是一个常量,底层const表示指针所指的对象是一个常量
  • More general,** 一个顶层const可以表示任意的对象是常量**。底层const则与指针和引用这些符合类型部分有关。也就是指针类型可以是顶层也可以是底层”。
  • 一个特殊情况,用于声明引用的const都是底层const。

那么const_cast的作用是什么呢?
其实就是将常量对象转化为非常量对象,即cast away the const-去掉const性质。当去掉了const性质,我们就可以对改对象进行写操作了。不过我想说,底层const不是本来就可以进行值的更改吗?

说了这么多,言归正传,这个功能如何应用到函数重载中呢?看例子:

string &shortString(string &s1, string &s2){
	auto &r = shortString(const_cast<const string&>(s1),const_cast<const string&>(s2));
	return const_cast<string&>r ;
}

这个例子就是闲的没事了,首先它将其实参强制转换为const的引用,然后调用了shortString的const版本,const版本返回const string的引用,然后将其转换为一个普通的string&。问题:const_cast也可以将非const转化为const?

constexpr用于常量表达式的函数
** 调试助手assert**

函数匹配

当有几个重载函数时,可能需要进行函数匹配来确定应该使用哪个重载函数。

函数指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值