c++入门学习

c++的学习
包含:c++语法
SLT+数据结构
在这里插入图片描述

1. 简单介绍c++

  • c语言是结构化和模块化的语言,适合处理较小规模的程序,对于复杂的程序,需要高度抽象和建模时,c语言不适合,为了解决软件危机,10世界80年代,计算机界提出了OOP(object oriented programming:面向对象)的思想,支持面向对象的程序设计应用而生。
  • 1982年,Bjarne Stroustrup博士在c语言的基础上引入并扩充了全面对象的概念,发明了一种新的程序语言,为了表达该语言与c语言的渊源关系,命名为c++,因此:c++是基于c语言而设计的,它既可以进行c语言的过程化程序设计,又可以进行抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计

1. c++的兼容性

c++兼容90%的c语言
在这里插入图片描述
在cpp文件下,我们依旧可以使用大部分c语言的东西

2. c++的关键字

这里只是看看c++的关键字,不进行具体学习,在后面学习用到的时候会具体学
c++一共有63个关键字,其中有32个是c语言的

asmdoifreturncotinue
tryautodoubleinlinetypedef
forshortbooldynamic_castint
longsignedtypeidpublicbreak
elsesizeoftypenamethrowcase
enummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigned
defaultcharexportnewstruct
usingfriendclassexternoperator
switchvirtualresigsterconstfalse
privatetemplatevoidtrueconst_cast
floatprotecctedthisvolatilewhile
deletegotoreinterpret_cast

2. 语法学习

2.1. 输出"hello world"

#include<iostream>
using namespace std;

int main()
{
	cout << "hello world" <<endl;
	return 0;
}

在这里插入图片描述

2.2. 命名空间

首先先看c语言实现的这段代码

#include<stdio.h>
int rand=0;
int main()
{
		printf("hello world\n");
		printf("%d\n",rand);
		return 0;
}

在这里插入图片描述
正常输出没问题,但是如果加入 stdlib.h 的头文件呢
在这里插入图片描述
这个时候我们的代码就会出现问题
原因是命名冲突

  • 在 stdlib.h 的头文件中,有一个 rand 函数,如果我们再定义一个 rand 变量,就会产生命名冲突
  • 如果和别人合作的项目中,我和别人定义的变量名或函数名重复时,也会出现命名冲突的情况
  • 命名冲突的情况在c语言中无法解决,只能由一方改变变量或函数名
    但是在c++中,有个命名空间的概念
#include<stdio.h>
#include<stdlib.h>

namespace xsz
{
    int rand=0;
}
int main()
{   
    printf("hello world\n");
    
    printf("%p\n",rand);
    
    return 0;
}

在这里插入图片描述在这里插入图片描述

这里的 namespace 是命名空间,写法类似结构体,但是末尾没有“;”,命名空间的作用是,在系统检测名称时,如果没有使用指定命名空间内的名字,系统不会进入命名空间内检查,因此在命名空间内外,可以出现不同的变量名
在这里插入图片描述

  • 在命名空间内定义的变量名,想当与用一块地用墙封起来
  • 对于上面的结构,因为我们将 rand 变量放在xsz的命名空间内,系统在检测的时候,绕过了命名空间,直接将 rand 函数作为 printf 的输出对象,而函数名代表函数地址,所以这里输出的是 rand 的地址
namespace xsz
{
    int rand=0;
}

如果我们想要使用xsz命名空间的内容,我们需要用下面的语句

  xsz::rand
#include<stdio.h>
#include<stdlib.h>
namespace xsz
{
        int rand = 0;
}
int main()
{
        printf("hello world\n");

        printf("%d\n",xsz:: rand);

        return 0;
}

在这里插入图片描述

加入后,我们就可以使用xsz命名空间内的变量
“ :: ” ------域作用限定符
这个符号相当于钥匙,在使用这个符号时,系统会进入指定的命名空间,查找指定的变量名
如果我和别人都要使用这个变量名,我们可以设置两个命名空间

  • 下面代码就设置了xsz和zss两个命名空间
    两个 printf 输出指定空间 rand 变量,不会发生命名冲突
    在这里插入图片描述
  • 同时,命名空间内不仅可以定义变量,还可以定义函数,结构体
namespace xsz
{
        int rand = 1;

        int Add(int left, int right)
        {
                return left + right;
        }

        struct A
        {
                struct Node* node;
                int val;
        };
}

在这里插入图片描述
在这里插入图片描述
和上面 rand 一样,如果我们想要调用 xsz 命名空间内的 Add 函数,需要先 xsz:: 指定一下才能使用,否则系统找不到 Add 函数,就会报错

  • 如果是向使用命名空间内的结构体
namespace xsz
{    
    struct A
    {
        struct Node* next;
        int val;
    };
}
xsz::struct Node*node=NULL;//正确
struct xsz::Node*node=NULL;//错误

在这里插入图片描述
命名空间内的结构体,需要指定的是结构体的名字,而不是指定 struct,所以 域作用限定符 应该在 名字A 前,而不是 struct 前

  • 如果命同一个命名空间内需要出现腾讯的变量名,命名空间可以进行嵌套处理
#include<stdio.h>
#include<stdlib.h>
//xsz命名空间内嵌套zss命名空间
namespace xsz
{
        namespace zss
        {
                int rand = 3;
        }
        int rand = 1;

        int Add(int left, int right)
        {
                return left + right;
        }

        struct A
        {
                struct Node* node;
                int val;
        };
}

在这里插入图片描述
可以在指定命名空间内,指定命名空间,套娃,但是可以,虽然不是很建议这么套娃写
对于多人实现额的同一类结构,如栈,容易出现命名相同的情况
我们就可以使用命名空间将各自的代码分开

namespace Stack
{
        typedef struct stack
        {
                int* a;
                int size;
                int capacity;
        }ST;

        void StackInit(ST* pst);
        void StackPush(ST* pst, int x);
}
#include"Stack.h"
namespace Stack
{
        void StackInit(ST* pst)
        {
                pst->a = NULL;
                pst->size = 0;
                pst->capacity = 0;
        }
        void StackPush(ST* pst, int x)
        {
            //....
        }
}
#include"Stack.h"

int main()
{
         Stack::ST st;
         Stack::StackInit(&st);
         Stack::StackPush(&st, 1);
         Stack::StackPush(&st, 2);
         Stack::StackPush(&st, 3);
         Stack::StackPush(&st, 4);
         Stack::StackPush(&st, 5); 

        return 0;
}

按照上面的写法,可以保证我的栈,在我的命名空间内,只有指定我的命名空间才能使用,这样就能避免和别人出现相同函数或变量的名字的问题。

如果仅仅是自己做测试,上面不断指定命名空间未免太麻烦,因此,在不是合作做项目,在自己能确保不出现冲突的情况下,可以使用下面的代码展开命名空间

using namespace xsz;
#include"Stack.h"
using namespace Stack;
int main()
{
         ST st;
         StackInit(&st);
         StackPush(&st, 1);
         StackPush(&st, 2);
         StackPush(&st, 3);
         StackPush(&st, 4);
         StackPush(&st, 5); 

        return 0;
}

c语言中,包含的头文件会在预编译阶段,将头文件中的代码全部复制拷贝过来
而上面 using namespace xsz 是将xsz这块地所有墙在该文件中拆了,可以直接使用,但是和头文件类似,只能放在需要调用命名空间内容的前面,放在后面检测不到。

2.3. 输入输出

  • 输出
#include<iostream>

int main()
{
    cout<<"hello world"return 0;
}

如果我们没有加 指定std命名空间的域作用限定符,系统就检测不到,因此会报错

想要直接使用,需要加上

using namespace std;

std 是c++官方库的命名空间,在第一点中的输出"hello world"我们就使用了这个,因为c语言的输出,输入都需要用到库函数,所以在那段代码中,我们直接将 std 命名空间展开

  • 在工程代码里,不要展开std,容易与库函数发生冲突
  • 日常练习,为了方便,可以展开

与c语言同样,c++一般包含 iostream 的库函数,如果想使用,和c一样,直接 #include ,不过需要注意的是,c++的头文件,没有 “.h”

在这里插入图片描述
cout后面的 "<<"是流插入运算符,可以认为后面的内容流向 cout
cout console out(控制台输出)

同时 cout 相较于 printf 还有些优点

  • cout会自动识别数据类型
    在这里插入图片描述
    b为double类型,a为int类型,cout输出的时候会按照元素类型输出,不像printf 需要自己控制输出类型
    换行可以采用c语言中的 ‘\n’
    在这里插入图片描述
    而在 c++中 我们有其他方式换行 加入 endl 可换行
#include<iostream>

int main()
{
        std::cout << "hello world\n";
        int a = 10;
        double b = 11.11;
        std::cout << a<<"\n";
        std::cout << b<<"\n";
        std::cout << a << "\n"<< b << "\n";
        std::cout << a << std::endl;
        std::cout << b << std::endl;
        std::cout << a << std::endl << b << std::endl;
        return 0;
}

在这里插入图片描述
但是像这样,每次制定命名空间都很不方便,直接展开又有冲突的风险
可以采用下面的方法

using std::cout;
using std::endl;

这在main外面,可以直接指定可以使用哪些库函数
这样,代码看起来能更简洁点
在这里插入图片描述
项目内建议采用上面的方法

  • 输入 cin
    console in cin
  using std::cin;
  #include<iostream>
using std::cout;
using std::endl;
using std::cin;
int main()
{
       cout << "hello world\n";
       int a = 10;
       double b = 11.11;
       cin >> a >> b;
       cout << a << endl << b << endl;

       return 0;
}

cin 就能像scanf 一样,输入2个值在这里插入图片描述

通用cin 也会判断数据类型
在这里插入图片描述
c++也可以控制精度,不过很麻烦,但是c++本身兼容c,所以控制精度我们可以使用c语言控制精度的方法

printf("%.2f");

2.4. 缺省参数

在c语言中

void Func(int a)
{
	return a;
}
int main()
{
    Func();
    Func(10);
    
    return 0;
}

main中的第一种写法会有问题,因为没有传入参数

但是在c++中

void Func(int a=0)
{
    cout<< a << endl;
}
int main()
{
    Func();
    Func(10);
    
    return 0;
}

我们可以将函数中传入参数的变量,在定义的时候赋值,如果该位置传入参数的话,采用传入的参数,没有传入参数,采用默认的值
所以上面两种写法都正确。

例如:
如果想在栈内输入数据,如果我们不知道需要输入多少数据

int main()
{
        ST st;
        StackInit(&st);
        //不知道插入多少数据
        StackPush(&st,1);
        StackPush(&st,2);
        StackPush(&st,3);
        StackPush(&st,4);
        StackPush(&st,5);
        
        return 0;
}

需要一个一个入,并且还会造成空间浪费
但是当我们知道需要向栈内传入多少值的话,如果我奶稍微改变一下初始化栈的函数

	void StackInit(ST* pst, int n=4);
        void StackInit(ST* pst,int n=4)
        {
                pst->a = (int*)malloc(n * sizeof(int));
                pst->size = 0;
                pst->capacity = 0;
        }

如果在调用的时候,只传入栈的地址,系统默认会开辟4sizeof(int)的空间。
如果在调用的时候,传入了100作为n,此时就不再是默认的4,系统就会开辟100sizeof(int)的空间。
但是如果定义和声明中都出现了 int n =4 ,就会报错
在这里插入图片描述
如果缺省参数在声明和定义中都出现,并且都有赋的值,假如声明和定义提供的值不相同,编译器无法判断该用哪个地方的缺省参数

  • 如果想要使用缺省参数,只能在声明中给,不能再声明和定义中同时给出来
    在这里插入图片描述
  1. 全缺省:所有参数都是缺省值
  2. 半缺省:缺省部分参数(缺省值只能从右往左给,必须是连续的)
#include<iostream>
using namespace std;

void Func(int a = 10, int b = 20, int c = 30)//全缺省
{
        cout << "a=" << a <<endl;
        cout << "b=" << b <<endl;
        cout << "c=" << c <<endl<<endl;
}
void Func(int a,int b=20, int c= 30)//半缺省,缺省的值从右往左
{
  //...
}

int main()
{
        Func();
        Func(1);
        Func(1,2);
        Func(1,2,3);
        return 0;
}

调用第一个Func函数
在这里插入图片描述
上面的传递方式是从左往右传
但是不能直接传第几个数

Func( ,2 ,);

这样传参数就不行

2.5. 函数重载

自然语言中,一个词有多个定义可以通过上下文来判断词的真实含义
概念:是函数的一种特殊情况,c++允许在同一作用域中,声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数,类型,类型顺序)不同,常用来处理功能类似,数据类型不同的问题
在这里插入图片描述
在c语言中,不能出现相同的函数名,因此上面代码会报错
但是在c++中,可以出现相同的函数名
不过都有点区别:
参数不同(类型不同,个数不同)返回值可同可不同
比如下面的代码:

#include<iostream>
using namespace std;
void Add(int a,int b)
{
	cout<<a+b<<endl;
}
void Add(double a,double b)
{
	cout<<a+B<<endl;
}

int main()
{
	Add(1,2);
	Add(1.2,2.3);
	return 0;
}

程序会根根据Add函数不同的传入参数,调用不同的函数
在这里插入图片描述
使用时注意下面的情况:

  • 这样会使函数判断类型变的严格
    在两个Add的情况下,如果同时传入 int 和 double 类型的数据时,程序会报错
    在这里插入图片描述
    但是只留下一个Add却可以通过
    在这里插入图片描述
    只存在一个Add时,不存在歧义
  • 缺省参数和函数重载的结合
void f()
{
	cout << "f()" << endl;
}
void f(int a=0)
{
	cout << "f(int a)" << endl;
}

上面两个构成重载函数,但是使用时存在很大的问题
在这里插入图片描述
使用对 f(1) ,1作为传入参数,函数必须有一个接收的值,此时有两个 f 函数,就会进入 f(int a=10) 这个函数。
但是使用 f() ,就会发生歧义

f() 可以看做是调用函数 f();
也可以看做是调用 f(int a=10),只不过这里没传入参数,直接使用的是默认值;

因此我们无法调用 f() 这个函数

  • 如果函数仅仅是返回值不同,不能构成重载
int f()
{
	return 0;
}
double f()
{
	return 1.1;
}

如果这样写,程序会报错
在这里插入图片描述

2.6. c++为什么支持重载函数

  • 程序在编译的过程中第一个步骤–预处理
    预处理包括:头文件展开,宏替换,条件编译,去掉注释(4个主要步骤,头文件展开是最重要的)
    下面有三个文件
    Func.h
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

void f(char b, int a);
void f(int b, char a);

Func.cpp

#include"Func.h"

void f(char b, int a)
{
        cout << b<< a << endl;
}

void f(int b, char a)
{
        cout <<  b<< a << endl;
}

test.cpp

int main()
{
        f(1, 'x');
        f('x',1);
        return 0;
}
  • 头文件展开,是将头文件的内容拷贝到 .c 文件中
    在 Func.c 文件中,包含了 Func.h 文件,预编译的时候会将头文件展开,但是不能直接将头文件内容拷贝进去,系统不会修改代码
  1. 系统会生成一个 Func.i 文件,里面包含了 Func.h 和 Func.cpp 里面函数的声明和定义,test.c 也会生成一个 test.i 的文件,里面有函数的声明和实际的调用
  2. 编译 检查语法,生成汇编代码(各种指令) 生成 Func.s test.s (都是由各自的 .i 文件生成)下面就是 Func.s 的代码
    在这里插入图片描述

在这里插入图片描述

在test.cpp文件的编译中,这两个函数的地址都是空,因为test.cpp展开后,只有它的包含的头文件内容和test.cpp的内容,也就是只有函数的声明,但是这也是可以通过编译的,系统会找在test.cpp中所有的调用函数类型,并在头文件中查找到,查找到了通过,否则编译出现问题,而具体的函数地址,都在后面的链接过程

  • 汇编 转化成二进制的机器码(CPU能认识的文件)
    Func.o test.o(由上面的 .s 文件转化而来
  • 链接 合并到一起
    链接一些没有确定的函数地址
    Linux下会生成一个 a.out 文件
    Windows 下会生成一个 .exe 文件
    如果这里链接,对于只有声明,没有定义的函数,就会显示链接错误
    在这里插入图片描述

回到正题,为什么c++支持函数重载
c语言链接函数地址时,因为c中不能出现重复的函数名,所以会以函数名去找函数地址。
c语言中有个符号表 名字----地址 对应
而在c++中,会给函数重命名
函数名修饰规则

void f(char b,int a)
void f(int b,char a);

在LInux中,规则 :_Z 函数名的字符个数 + 函数名 + 参数的首字母
第一个函数: _Z1fci
第二个函数: _Z1fic
c++在查找链接函数的时候,按照修饰名去查找,而不是按照函数名去查找,只要类型不同,找到的函数也就不同
Windows 下的修饰函数名
在这里插入图片描述

2.7. 引用

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

#include<iostream>
using namespace std;
int main()
{
	int a=1;
	int b=a;//赋值

	int& c=a;//引用
	cout << a << b << c << endl;
	c=10;
	cout << a << b << c << endl;
}

在这里插入图片描述

int& c=a;
int&d =a;
int&e =c;

在这里插入图片描述
a,c,d,e共用一块空间,相当于给最开始叫做a的空间,重新起了个名字c,d,e
而a,c,d,e的地址相同
在这里插入图片描述

2.7.1. 传入参数

引用做参数的价值
c语言中的传入参数

void Swap(int *a,int *b)
{
    int tmp=*a;
    *a=*b;
    *b=tmp;
}

c++的传参

void Swap(int&a,int&b)
{   
    int tmp=a;
    a=b;
    b=tmp;
}

如果有个单链表结点的结构体

typedef struct SListNode
{
    struct SListNode*next;
    int val;
}SLT;

c语言中

void SListPushBack(SLTNode*phead,int x)
{
    if(phead==NULL)
    {    
        phead=newnode;
    }
    else
    {
        //找尾
        //阿巴阿巴
    }
}
//这里不能传入一级指针,phead是空的时候,传入的phead是形参,在这个函数内的phead的改变不会影响实参的改变
void SListPushBack(SLTNode** pphead,int x)
{
    if((*pphead)==NULL)
    {    
        (*pphead)=newnode;
    }
    else
    {
        //找尾
        //阿巴阿巴
    }
}
    //使用二级指针修改一级指针

使用c++完成过程

typedef struct SListNode
{
    struct SListNode* next;
    int val;
}SLTNode,*PSLNode;//节点指针的 typedef
//传入的一级指针的引用,也就是传入phead的别名,函数内可以对phead修改
void SListPushBack(SLTNode*& phead,int x)
{
    if(phead==NULL)
    {    
        phead=newnode;
    }
    else
    {
        //找尾
        //阿巴阿巴
    }
}

2.7.2. 引用特性

  1. 引用在定义的时候,必须初始化
    在这里插入图片描述
  2. 引用一旦引用了一个实体,就再也不能引用其他实体
    在这里插入图片描述这个特性就决定了,c++不能脱离指针。
  • 实现链表功能的时候,每个节点的 next 可能会发生改变,但是c++中的引用无法修改指向,c++的引用可以理解为对c指针的补充。
  • 在 java 中引用可以改变指向,并且可以初始化为空,java 可以脱离指针。
  1. 一个变量可以有多个引用,变量的别名也可以起别名
    在这里插入图片描述
    上面 a 的别名,地址都一样,说明都是指向 a 空间的
    如果对变量别名赋值
c=b

c的地址不会发生变化,同时因为 c 这块空间的值发生改变,所以这块空间的所有别名的值都会改变
在这里插入图片描述

2.7.3. 引用做返回值

  1. 传值返回
#include<iostream>
using namespace std;
int Count()
{
        int n = 0;
        n++;

        return n;
}
int main()
{
		int ret=Count();
        return 0;
}

在这里插入图片描述

如果数据过大,寄存器装不下的时候,会将数据存在 Count 函数和 main 函数之间
在这里插入图片描述
2. 传引用返回----返回n的别名

#include<iostream>
using namespace std;
int& Count()
{
        int n = 0;
        n++;

        return n;
}

int main()
{
        int ret = Count();
        return 0;
}

在这里插入图片描述
返回引用,在 mian 函数里修改 ret 就是直接对 n 的值进行修改。
但是这样写会产生问题,函数中的 n 已经销毁了,虽然可以返回,但是返回的 n 类似野指针,会警告
在这里插入图片描述
对编译器来说,销毁的空间可能会赋值 1 或者是随机数

  • 没有进行引用返回
    在这里插入图片描述

  • 进行引用返回
    在这里插入图片描述
    如果接收值也是引用的话,第二次就会输出随机值
    cout也是一个函数
    请添加图片描述
    当第一次在 cout 函数中输出 ret 的值,首先会传入参数,再建立 cout 的函数的函数栈帧。在建立函数栈帧之前就传入了 ret 的值,但是后来的 cout 函数建立的函数栈帧会覆盖那块空间,所以在第一次 ret 输出完后,cout 函数栈帧会销毁,导致ret 空间的值会改变为随机值。
    第二次调用函数的时候,ret 此时的值,已经变成了覆盖后的值,所以会输出随机值。

  • 下面的函数同理
    在这里插入图片描述
    因为第一次调用 Add(1,2) 后,函数中的 c 变量会因为函数的结束而销毁,但是 main 函数中的 ret 会指向那块空间,第二次调用 Add(3,4) 的时候,ret 指向的位置和函数中新的 c 变量的位置重合,所以最后输出的结果就是 Add
    (3,4) 返回的值,也就是 7。

  • 所以上面的这种返回引用的函数,没有意义,而且非常不安全(野引用,和野指针的问题类似)

什么情况下,返回引用的函数有用呢?
如果函数返回时,出了函数作用域,如果返回的对象还在(还没有还给系统),则可以使用引用返回,如果已经还给了系统,就只能传值返回。

  • 如下面的情况
    在c语言中,我们需要改变顺序表确定位置的值
#include<stdio,h>
#include<assert.h>
typedef struct Seqlist
{
        int* data;
        int size;
        int capacity;
}SE;
//读取
int SLAT(SE*ps,int i)
{
    assert(i < ps->size);
    return ps->a[i];
}
//修改
void SlModify(SE*ps,int i,int x)
{
    assert(ps->size);
    ps->a[i]=x;
}

在c++中的写法

#include<iostream>
#include<assert.h>
using namespace std;
typedef struct Seqlist
{
        int* data;
        int size;
        int capacity;
}SE;
int& SLAT(SE& ps, int i)
{
        assert(i < ps.size);
        //查找第 i 位置
        return ps.data[i];
}

int main()
{
        SE s;
        //读取
        SLAT(s, 0) = 1;//返回来的第i个位置的别名,对其修改
        //修改
        cout << SLAT(s, 0) << endl;
}

使用空间位置的别名,对空间内容进行修改
在这里插入图片描述

  • c++通过 返回引用,能直接修改对应位置的值(出了函数作用域值还在)
    而且这样写不会有风险,s 是在 main 函数中定义的,出了 SLAT 函数的作用域还存在,对其中一个值,通过引用修改完全可以

但如果返回的仅仅是值呢?
在这里插入图片描述

  • 返回的仅仅是值,只是值的拷贝,对拷贝进行修改时没有意义的,而且在从 SLAT 返回到 main 函数中,会生成一个临时变量,这个变量保存的就是 SLAT 的返回值,然后将 临时变量 的值赋给 main 中需要的地方。
  • 这个临时变量具有常性,类似常数,不可修改,所以上面对函数赋值会报错。

上面我们只是用c++实现c语言中的顺序表,c++还有更快乐的地方
(c++的结构体升级为类,在这个类中可以定义函数)

#include<iostream>
#include<assert.h>
using namespace std;
#define N 10
struct Seqlist
{
        int& at(int i)
        {
                assert(i < N);
                return a[i];
        }
        int a[N];//偷个懒写个静态
        int size;
};
int main()
{
        struct Seqlist s1;
        Seqlist s2;//c++中两种定义的方法都可以
        for (int i = 0; i < N; i++)//向 s2 中输入0~9
        {
                s2.at(i) = i;
        }
        for (int i = 0; i < N; i++)//输出
        {
                cout << s2.a[i] << "  ";
        }
        return 0;
}

上面的函数是通过返回引用,在循环中直接赋值
在这里插入图片描述

2.7.4. 权限问题

const int a=0;
int& b=a;

上面两段代码,会在编译器中报错
在这里插入图片描述
对 a 变量来说,他是个常量,但是赋给一个自由的 int 类型的变量,会放大权限,所以这里会报错。
在这里插入图片描述
在引用的过程中:

  • 权限可以 平移
  • 权限可以 缩小
  • 权限不可以 放大
    ps:这对赋值没影响
const int a=10;
int b=a;

有下面的代码:

#include<iostream>
using namespace std;
int main()
{
		int i = 0 ;
		double d = i ;
		double& e = i;
		const double& f = i;
		return 0;
}

在这里插入图片描述
这里 double&e = i ;会报错
在这里插入图片描述
i 的值会给一个临时变量,而这个临时变量具有常性,不可改变,不能起别名,这里给 e 的时候权限会放大,所以只要加上 const double& e = i; 可成功通过,权限平移。

  • 函数返回值也是同理
    在这里插入图片描述
    返回值传给 main 之前,会生成 临时变量,临时变量传给 ret ,权限会放大,所以这里也会报错
    在这里插入图片描述
    同理,加上 const 修饰即可

传引用参数(任何时候都可以)

  1. 提高效率
  2. 输出型参数(形参的修改,影响的实参)

传引用返回(出了函数作用域,对象还在,才可以使用)

  1. 提高效率
  2. 修改返回对象
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值