第八天
简单记录一下今天。很忙,上午第一节9.30下课,去离退休处报销医药费,查验的人说我没签字,也没明细,只有发票,不能报销,第一节课的时候辅导员突然催交申请书,还是先回宿舍写了申请书交上去,然后又打车去医院打印发票,回来以后再去校医院签字,然后再去报销,就已经12:50了。下午还得去马区盖章,1:30的车,吃了点饭又去赶校车,到了就2:30,盖章五分钟,突然发现流水号错了,又去网站上重新打印,联系科技处老师,折腾到3:30发现不能改了?后来就再问指导老师,老师那儿也不能改,到了4:50回到学校,又给验证码的老师问了一下,他说没问题,等到授权了再改也行。白忙活了,5:00,去学工办重新签字,签完字被辅导员拉去做迎新志愿者,听一个简单的培训,5:40,回到宿舍了,收拾包,6:10去的图书馆,今天学的东西,让我更压抑了,下面再说。
现在21:38,开始
今天学的函数的相关操作,有点难,有点绕,但是觉得学到了一点东西,比昨天好一点
1.函数
参数传递
实参与形参交互。
1.如果形参是引用类型,那么它将会绑定到实参上
2.否则,一般地,实质就是实参向形参的拷贝
当形参是引用类型,那么就是引用传递(就是绑定)
当形参是一般类型,那么就是传值调用(字面的意思)
void reset (int *ip){
*ip = 0; //改变了ip的指向
ip = 0; //只是改变了局部拷贝
}
但是上述函数的实参必须是地址
int i = 42;
reset(&i);
综上,函数的实参和形参的代换,可以认为就是一种赋值或者是拷贝形式*p = &i;
传引用参数
void reset(int &i){
i = 0;
}
int j = 42;
reset(j); //j采用引用传值,它的值被改变
这里的实参也发生了改变,跟上述的赋值或者拷贝形式一样的,这里也可以这么认为
但是拷贝大容量的类对象或者容器对象很低效,甚至有的类型不支持拷贝操作。当类型不支持拷贝操作的时候,函数只能通过引用形参访问该类型的对象,字符串不支持直接拷贝,同传递。
bool isShorter(const string &s1, const string &s2){
return s1.size() < s2.size();
}
上述例子只是对两个对象进行比较,不需要对实参进行改变,这里用const更好,当函数无需修改时隐形形参的值最好使用常量引用
下面使用形参返回额外信息
string::size_type find_char(const_string &s, char c, string::size_type &occurs){
auto ret = s.size();
occurs = 0;
for (decltype(ret) i = 0; i != s.size(); ++i)
if (s[i] == c){
if (ret == s.size()){
ret = i; //这里看到第一个i直接改变值,后面就不会再出现
}
++occurs; //出现一个就加一次
}
return ret; //出现次数通过occurs隐式返回
}
代码意思不多说了,这里简答说一下几个类型,做一个总结
string::size_type
代表的是对字符串计数的数字,是整型,例如ret = s.size()
decltype(a) b = 0
将b定义成和a一样的类型
但是c++里面又auto就很不错了,对于里面出现的类型,最起码作为左值声明的时候基本上不会出现错误
const形参和实参
这里再简单的说一下顶层const和底层const,顶层const对值,对具体的规定对象,但是底层const对应的地址
const int ci = 42;//这里是顶层const,ci的值不允许改变
int i = ci; //正确,但是拷贝成功了以后,自动忽略顶层const,i就是一般类型
int * const p = &i; //const是顶层的,不能给p赋值
*p = 0; //通过p改变了i
第三条意思是可以改变p的指向,但是不能通过改变地址来改变指向的值
上面说再用实参传递值的时候可以自动忽略顶层const,也就是一般的声明了
void fcn(const int i)//能够读取i,但是不能通过i向实参写值
void fcn(int i) //这里重新定义了fcn,会报错,并不是重载函数
指针或引用形参与const
int i = 42;
const int *cp = &i; //不能通过cp改变i
const int &r = i; //r不能改变i
const int &r2 = 42; //r绑定在字面值上
int *p = cp; //p的类型和cp的类型不匹配
int &r3 = r; //r3的类型和r的类型不匹配
int &r4 = 42; // 不能用字面值初始化
一些简单的初始话,理解了好长时间,得多看,一段时间不看就忘了
reset (&i); //int *
reset (&ci); //不能用指向const int的指针初始化int*
reset (&i); //int&
reset (&ci); //不能用普通引用绑定
reset (42); //不能用数值常量绑定啊
reset (ctr); //ctr是无符号类型,不行
使用引用而非常量引用也会极大的限制函数所能接受的实参类型。我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
//不良设计:第一个形参的类型为const string&
string::size_type find_char(string &s, char c, string::size_type &occurs);
解释一下,如果或说是下面这个,那么就只能将其作用于string对象,简单的引用定义就是,上面的是对于一般的字符常量可以通过。
但是如果是下面的
find_char("Hello World", 'o', ctr);
第一个参数相当于常量,当然就编译不通过
还有一个更加复杂的问题,假如说一个函数里面定义了调用了另一个函数
bool is_sentence (const string &s){
string::size_type ctr = 0;
return find_char(s, '-', ctr) == s.size() - 1 && ctr == 1;
}
如上,如果说传入的是一个常量string,那么在find_char中的编译会发生错误,因为其中的s不被定义,默认为不能接受常量引用,只能就收一般引用。
解决该问题有两种思路,第一种是修改is_sentence的形参类型,但是这不能从根本上解决问题,因为一旦还是一个常量string还是无法进行处理。正确的修改方法是修改find_char的形参,但是如果find_char的形参不能修改。
第二种方法是在is_sentence的函数内部修改,可以将传入的常量string存入一个s的变量,作为一个副本,再传入find_char中。
数组形参
void print(const int*);
void print(const int[]); //函数的意图是作用一个数组
void print(const int[10]); //这里的维度说明我们希望传入的数组中含有多少个元素
尽管表现形式不同,但是上述三个语句是等价的
实参传入形参形式,同赋值形式&a[0]
,a
上面就说明数组是以指针的形式传递给函数的,所以函数一开始并不知道数组的确切尺寸
下面利用c++的标准库规范来说明一下
void print(const int *beg, const int *end){
while (beg != end)
cout << *beg++ << endl;
}
void print(begin(j), end(j));
一种方法
void print(const int ia[], size_t size){
for (size_t i = 0; i != size; ++i){
cout << ia[i] << endl;
}
}
print (j, end(j) - begin(j));
当函数不需要对数组元素进行读写操作的时候,数组形参应该是指向const的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非向量的指针。
下面对于数组的一部分
void print(int (&arr) [10]){
for (auto elem : arr)
cout << elem << endl;
}
}
上述(&arr)[10]
是绑定再arr上的一个存有10个整型的数组。
传递多维数组方法
void print (int matrix[] [10], int rowSize)
void print (int (&matrix)[10], int rowSize)
今天先到这了,11点了,整理速度还是不大快啊,还是注意力不太集中,最重要的是有些地方看第一遍还好,看第二遍就看不大明白了,明天继续加油把,我是想先学完class类再开始看的,有点纠结,太费时间了。