首页 > 编程语言 >【C++篇】迈入新世界的大门——初识C++(下篇)

【C++篇】迈入新世界的大门——初识C++(下篇)

时间:2024-08-20 11:51:58浏览次数:13  
标签:下篇 rs int C++ 初识 inline const 指针

文章目录

前言

接上篇:【C++篇】迈入新世界的大门——初识C++(上篇)


引用

引用的概念和定义

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间, 它和它引⽤的变量共⽤同⼀块内存空间

⽐如:⽔壶传中李逵,宋江叫"铁⽜",江湖上⼈称"⿊旋⻛";林冲,外号豹⼦头;

在这里插入图片描述

引用就是取别名!!!

定义方式

类型&引⽤别名=引⽤对象

C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<<>>,这⾥引⽤也和取地址使⽤了同⼀个符号&,注意使⽤⽅法⻆度区分就可以。

#include <iostream>
using namespace std;
int main()
{
 int a = 0;
 // 引⽤:b和c是a的别名 
 int& b = a;
 int& c = a;
 // 也可以给别名b取别名,d相当于还是a的别名 
 int& d = b;
 ++d;
 // 这⾥取地址我们看到是⼀样的 
 cout << &a << endl;
 cout << &b << endl;
 cout << &c << endl;
 cout << &d << endl;
 return 0;
}

其实就是一个变量所在的内存空间给它起了不同的名字

在这里插入图片描述


引用的特性

  • 引用在定义时必须初始化
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再也不能引用其他实体
#include<iostream>
using namespace std;
int main()
{
    int a = 10;

    // 编译报错:“ra”: 必须初始化引⽤ 
    //int& ra;
    int& b = a;
    int c = 20;
    // 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向, 
    // 这⾥是⼀个赋值 
    b = c;
    cout << &a << endl;
    cout << &b << endl;
    cout << &c << endl;
    return 0;
}
  • 比如在链表等数据结构,就只能用指针,因为引用无法改变指向(但是在Java中就只使用引用,其引用就可以改变指向)

在这里插入图片描述


引用的使用

  • 引⽤在实践中主要是于引⽤传参和引⽤做返回值中减少拷⻉提⾼效率和改变引⽤对象时同时改变被引⽤对象
  • 引⽤传参跟指针传参功能是类似的,引⽤传参相对更⽅便⼀些。
void Swap(int& rx, int& ry)
{
    int tmp = rx;
    rx = ry;
    ry = tmp;
}
int main()
{
    int x = 0, y = 1;
    cout << x <<" " << y << endl;
    Swap(x, y);
    cout << x << " " << y << endl;
    return 0;
}
  • 引⽤返回值的场景相对⽐较复杂,我们在这⾥简单⼀下场景,还有⼀些内容后续类和对象章节中会继续深⼊讲解。

我们在数据结构介绍了栈,具体的可以看【初阶数据结构篇】栈的实现(附源码)这篇文章

我们这里要取栈顶数据,想直接改变栈顶数据的值,就可以用传引用返回

#include<iostream>
using namespace std;
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;
    int top;
    int capacity;
}ST;
void STInit(ST& rs, int n = 4)
{
    rs.a = (STDataType*)malloc(n * sizeof(STDataType));
    rs.top = 0;
    rs.capacity = n;
}
// 栈顶 
void STPush(ST& rs, STDataType x)
{
    assert(ps);
    // 满了, 扩容 
    if (rs.top == rs.capacity)
    {
        printf("扩容\n");
        int newcapacity = rs.capacity == 0 ? 4 : rs.capacity * 2;
        STDataType* tmp = (STDataType*)realloc(rs.a, newcapacity * 
                                         sizeof(STDataType));
        if (tmp == NULL)
        {
            perror("realloc fail");
            return;
        }
        rs.a = tmp;
        rs.capacity = newcapacity;
    }
    rs.a[rs.top] = x;
    rs.top++;
}
// int STTop(ST& rs)
int& STTop(ST& rs)
{
    assert(rs.top > 0);
    return rs.a[rs.top];
}
int main()
{
    // 调⽤全局的 
    ST st1;
    STInit(st1);
    STPush(st1, 1);
    STPush(st1, 2);
    cout << STTop(st1) << endl;//2
    STTop(st1) += 1;
    cout << STTop(st1) << endl;//3
    return 0;
}
  • 当然也可以用指针,但是显然引用更方便
int* STTop(ST& rs)
{
    assert(rs.top > 0);
    //return &(rs.a[rs.top - 1]);
    return rs.a + (rs.top - 1);
}

补充:

如果是写成下图左边形式->传值返回,编译会直接报错,C++在传值返回时会先把数据拷贝到一份空间中,这个叫做临时对象,C++规定临时对象具有常性,所以不能进行+=操作,会报错。而传引用返回就跳过了这一拷贝步骤,直接返回变量的别名,就可以对变量进行修改了

所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象, C++中把这个未命名对象叫做临时对象。

在这里插入图片描述

  • ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参,⽬的是简化程序,避开复杂的指针。
#include<iostream>
using namespace std;
typedef struct SeqList
{
    int a[10];
    int size;
}SLT;
 
void SeqPushBack(SLT& sl, int x)
{
    //...
}
  • 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名,这样就不需要⽤⼆级指针了,相对⽽⾔简化了程序
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}LTNode, *PNode;

//void ListPushBack(LTNode** phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PNode& phead, int x)
{
    PNode newnode = (PNode)malloc(sizeof(LTNode));
    newnode->val = x;
    newnode->next = NULL;
    if (phead == NULL)
    {
        phead = newnode;
    }
    else
    {
        //...
    }
}
int main()
{
    PNode plist = NULL;
    ListPushBack(plist, 1);
    return 0;
}

总之:引⽤和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引⽤跟其他语⾔的引⽤(如Java)是有很⼤的区别的,除了⽤法,最⼤的特点就是:C++引⽤定义后不能改变指向, Java的引⽤可以改变指向。


const引用

在这之前先看我们在C语言指针里讲过的:

#include <stdio.h>
int main()
{
	const int a = 5;
	 int* p = &a;
	 *p = 4;
	 printf("%d", a);
	return 0;
}

在这里插入图片描述

  • 在C语言里它不会报错,只会报警告,可以成功运行,这里我们通过指针修改了const修饰的变量,按理来说是不合法的,但由于C的语法检查不是那么严,所以仍然可以运行

在这里插入图片描述

  • 但如果将它改为.cpp,那就会报错了,C++的语法检查更为严格,不允许我们修改const修饰的变量

注意:

下面这种情况不存在权限放大,因为const修饰的是p5本身而不是指向的内容

int b = 5;
int* const p5 = &b;
int* p6 = p5;
*p6 = 15;

在引用中也是类似的,总结就是:权限可以缩小,不能放大

  • 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。
int main()
{
    const int a = 10;
    // 编译报错: “初始化”: ⽆法从“const int”转换为“int &” 
    // 这⾥的引⽤是对a访问权限的放⼤ 
    //int& ra = a;
    // 这样才可以 
    const int& ra = a;
    // 编译报错: “ra”: 不能给常量赋值 
    //ra++;
    
    // 这⾥的引⽤是对b访问权限的缩⼩ 
    int b = 20;
    const int& rb = b;
    // 编译报错: “rb”: 不能给常量赋值 
    //rb++;
    b++;//这是可以的
    return 0;
}

在这里插入图片描述

  • 不过需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下例如a*3的结果保存临时对象中, int& rd = d 也是类似,在类型转换中也会产⽣临时对象存储中间值,也就是时,rbrd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。
#include<iostream>
using namespace std;
int main()
{
    int a = 10;
    const int& ra = 30;

    // 编译报错: “初始化”: ⽆法从“int”转换为“int &” 
    // int& rb = a * 3;
    const int& rb = a*3;
    double d = 12.34;
    // 编译报错:“初始化”: ⽆法从“double”转换为“int &” 
    // int& rd = d;
   
    const int& rd = d;
    return 0;
}

在这里插入图片描述

const引用在传参时很有效:

如下:如果不是const传参,那除了f1(a)都要报错

void f1(const int& rx)
{
    //..
}

int main()
{
    const int xx = 20;
    int a = 10;
    const int& rb = a * 3;

    double d = 12.34;
    const int& rd = d;

    f1(xx);
    f1(a);
    f1(a*3);
    f1(d);

    return 0;
}

指针和引用的关系

C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

  • 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。
  • **引⽤在定义时必须初始化,指针建议初始化,但是语法不是必须的 **。
  • 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。
  • 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象
  • sizeof中含义不同,引⽤的结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8字节)
int main()
{
    int& r;
    int* p;

    char x = 'a';
    char& rx = x;
    cout << sizeof(rx) << endl;
    char* ptr = &x;
    cout << sizeof(ptr) << endl;

    return 0;
}
  • 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

但是引用也会存在“野引用”
如下:

int& func()
{
	int ret = 10;
	ret++;
	//...

	return ret;//函数返回后销毁,此时此块空间已经还给操作系统,不能使用
}

只是相对来说更安全一些

补充:

在第一条我们是这么说的:

语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

但在实际的底层汇编代码来看,引用也是通过指针实现的

int main()
{
	int x = 0;
	int& rx = x;
	rx += 1;

	int* ptr = &x;
	*ptr += 1;

	return 0;
}

在这里插入图片描述

知道一下就可以了

标签:下篇,rs,int,C++,初识,inline,const,指针
From: https://blog.csdn.net/2301_79849925/article/details/141341563

相关文章

  • C++类型推导
    C++11引入了auto和decltype关键字实现类型推导,通过这两个关键字不仅能方便地获取复杂的类型,还能简化书写,提高编码效率。1.auto类型推导1.1auto关键字的新意义在go语言中,在方法范围中声明的变量可以具有类型推导,例如:vari=10;//在go中自动类型推导变量i为int型而C++11......
  • C++第十一弹 -- STL之List的剖析与使用
    文章索引前言1.list的介绍2list的使用2.1list的构造函数2.2iterator的使用2.3listcapacity2.4listelementaccess2.5listmodifiers3.list的迭代器失效4.list与vector的对比总结前言本篇我们旨在探讨对于STL中list的使用,下一篇我们将会对list进行底层......
  • C++面试基础系列-volatile
    系列文章目录文章目录系列文章目录C++面试基础系列-volatile1.volatile核心规则2.C与C++中volatile区别2.1.C语言中的volatile2.2.C++中的volatile2.3.原子性和顺序2.4.易失性2.5.优化2.6.使用场景2.7.C++特有的特性2.8.C++20引入的变化(如果有)3.volatile常见面试问题4......
  • ArchLinux配置OpenCV C++环境
    本文将简单介绍在ArchLinux中安装OpenCVC++库并运行一个简单的OpenCV程序的过程。参考:https://github.com/donaldssh/Install-OpenCV我的环境最新的ArchLinuxKDEPlasma6桌面环境OpenCV4.10.0clang18.1.8gcc14.2.1安装安装以下包:sudopacman-Shdf5vtk......
  • 【C++】类与对象篇一
    【C++】类与对象篇一一.面向过程和面向对象初步认识二.类的详解1.类的引入2.类的定义3.类的访问限定符及封装(面试题)4.类的作用域5.类的实例化6.类对象模型三.结构体内存对齐规则(面试题)四.this指针1.this指针的特性2.this指针的(面试题)一.面向过程和面向对象......
  • 3142:练23.4 首字母(C、C++、python)
    3142:练23.4 首字母信息学奥赛一本通-编程启蒙(C++版)在线评测系统C源码:#include<stdio.h>#include<stdlib.h>intmain(){ charb; scanf("%c",&b); if(b=='a'){ printf("apple"); } elseif(b=='b'){ printf("ba......
  • C++运算符重载
    文章目录一、运算符重载1、规定2、operator关键词的使用二、赋值运算符的重载1、功能2、使用一、运算符重载1、规定C++允许我们对类类型使用运算符,但要我们自己通过运算符重载完成类类型的运算,如果没有对应的运算符重载就会报错。运算符重载需要使用特殊关键词......
  • 【C++】看完就会--右值引用!!!
    右值引用一、什么是右值?什么是左值?二、右值引用三、右值引用的好处四、万能引用五、完美转发一、什么是右值?什么是左值?首先,当我们看到右值的时候,我们很自然的就会产生疑问?什么的右边呢?等号的右边吗?那么如果是按赋值=符号的右边来定义的话,那么,左值是不是就是=符号......
  • 【LGR-196-Div.4】洛谷入门赛 #26 题A - H 详细题解--优化思路简洁代码(C++,Python语
    前言:    觉得这个比赛很有意思的,都是暴力题,涉及一些细节,难度比较适合刚学编程语言的,可以很好的锻炼基础还有手速,最后两题也是比较有意思,之后也准备更新atc的比赛题解和洛谷的一些高质量比赛题解(算法网瘾就是想参加各种比赛)   如果觉得有帮助,或者觉得我写的好,......
  • 生产者消费者问题-C++代码实现
    生产者消费者问题C++代码本文主要记录面试中手撕代码环节比较经常考察的生产者消费者问题,方便后续巩固和查看#include<iostream>#include<thread>#include<mutex>#include<condition_variable>#include<queue>#include<functional>usingnamespacestd;classProd......