C++笔记(2023.4.17)

目录

引用

引用的应用场景

1、引用做参数 (输出型参数)形参的改变要影响实参2、引用做参数 (提高效率) (大对象/深拷贝对象--以后补充)

2、引用做返回值(不会生成临时变量)(慎用,当值为局部变量时,会很危险,出作用域后,栈帧就被销毁,值被覆盖)

修改返回值+获取返回值;

引用总结

常引用

引用和指针的区别(面试常考点)

auto关键字

范围for 语法糖


引用

给名字起个外号,别名(语法层面看没有开辟空间,底层汇编指令的角度看引用是类似指针的方式实现的)

引用必须得给一个值(初始化)

int&d错误

int&d=a;正确

一个变量可以有多个引用,

一个引用一旦引用一个实体,再也不能引用其它实体以后无法改变 (C++跟java的差异

引用的应用场景

1、引用做参数 (输出型参数)形参的改变要影响实参
2、引用做参数 (提高效率) (大对象/深拷贝对象--以后补充)

//在C语言中,想要改变实参,需要传实参的地址,现在我们只需要在实参类型后面加一个&(引用)



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


int* preorderTraversal(struct TreeNode* root, int* returnSize)  -- c
int* preorderTraversal(struct TreeNode* root, int& returnSize)  -- cpp

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



typedef struct ListNode
{
	int val;
	struct ListNode* next;//c,cpp(兼容c)
    ListNode* next;//cpp
}LTNode;

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

//void ListPushBack(struct ListNode*& phead, int x)
//void ListPushBack(LTNode*& phead, int x)
void ListPushBack(PLTNode& phead, int x)
{
	// ...
	// phead = newnode;
	// ...
}

int main()
{
	int x = 0, y = 1;
//不需要传递实参地址,只需要将实参值传过去
	Swap(x, y);
	cout << x << " " << y << endl;

	int* px = &x, * py = &y;
	cout << px << " " << py << endl;
	Swap(px, py);
	cout << px << " " << py << endl;

	PLTNode plist = NULL;
	ListPushBack(plist, 1);

	return 0;
}

我们可以通过一组代码来看普通传参与引用传参的速度对比

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


using namespace std;

struct A { int a[100000]; };

void TestFunc1(A a) {}

void TestFunc2(A& a) {}

void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();

	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();

	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

int main()
{
	TestRefAndValue();

	return 0;
}

 

2、引用做返回值(不会生成临时变量)(慎用,当值为局部变量时,会很危险,出作用域后,栈帧就被销毁,值被覆盖)

作用:减少拷贝,提高效率;

#include<iostream>

using namespace std;

#include<assert.h>
#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }

void TestReturnByRefOrValue()
{
	// 以值作为函数的返回值类型
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc1();
	size_t end1 = clock();

	// 以引用作为函数的返回值类型
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)
		TestFunc2();
	size_t end2 = clock();

	// 计算两个函数运算完成之后的时间
	cout << "TestFunc1 time:" << end1 - begin1 << endl;
	cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

int main()
{
	TestReturnByRefOrValue();

	return 0;
}

修改返回值+获取返回值;

传值返回

传值返回,不管怎么样,都会生成临时变量

函数返回值不会直接返回,在寄存器中,生成临时变量

寄存器只有4字节或者八字节

int Count()

{

static int n=0;(静态变量,也会生成临时变量)

n++;

return n;
}

传引用返回

减少拷贝,提高效率

n为静态变量时不危险,但是n为局部变量时,会发生非法访问,结果可能为随机值

正确样例:

全局变量。静态变量,malloc

int& Count()

{

static int n=0;

n++;

return n;(返回的是n的别名,没有拷贝)
}

错误样例

顺序表,位置读写与修改,

int& SLAt(SeqList&ps,int pos)

{

return ps.a[pos];
}

SLAt(s,0)=1;该位置赋值为1

SLAt(s,0)+=5;该位置值加5

引用总结

1、基本任何场景都可以用引用传参。

2、谨慎使用引用做返回值,出来函数作用域,对象不在了,就不能用引用返回,还在就可以用引用返回。

命名空间域不会影响生命周期。

常引用

引用时,才会出现权限放大、平移、缩小

//不可以

引用过程当中,权限不能放大

const int a=0;

int& b=a;错误

//可以,c拷贝给d,没有放大权限,因为d的改变不影响c

const int c=0; 

int d=c;

//可以

引用过程中,权限可以平移或缩小

int x=0;

int &y=x;//平移

const int&z=x;//缩小

++x;正确

--z;错误

可以

const int&m=10;//平移

不可以

double dd=1.11;

int ii=dd; 不同类型转换会产生临时变量,提升,临时变量具有常性

int main()
{
int i=1;
double j=1.1;
if(j>i)//在运算符两边的两个变量类型不同时会发生提升(i会提升,但不会对i本身进行提升,因为在这不能把i给修改了),因此生成一个double的临时变量,8字节。一般情况下都是小的向大的提升
{
cout<<"xxxxx"<<endl;
}
return 0;
}

int&rii=dd;错误  拷贝的是int临时变量;临时变量具有常性

const int&rii=dd;正确

引用和指针的区别(面试常考点)

int main()
{
//语法层面看没有开辟空间,底层汇编指令的角度看引用是类似指针的方式实现的)
	int a = 10;

	// 语法层面:不开空间,是对a取别名
	int& ra = a;
	ra = 20;

	// 语法层面:开空间,存储a的地址
	int* pa = &a;
	*pa = 30;

	return 0;
}

因为内存太慢,所以需要借助寄存器

lea(取地址) 将a的地址放到eax寄存器中

[ ]表示解引用  把eax中a的地址放到pa中

取出pa里面的内容,a的地址放到eax中去

对eax进行解引用,获取a地址里面的内容,将其修改为30

auto关键字

一般类型过长时,用auto替代(例如迭代器)

int a=0;

int b=a;

auto c=a;//根据右边的表达式推导c的类型

auto d=1+1.11;

cout<<typeid(d).name()<<endl;(打印类型)

注意事项:

auto xx;错误

auto c=3,d=4.0;错误

auto不能做函数参数,不能直接用来声明数组

范围for 语法糖

适用于数组,容器

依次取数组中数据赋值给e(element元素)

自动迭代(往后走),自动判断结束

auto与e都是可修改的,但是建议用这两个

#include<iostream>

全部展开
//using namespace std;

//部分展开
using std::cout;
using std::endl;

int main
{
int arr[] = { 2,4,6,8,10 };

//赋值操作(这样无法修改数组中的值,因为这是一种赋值操作,需要加一个引用)
	for (auto e : arr)
	{
		e *= 2;
	}

	for (auto e : arr)
	{
		cout << e << " ";
	}

	cout << endl;

	//引用操作
	for (auto& e : arr)
	{
		e *= 2;
	}
	for (auto e : arr)
	{
		cout << e << " ";
	}

	cout << endl;

return 0;
}

错误用法

//在这里,数组传参变成了指针
void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

猜你喜欢

转载自blog.csdn.net/2202_75625589/article/details/130210722