C++入门(下)

📋 个人简介

  • 💖 作者简介:大家好,我是菀枯😜

  • 🎉 支持我:点赞👍+收藏⭐️+留言📝

  • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

    v2-af3cfbda99d44f8eb114cc6c74c92253_720w

    前言

🐸上一周博主在准备互联网+的比赛,所以断更了一周。上一次我们聊了一些C++的基础语法,比如namespace关键字, 缺省参数,函数重载等。不记得的朋友可以再去看一看呀👀。今天呢我们继续学习C++的基础语法。

引用

引用的概念

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

就像在生活中,我们会给别人取外号一样,引用就相当于给人取外号。虽然名字不同,但是都是同一个人。

引用的用法

类型& 引用变量名 = 引用实体。

void Test(){
    int a = 20;
    int &b = a;      //此时b就是a的别名
    
	cout << a << endl;
	cout << b << endl;
	cout << &a << endl;
	cout << &b << endl;
}

uTools_1652135854915

我们可以看到a和b不仅值一样,地址一样。这说明二者使用同一块内存空间。

引用特性

  1. 引用在定义时必须初始化

如果我们想给某个人取外号,首先这个人必须得存在我们才能给它取外号。

  1. 一个变量可以有多个引用
void test(){
    int a = 10;
    int& b = a;
    int& c = a;
}

image-20220510071833142

我们可以看到a, b ,c的地址是一样的,所以他们用的都是同一块内存空间。

  1. 引用一旦引用一个实体,再不能引用其他实体。

简单来说,就是一个外号只能给一个人取。比如:孙悟空,既可以叫做“孙行者”,也可以叫做“斗战胜佛”。斗战胜佛这个名字已经和孙悟空绑定了,那么我们不能把唐僧也叫做斗战胜佛。

1e75548d612e6e8f2bb40401abce0c95

引用规则

取别名原则:对原引用变量,权限只能缩小,不能放大。

这里的权限指的是读写权限。

int main(){
    int a = 10;
    int& b = a;          //(1)
    
    const int c = 20;
    int &d  = c;        //(2)
    
    int e = 30;
    const int& f = e;   //(3)
    return 0;
}
  1. a和b的权限相同,都是既可以读又可以写,可以编译成功。
  2. c是只读变量,但是d既可以读又可以写,权限被放大,所以编译失败。
  3. e既可以读又可以写,f是只读变量,权限被缩小,所以编译成功。

引用的使用场景

  1. 做参数
void Swap(int& a, int& b){
    int tmp = a;
    a = b;
    b = tmp;
}

以前我们想在函数里面改变参数的值,需要用到指针,当变量比较复杂的时候,甚至需要用到二级,三级指针,很容易出现问题。C++的引用就很好的解决了这个问题。

  1. 做返回值
int& Test(){
    static int a = 1;
    a++;
    return a;
}

int main (){
    for (int i = 0; i < 10; ++i){
        cout << Test() << " ";
    }
    return 0;
}

上面代码的输出结果是什么呢???

1647935710514

我们都知道static变量是静态变量,创建一次后,在程序结束之前就会一直存在。所以,输出结果如下:image-20220510081911471

C语言中如果我们想要访问在函数中创建的静态变量就需要用到指针。而在C++中可以用更为方便的引用。

那么什么时候用引用返回,什么时候用传值返回呢❓

其实很简单,就是看这个变量在函数结束后是否被销毁,如果会被销毁就使用传值返回,不会被销毁就使用引用返回。

传值,引用返回效率

❗️以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。❗️

我们可以用下面这个代码来对比一下

#include <iostream>
#include <time.h>

using namespace std;

struct tmp {
	int b[10000];
};                                                      

void Test1(tmp a) {                    //传值调用
	;
}

void Test2(tmp& a) {                  //引用调用
	;
}

int main() {
	tmp a;
	int begin1 = clock();                  
	for (int i = 0; i < 10000; ++i) {
		Test1(a);
	}
	int end1 = clock();

	int begin2 = clock();
	for (int i = 0; i < 10000; ++i) {
		Test2(a);
	}
	int end2 = clock();

	cout << "Test1:" << end1 - begin1 << endl;           //传值调用时间
	cout << "Test2:" << end2 - begin2 << endl;           //引用调用时间
	return 0;
}

我们来看看结果:image-20220510083628655

很明显,当我们使用引用调用去传递参数时,函数省去了复制参数的过程,省下了很多时间。

引用和指针的区别

学到这里,我们发现引用和指针很像。引用能做到的事情,指针都能做到。

其实,在汇编代码上,引用和指针是一模一样的。

int main()
{
 int a = 10;
 
 int& ra = a;
 ra = 20;
 
 int* pa = &a;
 *pa = 20;
 
 return 0; 
}

引用的汇编代码:

image-20220510084224562

指针的汇编代码:

image-20220510084312256

二者在底层实现上没有差别,但是在用法上有区别。

  1. 引用在定义时必须初始化,指针没有要求。
  2. 引用在初始化时应用一个实体后,就不能再引用其他实体。而指针可以在任何时候指向任何同一个类型的对象。
  3. 没有NULL引用,但有NULL指针。
  4. sizeof进行计算时结果不同,引用为引用类型大小,而指针是地址空间的大小
  5. 引用进行加减是对值进行加减,而指针加减是对指针指向的位置进行加减。
  6. 指针存在多级指针,而引用没有。

内联函数

内联函数概念

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

内联函数有点像我们在C语言中用的#define, 一些功能比较简单,代码比较少的函数,我们就可以用#define来实现,比如:

#define ADD(a, b) ((a) + (b))

这样减少了我们去调用函数的时间,而C++的内联函数就和这个类似,将函数的代码在被调用的地方直接展开。内联函数相对于宏来说更加安全,也不容易写错。

内联函数用法

在函数的定义前加上inline即可

inline int Add(int a, int b){
    return a + b;
}

int main(){
    int a = 10;
    int b = 20;
    cout << Add(a, b) << endl;
    return 0;
}

内联函数特性

  1. 内联函数是一种以空间换时间的做法,省去调用函数的开销。所以当代码很长或有循环/递归时不建议使用内联函数。
  2. 内联函数不建议声明和定义分离,因为inline在调用位置展开后,就没有函数地址了,会发生链接错误。

auto关键字

auto简介

​ 不知道大家在C语言学习中有没有了解过auto关键字,其实C语言中也有auto这个关键字。当我们在函数中定义变量时,变量的默认类型就是auto。

auto用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量

这个关键字在我们写代码的时候,几乎看不到,但是又如此的重要。在C++11标准中,auto关键字重新崛起,添加了新的功能。

image-20220512082815751

auto用法

在C++11中auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

简而言之,auto可以根据传递的值来推断变量的类型。

#include <iostream>

using namespace std;

int main(){
    auto a = 10;
    auto b = 2.0;
    auto c = &a;
   	//typeid().name 可以返回变量的类型
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    
    return 0;
}

我们来看看结果:image-20220512083653307

auto使用细则

  1. 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。

  2. auto与引用和指针混合使用。

    用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&。

    #include <iostream>
    
    using namespace std;
    
    int main(){
        int a = 0;
        auto& b = a;
        return 0;
    }
    

    此时b就是a的引用。

  3. 在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对

第一个类型进行推导,然后用推导出来的类型定义其他变量.。

int main(){
    auto a = 1, b = 2;
    auto c = 1, d = 2,0;   //编译器报错
    return 0;
}
  1. auto不能做函数的参数和返回值

  2. auto不能用来声明数组。

for循环

C++的for循环相对于C语言来说,没有很大的改变。只是添加了一种新的遍历方式:基于范围的for循环

#include <iostream>

using namespace std;

int main(){
    int a[5] = {1, 2, 3, 4, 5};
    for (auto i : a){
        cout << i << " ";
    }
    return 0;
}

image-20220512091525932

这种遍历就是用 i 这个变量去取数组中的值。

注意:此时的i是数组数据的拷贝,对i进行修改无法改变数组中的值。我们可以使用引用来解决这个问题

#include <iostream>

using namespace std;

int main(){
    int a[5] = {1, 2, 3, 4, 5};
    for (auto& i : a){
        i *= 2;
    }
    
    for (auto e : a){
        cout << e << " ";
    }
    return 0;
}

结果如下:

image-20220512091956322

nullptr

nullptr用法相当于C语言中的NULL,我们在C++中使用nullptr即可

结语

1647941444633

欢迎各位参考与指导!!!

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

。菀枯。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值