引用
- 引用是变量的别名;
- 注意区分引用与取地址符号:
int rats = 100;
int* pr = &rats; // &是取地址符号
int& rotatens = rats; // &是引用
引用初始化的时候要定义好,后期不能赋值,所以出现在等号左边的就是引用,出现在等号右边的是取地址符号;
3. 按引用传递,函数的引用参数被初始化为函数调用传递的实参;
4. 当引用仅仅是用来传递信息,而不希望引用会带来实参的修改,那么应该在函数原型和函数头中使用const;
5. 函数返回引用 vs 传统返回机制:
传统返回机制 类似于 按值传递,计算关键字return后面的结果,并将结果返回给调用函数。这个过程,这个值会被复制到一个临时的位置,调用程序使用这个临时的值。
const free_throws& clone2(free_throws& ft){
frew_throws new_copy;
new_copy = ft;
return new_copy;
}
const free_throws& clone2(free_throws& target){
set_pc(target);
return target;
}
const free_throws& clone2(free_throws& ft){
frew_throws *new_copy;
*new_copy = ft;
return *new_copy;
}
上面两个函数都返回引用,但是第一个是有问题的,因为ne_copy是一个临时变量,运行完毕后临时变量就不再存在,所以要避免返回临时变量的引用;
第二个函数是返回引用的正确的写法,返回一个作为参数传递给函数的引用;
或者采用第三种方法,通过new出一块内存给这个变量,但是问题在于只有new没有配套的delete,会导致内存泄漏。
double m = sqrt(16.0);
cout << sqrt(16.0);
dup = accumulate(team, five);
accumulate(team, five) = four;//accumulate返回引用的时候允许,返回值的话禁止
第一句中最后的值4.0会被先复制到临时位置,然后从临时位置复制给m。第二句中4.0同样先复制到临时位置,然后复制给标准输出cout。
第三句,如果accumulate返回的是结构,那么将整个结构先复制到临时位置,然后再复制给dup;但是如果返回的是引用,将直接把结果复制到dup效率更高;
4. 返回引用的函数实际上是被引用的函数的别名;
5. 引用的使用注意:
- 数据对象很小,直接按值传递
- 数据对象是数组,使用指针,唯一选择,并且声明为const的指针
- 数据对象是较大的结构,使用const指针或const引用
- 数据对象是类对象,使用const引用
引用 vs 指针
- 不存在空引用,引用必须指向一块合法的内存;
- 引用一旦初始化只会指定一个变量,且不能改变,相当于与变量绑定;但是指针随时可以指向其他的对象;
- 引用必须在创建的时候初始化,指针可以在任意时间初始化。
const 关键字
- 函数中不会被改变的参数用const修饰,这样就可以从函数定义中了解函数的意图:根据什么修改什么;
const Stock land = Stock("word", 199, 12);
land.show();
//show函数定义:
void Stock::show(); / /show函数不是const修饰,会被编译器拒绝;
void Stock::show() const;
- 上面的代码中,如果定义方式是第一种,编译器会拒绝;因为无法确定show函数是否会修改调用他的对象land;需要保证函数不会修改调用对象,在函数后面加const关键字,形如第二种方式,称之为const成员函数;
- 等同于上述第一点描述的一样,只要类方法不会修改调用对象,就将其声明为const;
const 与 函数
- 类成员函数中的const:
- 类的成员函数只要不修改类的数据成员就应该被定义为const;
- 如果const的成员函数修改了类内成员变量或者调用了其他的非const的成员函数,编译器会主动报错;
- 只有const的成员函数才能操作const的成员变量;
- const的对象默认调用类内const的成员函数;(函数重载中发挥作用)
- 函数返回值的const:
- 通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const(const A& test = A实例) ,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用;
- 函数参数的const:
- 函数参数中有const的存在多数为const引用,旨在说明这个参数在函数中不能被修改;
const 与 指针
int b = 100;
const int *a = &b; [1]
int const *a = &b; [2]
int* const a = &b; [3]
const int* const a = &b; [4]
* 左侧的const是为了表示指针是指向的常量;
* 放在右侧是指针本身是指针本身是常量;
[1] 和 [2] 都表示指针指向的是常量,指针也可以指向其他的常量;
[3] 表示的是指针本身是常量,只能指向b的地址,不能被赋其他值;
[4] 表示指针本身与指向的变量都是常量,钉死了,都不能改;
函数重载
函数多态与函数重载是一回事,通常使用函数重载:
- 函数的特征标涉及函数参数数目和类型,以及排列顺序;
- 函数的特征标与函数名字无关,所以允许函数名字相同,条件是他们的特征标不同;
- 类型引用和类型本身都是同一个特征标,即同一个类型引用和非引用被编译器视为同样的一种特征标;(下面两个相同)
double cube(double x);
double cube(double& x);
- 函数的名字相同,但是特征标不同才能重载,特征标是区别相同函数名字下彼此的不同,所以下面的两个函数不算重载,会报错,因为特征标相同;(返回值不属于函数特征标)
long gronk(int n, float m);
double gronk(int n, float m);
函数模板
相同的算法需要适配不同的数据类型时,需要采用函数模板:
现在比较通用的方式是第一种;
template <typename T>
void Swap(T& a, T& b) {
T temp;
temp = a;
a = b;
b = temp;
}
template <class T>
void Swap(T&a