Learn C++学习笔记:第六章— 常量指针、引用变量和for-each循环

指向常量的可变指针和常量指针

之前介绍的指针都是指向变量的可变指针。但是除了变量还有常量,所以指针的类型也有针对常量的指针。
大概可以把指针分为三类:

  • 指向变量指针:指针本身指向变量,指针自身也是可变的。
  • 指向常量指针:指针可以指向常量也能指向变量,并且指针的指向是可变。
    指向常量:
const int value = 5;
const int *ptr = &value;
*ptr  = 6;       // Error

指向变量:

int value = 5 ;
const int *ptr = &value;
value = 6     //值可变
  • const指针:可以指向常量或者变量,但是只能初始化一次。
    主要用于函数的参数中,确保函数不会无意中更改传入的参数。
int value = 5;
int value2 = 7;
int * const ptr = &value;
*ptr = 6 ;        // OK
ptr = &value2;    // Error

在这里插入图片描述

引用变量

之前已经介绍了C++的两种变量类型:①普通变量;②指针变量。
本节介绍第三种变量:③引用变量。
指针是变量的地址,引用是变量的“小名”,也就是原变量的小名,两个变量同指一个地址一个数据。
定义格式为int & 变量 = XXXX。这个&不是取地址的含义,位置也不一样,专指定义引用。
示例:

#include <iostream>
int main() {
    int a = 5;
    int& b = a;
    std::cout << &a << '\n'; // 取a的地址
    std::cout << &b << '\n'; // 取b的地址
    return 0;
}

输出:
0x7fff5fbff80c
0x7fff5fbff80c
变量和它的引用变量指向相同的地址。

关于引用变量有如下需要注意点:

  1. 引用必须被初始化,指针不必。
    引用本来就是别的变量的小名,所以定义时候必须被初始化。
int &ref   //Error
  1. 一旦与某个变量关联起来,就只能是这个变量的引用。指向不能改变,但是值可以改变。
#include <iostream>
int main(int argc, const char * argv[]) {
    int a = 5;
    int& b = a;

    int c = 20;
    b = c;      //并不是修改b的指向,而是把c的值20赋给变量b和a
    cout << a << endl; 
    cout << b << endl; 
    return 0;
}
  1. 不存在指向空值的引用,但是存在指向空值的指针。

左值和右值

左值:所有具有地址的值,也就是赋值符号=左边的值,=左边的因为是要把变量赋过去,所以必须要有地址,比如5=6,这显然错误。
所以全部的普通变量,指针变量这些都是左值。
右值:可以分配给左值的任何值。比如变量,比如6这样的数字,比如表达式6+7等。
示例:

x = x +1

这条语句中=左边的x属性是左值,主要利用其地址,右边的是右值,主要用它的值。

const引用

跟常量指针一样,引用变量也有const引用。
只需要在引用前添加关键字const即可。const引用可以引用普通变量也可以引用常量,只不过引用普通变量后,依然不能对其赋值。
示例:

int x = 5;
const int &ref1 = x; // okay, x is a non-const l-value
x = 8;               //ok
ref1 = 9;            //Error
 
const int y = 7;
const int &ref2 = y; // okay, y is a const l-value
 
const int &ref3 = 6; // okay, 6 is an r-value

引用的作用

此处有参考博文C++ | 引用变量
引用最常用作功能参数,好处是省时间、省空间。大家都知道,我们平时传参时,都需要将原来的变量拷贝一份至一个临时变量,再将这个临时变量作为形参传入函数,但现在不需要了,因为从始至终,都是原来的那个变量。
别看一个小小的原生类型占不了多少空间,复制一份也用不了多少时间,但是当我们传的是一个自定义变量,一个十分巨大的结构体或类的时候呢?,我们不需要去占用空间拷贝,这省了很多时间与空间。所以比较适合传递结构体和类。
通过(const)reference传递非指针,非基本数据类型的变量(例如struct)。
如果给定任务可以通过引用或指针解决,则通常应首选引用。指针仅应在引用不足的情况下使用(例如,动态分配内存)。
但同时也要注意,如果这时候您不希望改变原值,记得加上 const 来修饰。
示例:
在这里插入图片描述
上面这个程序里,c 是一个临时变量,而 Add 函数的返回值并不是 c ,因为出了这个函数, c 就相当于不在了,所以他会先将 c 的值复制给另一个临时变量(如果较小是寄存器,较大则是提前开辟好的一块空间),这个临时变量的生存周期比较长,能将其值复制给 ret 变量。

这样就存在一个效率问题,也就是多复制了一步。别看这里只是32位平台上八个字节的 double 变量而已,但如果是一个极大的结构体,就会浪费很多时间和空间。但是返回引用变量能很好地解决这个问题。
在这里插入图片描述
a 自增以后再返回其值,利用引用,这时候并不需要考虑生命周期的问题,因为来来回回都是在堆那一块空间进行修改,一直在 a / ra 的作用域内,省去了再复制一步的时间和空间。

类或结构指针的成员读取

对于引用,可以用.符号去读取成员数据。

struct Person
{
    int age;
    double weight;
};
Person person; // define a person
 
// Member selection using reference to struct
Person &ref = person;
ref.age = 5;

但是指针用.符号去读就会比较不好看:

struct Person
{
	int age;
	int weight;
};
Person person;
Person * ptr = &person
(*ptr).age = 5

由于运算符优先级的关系,必须添加()符号,(*ptr)不易读,所以c++提供了一种等价但易读的符号->
以下两句话等价:

(*ptr).age = 5;
ptr->age = 5;

for-each循环

有了上面的概念介绍,就可以介绍C++的另外一种循环:for-each循环。
它的作用跟python里面的for...in...的作用一样,不是根据序号去索引元素,而是遍历元素,没有序号的概念。
语法示例:

#include <iostream>
 
int main()
{
    int fibonacci[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
    for (int number : fibonacci) // iterate over array fibonacci
       std::cout << number << ' '; // we access the array element for this iteration through variable number
 
    return 0;
}

使用注意点:
①因为for里面要对元素声明类型,这个时候就是auto关键字绝佳的使用场景,不管什么都可以无脑写auto
②使用引用

int array[5] = { 9, 7, 5, 3, 1 };
for (auto element: array) 
	std::cout << element << ' ';

这意味着迭代的每个数组元素都将被复制到变量元素中。复制数组元素可能会很昂贵,而且在大多数情况下,我们实际上只是想引用原始元素。幸运的是,我们可以使用引用将代码改为:

int array[5] = { 9, 7, 5, 3, 1 };
for (auto& element: array) 
	std::cout << element << ' ';

在上面的示例中,element将是对当前迭代数组元素的引用,从而避免进行复制。同样,对element所做的任何更改都将影响正在迭代的数组,如果element是一个普通变量,那么这是不可能的。
如果避免对元素的修改,可以添加const关键字变成只读。

int array[5] = { 9, 7, 5, 3, 1 };
for (const auto& element: array) 
	std::cout << element << ' ';

③有时候数组会退化为指针,这个时候for-each不能起作用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值