概述
指向常量的可变指针和常量指针
之前介绍的指针都是指向变量的可变指针。但是除了变量还有常量,所以指针的类型也有针对常量的指针。
大概可以把指针分为三类:
- 指向变量指针:指针本身指向变量,指针自身也是可变的。
- 指向常量指针:指针可以指向常量也能指向变量,并且指针的指向是可变。
指向常量:
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
变量和它的引用变量指向相同的地址。
关于引用变量有如下需要注意点:
- 引用必须被初始化,指针不必。
引用本来就是别的变量的小名,所以定义时候必须被初始化。
int &ref //Error
- 一旦与某个变量关联起来,就只能是这个变量的引用。指向不能改变,但是值可以改变。
#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;
}
- 不存在指向空值的引用,但是存在指向空值的指针。
左值和右值
左值:所有具有地址的值,也就是赋值符号=
左边的值,=
左边的因为是要把变量赋过去,所以必须要有地址,比如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
不能起作用。
最后
以上就是舒心学姐为你收集整理的Learn C++学习笔记:第六章— 常量指针、引用变量和for-each循环指向常量的可变指针和常量指针引用变量的全部内容,希望文章能够帮你解决Learn C++学习笔记:第六章— 常量指针、引用变量和for-each循环指向常量的可变指针和常量指针引用变量所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复