C++——入门基础(下)

目录

一、引用

(1)引用的概念和定义

(2)引用的特性

(3)引用的使用

(4)const引用

(5)指针和引用的关系

二、inline

三、nullptr

四、写在最后


一、引用

(1)引用的概念和定义

引用就是给已经存在的变量取别名,而且编译器不会为引用变量开辟内存空间,它和被引用的变量公用同一块内存空间。

类型& 引用别名 = 引用对象。

#include <iostream>
using namespace std;

int main()
{
    int a = 0;
    int& b = a;//b是a的别名
    int& c = b;//也可以给别名再取别名:c是b的别名

    ++c;
    cout << &a << endl;
    cout << &b << endl;
    cout << &c << endl;
    //取地址之后我们看到这几个地址是一样的
    //也就是说:这几个别名共用一个地址
    
    return 0;
}

 

在C++中,为了避免引入太多的运算符,会复用前面的一些符号,比如<<和>>,以及&,需要从使用方法的角度进行区分。

(2)引用的特性

①引用在定义时必须初始化;

②一个变量可以有多个引用;

③一旦引用一个实体,就不能再引用其他实体。

#include <iostream>
using namespace std;
int main()
{
    int a = 10;
    int c = 20;

    //int& a;
    //编译报错:引用在定义时必须初始化  
    
    int& b = a;
      
    b = c;
    //这里不是让b引用c,因为引用不能改变指向,因此这里是赋值

    cout << &a << endl;
    cout << &b << endl;
    cout << &c << endl;
    //可以看出a和b的地址一样,c与之不同

    return 0;
}

(3)引用的使用

①引用在实践中主要在传参引用做返回值时减少拷贝并提高效率、改变引用对象的同时改变被引用对象;

//减少拷贝并提高效率:比如在栈的初始化时,直接引用结构体,减少了拷贝
//因为如果数组a过大就会造成大量拷贝
#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;
    STTop(st1) += 10;

    cout << STTop(st1) << endl;
 
   return 0;
}
//改变引用对象的同时改变被引用对象
#include <iostream>
using namespace std;

void swap(int& ra, int& rb)
{
    int tmp = ra;
    ta = rb;
    rb = tmp;
}

int main()
{
    int x = 0;
    int y = 1;
    cout << x << " " << y << endl;

    swap(x,y);
    cout << x << " " << y << endl;

    return 0;
}

引用传参指针传参的功能是类似的,但是引用传参相比来说更方便;

引用指针在实践中相辅相成,功能具有重叠性,但是各有特点,不能相互替代;

④C++的引用和其他语言的引用有很大区别,最大的不同是:C++的引用不能改变指向,Java的引用可以改变指向。

#include<iostream>
using namespace std;

// ⼀些主要⽤C代码实现版本数据结构教材中,使⽤C++引⽤替代指针传参
//⽬的是简化程序,避开复杂的指针
typedef struct SeqList
{
    int a[10];
    int size;
}SLT;
 

void SeqPushBack(SLT& sl, int x)
{

}
typedef struct ListNode
{
    int val;
    struct ListNode* next;
}LTNode, *PNode;




// 指针变量也可以取别名,这⾥LTNode*& phead就是给指针变量取别名 
// 这样就不需要⽤二级指针了,相对⽽⾔简化了程序 
//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;
}

(4)const引用

①在引用时可以引用const对象,但必须用const引用;

int main()
{
    const int a = 10;
    //错误!这里的引用是对a访问权限的放大
    //int& ra = a;

    //正确:使用const引用
    const int& ra = a;

    //错误:不能给常量复制
    //ra ++;

    return 0;
}

②const引用可以引用const对象,也可以引用普通对象,因为在引用时对象的访问权限可以缩小,但不能放大;

int main()
{
    //const引用可以引用普通对象
    int b = 10;
    const int& rb = b;//这里是对b访问权限的缩小

    //const引用可以引用const对象
    const int c = 20;
    const int& rc = c;

    return 0;
}

③需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产生临时对象存储中间值,也就是是,rb和rd引用的都是临时对象,而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;
}

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

(5)指针和引用的关系

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

②在定义时:引用必须初始化;指针建议初始化,但是语法上不是必须的。

③引用在初始化时引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象

④在访问对象时:引用可以直接访问指向对象;指针需要解引用才能访问指向对象。

⑤在sizeof中的含义不同:引用结果为引用类型的大小;但指针始终是地址空间所占字节个数(32位平台下 占4个字节,64位下是8byte)。

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

二、inline

①用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,可以提高效率。

②inline对于编译器而言只是⼀个建议,也就是说:即使加了inline,编译器也可以选择在调用的地方不展开,不同编译器对于inline的展开情况各不相同,因为C++标准没有规定。

#include<iostream>
using namespace std;

inline int Add(int x, int y)
{
    int ret = x + y;
    ret += 1;
    ret += 1;
    ret += 1;
 
    return ret;
}


int main()
{
    // 可以通过汇编观察程序是否展开 
    // 有call Add语句就是没有展开,没有就是展开了 
 
    int ret = Add(1, 2);
    cout << Add(1, 2) * 5 << endl;
 
    return 0;
}

③inline适用于频繁调用短小函数,对于递归函数和代码相对多⼀些的函数,加上inline也会被编译器忽略。

④C语言实现的宏函数也会在预处理时替换展开,但是宏函数实现很复杂、很容易出错,且不方便调试,C++设计inline的目的就是替代C语言的宏函数。

#include<iostream>
using namespace std;

// 实现⼀个ADD宏函数的常⻅错误
//#define ADD(int a, int b) return a + b;
//#define ADD(a, b) a + b;
//#define ADD(a, b) (a + b)

// 正确的宏实现 
#define ADD(a, b) ((a) + (b))

int main()
{
    int ret = ADD(1, 2);
    cout << ADD(1, 2) << endl;//为什么不能加分号?
    cout << ADD(1, 2)*5 << endl;//为什么要加外⾯的括号?
    int x = 1, y = 2;
    ADD(x & y, x | y); //为什么要加⾥⾯的括号?
    // -> (x&y+x|y)

    return 0;
}

1.为什么不能加分号? 

比如:cout << ADD(1, 2) << endl;

如果宏函数加了分号就会变成  cout <<((1)+(2));<< endl;   会导致提前结束语句。

2.为什么要加外面的括号? 

比如:cout << ADD(1, 2)*5 << endl;

如果宏函数没有加外面的括号就会变成 cout << (1)+(2)*5 << endl;   与原始结果cout << ((1)+(2))*5 << endl;   不同。

3.为什么要加里面的括号?  

比如:ADD(x & y, x | y);

如果宏函数没有里面的括号就会变成(x & y + x | y) ,原始结果((x & y)+ (x | y))不同。

注:+的优先级要高于&和|,那么x & y + x | y = x & (y + x) | y。


⑤vs编译器在debug版本下默认是不展开inline的,这样方便调试,但如果在debug版本想展开需要设置⼀下以下两个地方:

⑥inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline一旦被展开,就没有函数地址,链接时会出现报错。

// F.h
#include <iostream>
using namespace std;

inline void f(int i);


// F.cpp
#include "F.h"
void f(int i)
{
    cout << i << endl;
}


// main.cpp
#include "F.h"
int main()
{
    // 链接错误:⽆法解析的外部符号 "void __cdecl f(int)" (?f@@YAXH@Z) 
    f(10);
 
    return 0;
}

正确做法:直接定义不声明。

三、nullptr

 NULL实际是⼀个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
    #ifdef __cplusplus
        #define NULL 0
    #else
        #define NULL ((void *)0)
    #endif
#endif

(1)NULL在C++中可能被定义为字面常量0,在C中被定义为无类型指针(void*)的常量。

不论采取何种定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过 f(NULL) 调用指针版本的 f(int*)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。所以f((void*)NULL); 调用时会报错。 

(2)C++11中引入了nullptr,nullptr 是⼀个特殊的关键字,也是⼀种特殊类型的字面量,它可以转换成任意其他类型的指针类型

(3)使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。

#include<iostream>
using namespace std;

void f(int x)
{
    cout << "f(int x)" << endl;
}

void f(int* ptr)
{
    cout << "f(int* ptr)" << endl;
}


int main()
{
    f(0);    
    // 本想通过f(NULL)调⽤指针版本的f(int*)函数
    //但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖
    f(NULL);
    f((int*)NULL);

    // 编译报错:error C2665: “f”: 2 个重载中没有⼀个可以转换所有参数类型 
    // f((void*)NULL);
 
    f(nullptr);
 
    return 0;
}

四、写在最后

至此基础已学完。敬请期待“类和对象”~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/873770.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

带相对位置表示的自注意力(201803)

Self-Attention with Relative Position Representations 带相对位置表示的自注意力 https://arxiv.org/pdf/1803.02155v1 Abstract Relying entirely on an attention mechanism, the Transformer introduced by Vaswani et al. (2017) achieves state-of-the-art results …

【加密社】比特币海量数据问题解决方案

加密社 比特币是无敌的存在&#xff0c;刚翻了一遍中本聪的论文&#xff08;其实以前看过一次&#xff0c;那时不明觉厉&#xff09;&#xff0c;发现咱们一直在考虑的问题&#xff0c;基本都能在其论文上找到解决方案了。。 现在出现的这些问题&#xff0c;完全是因为bitcoin…

4千6历年高考英语试题大全ACCESS\EXCEL数据库

《历年高#考英语试题大全ACCESS数据库》搜集了大量的全#国各#地高#考英语模拟试题&#xff0c;每道题目均有相应的答案和解析&#xff1b;这种数据虽然没有《一站到底》类的数据结构&#xff08;一个选项一个字段&#xff09;那么好&#xff0c;但是通过技术人员还是可以很简单…

自适应中值滤波器:图像去噪的高效解决方案

在数字图像处理中&#xff0c;椒盐噪声是常见的干扰之一&#xff0c;它会导致图像出现随机的黑点和白点&#xff0c;严重影响图像质量。传统的中值滤波器虽然在一定程度上能够去除这种噪声&#xff0c;但可能无法完全恢复图像的细节。为此&#xff0c;本文将介绍一种自适应中值…

k8s上搭建devops环境

一、gitlab 1.安装gitlab # 下载安装包 wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm # 安装 rpm -i gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm # 编辑 vi /etc/gitlab/gitlab.rb 文件 # 修改 external_url 访问路径 htt…

【网络安全】分析JS文件实现账户接管

未经许可,不得转载。 文章目录 正文正文 网站使用的是简单的OTP(一次性密码)验证机制,通过用户注册时提供的电子邮件发送邮箱验证码。在功能有限的情况下,我选择去分析网站加载的JavaScript文件。 我发现了一个名为 saveJobseekerPasswordInCache 的函数: 这个函数虽然…

vscode侧边工具栏不见了找回方法

有时候因为误操作&#xff0c;vscode编辑器里面的侧边工具栏不见了找回方法&#xff0c;请按照以下步骤操作。 例:1&#xff1a;这个工具栏不见了 方法&#xff1a;菜单栏点击文件》点击首选项》点击设置》点击工作台》点击外观》勾选如下图选项 例如2&#xff1a;蓝控制台底…

无人机之穿越机的飞行模式

穿越机的飞行模式主要分为两种基本类型&#xff1a;自稳模式&#xff08;ANGLE MODE&#xff09;和手动模式&#xff08;ACRO MODE&#xff09;&#xff0c;以及一些衍生的飞行模式&#xff0c;如半自稳模式&#xff08;Horizon Mode&#xff09;等。下面将详细介绍这两种基本模…

vulhub think PHP 2-rce远程命令执行漏洞

1.开启环境 2。访问对应网站端口 3.这里我们直接构造payload&#xff0c;访问phpinfo() http://192.168.159.149:8080/?s/Index/index/L/${phpinfo()} 4.可以访问到我们的phpinfo&#xff0c; 所以写入一句话木马&#xff0c;也可使用蚁剑进行连接&#xff0c;获得其shell进…

云计算之大数据(下)

目录 一、Hologres 1.1 产品定义 1.2 产品架构 1.3 Hologres基本概念 1.4 最佳实践 - Hologres分区表 1.5 最佳实践 - 分区字段设置 1.6 最佳实践 - 设置字段类型 1.7 最佳实践 - 存储属性设置 1.8 最佳实践 - 分布键设置 1.9 最佳实践 - 聚簇键设置 1.10 最佳实践 -…

AT3340-6T杭州中科微BDS定位授时板卡性能指标

AT3340-6T是一款高性能多系统卫星定位安全授时板卡&#xff0c;可通过配置支持各个单系统的定位授时。 外观尺寸&#xff1a; 电气参数 应用领域&#xff1a; 通信基站授时 电力授时 广播电视授时 轨道系统授时 金融系统授时 其他授时应用 注意事项&#xff1a; 为了充分发挥…

Linux入门攻坚——31、rpc概念及nfs和samba

NFS&#xff1a;Network File System 传统意义上&#xff0c;文件系统在内核中实现 RPC&#xff1a;函数调用&#xff08;远程主机上的函数&#xff09;&#xff0c;Remote Procedure Call protocol 一部分功能由本地程序完成 另一部分功能由远程主机上的 NFS本质…

软件部署-Docker容器化技术

开始前的环境说明 VMware 17 Pro Centos release 7.9.2009(防火墙已关闭) Docker 26.1.4 Docker镜像加速器配置:"https://do.nark.eu.org", "https://dc.j8.work", "https://docker.m.daocloud.io", "https://dockerproxy.com", &…

2. c#从不同cs的文件调用函数

1.文件目录如下&#xff1a; 2. Program.cs文件的主函数如下 using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms;namespace datasAnalysis {internal static class Program{/// <summary>…

HUAWEI华为MateBook B5-420 i5 集显(KLCZ-WXX9,KLCZ-WDH9)原装出厂Windows10系统文件下载

适用型号&#xff1a;KLCZ-WXX9、KLCZ-WDH9 链接&#xff1a;https://pan.baidu.com/s/12xnaLtcPjZoyfCcJUHynVQ?pwdelul 提取码&#xff1a;elul 华为原装系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、华为浏览器、Office办公软件、华为…

网络传输的基本流程

目录 0.前言 1.TCP/IP四层协议模型的认识 2.数据传输的大致流程 3.局域网通信的原理 4.同一网段下两台主机之间的通信 5.不同网段下两台主机之间的通信 0.前言 不知道你有没有这样的疑问&#xff0c;为什么不同的设备之间能够进行数据的发送和接收&#xff1f;不同的通信…

计算机毕业设计选题推荐-农家乐综合服务系统-乡村游乐购系统-田园休闲生活系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

基于微信小程序+Java+SpringBoot+Vue+MySQL的网上花店/鲜花销售小程序

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的网上花店/鲜花销售…

【计算机网络】socket编程 --- 实现简易TCP网络程序

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

【北京迅为】《STM32MP157开发板使用手册》- 第十二章 编译Linux内核

iTOP-STM32MP157开发板采用ST推出的双核cortex-A7单核cortex-M4异构处理器&#xff0c;既可用Linux、又可以用于STM32单片机开发。开发板采用核心板底板结构&#xff0c;主频650M、1G内存、8G存储&#xff0c;核心板采用工业级板对板连接器&#xff0c;高可靠&#xff0c;牢固耐…