当前位置: 首页 > article >正文

突破编程_C++_面试(基础知识(7))

面试题16:什么是引用,它与指针有什么区别

引用是变量的别名。对于变量名而言,C++ 实际上对其是不作存储的,在汇编以后不会出现变量名,变量名作用只是用于方便编译器成汇编代码,是给编译器看的,同时也是方便人编写与阅读代码。作为变量名的别名,引用自然也不会在内存中存储,它只是提供了另一种访问已分配内存的方式。另外,引用也并没有自己的内存地址,即使对引用进行取地址操作,返回来的结果也是原变量地址,如下为样例代码:

#include <iostream>  

int main() {

	int val = 1;
	int& refVal = val;

	printf("val address = %p\n", &val);
	printf("refVal address = %p\n", &refVal);

	return 0;
}

上面代码输出为:

val address = 00000025324FF724
refVal address = 00000025324FF724

从上面的输出可以看到,引用指向的地址(00000025324FF724)就是原变量的地址(00000025324FF724),因此,对引用的任何操作实际上修改的就是原有变量
引用与指针的区别如下
(1)基本概念
引用是变量的别名,是一个已经存在的变量的另一个名字,不占用存储空间。一旦引用被定义并初始化,就不能被重新指向另一个变量。引用总是指向在初始化时被指定的变量,直到该引用和变量都超出作用域。
指针是一个变量,占用存储空间( 32 位平台编译是 4 个字节, 64 位位平台编译是 8 个字节),其值为另一个变量的地址。非 const 的指针可以重新指向另一个变量,即可以改变指针的值,使其指向另一个地址。
(2)访问所指向的变量
使用引用就像使用它所引用的变量一样。例如:int val=1; int& valRef=val; valRef=2;, 该段代码将 val 的值由 1 修改为 2。
使用指针访问其指向的变量需要使用解引用操作符(*)。例如:int val=1; int* prt=&val; *prt=2; , 该段代码将 val 的值由 1 修改为 2。
(3)初始值
引用在定义时必须要同时初始化指向有效的变量,没有空引用的概念。
指针可以是空( nullptr ),指向不确定的内存,或者指向已经被释放的内存。
(4)运算
引用没有自己的地址,不可以进行指针算术。
指针有自己的地址和值,可以进行指针算术(如递增、递减、比较等)。
(5)用途
引用通常用于函数参数和函数返回值,以确保传递的是变量的别名而非副本,从而可以避免值拷贝的过程并且能够修改实际参数的值。
指针除了能够用于函数参数和函数返回值,还可以用于动态内存分配、构建数据结构(如链表、树、图等)以及以函数指针的形式用于回调等过程。
(6)安全性
引用总是指向有效的对象,并且不能被重新指向。所以其类型是固定的,更为安全。
指针可以强制类型转换、可以为空、可以指向无效的地址,所以在使用不当的情况下容易引起程序崩溃。

面试题17:什么是 const 引用,其用途是什么

const 引用指的是对常量的引用,也就是说,不可以通过该引用修改其指向变量的值。其语法形式如下 :

const 类型名& 引用名 = 被引用的常量值或变量;

const 引用有多个重要的用途:
(1)保护数据不被修改:最基本的作用是防止不小心修改数据的值。当你不希望一个变量在函数中被修改时,可以使用 const 引用来传递这个变量。
(2)函数参数传递:在函数形参中使用 const 引用,可以确保在函数内部不会修改实参的值。同时,由于引用避免了数据的拷贝,因此在传递大型数据结构时,使用 const 引用可以提高效率。
(3)增加代码的可读性和安全性:使用 const 引用可以明确地告诉其他阅读代码的人,这个数据是不应该被修改的,从而增加代码的可读性和安全性。
(4)扩展函数的应用范围:使用 const 引用作为函数参数,可以使得函数既能接受常量也能接受非常量作为参数,从而扩展了函数的应用范围。例如:

void printStr(const string& str)
{
	printf("%s\n", str.c_str());
}
int main()
{
	printStr("hello");			//正确:这种场景下使用 const 引用可以方便的直接以常量字符串作为入参。
	
	return 0;
}

面试题18:当一个对象通过引用传递给函数时,会不会调用拷贝构造函数

当一个对象通过引用传递给函数时,不会调用拷贝构造函数。这是因为引用只是对象的别名,而不是对象的副本。所以引用作为函数参数可以提高程序性能:避免了值传递时无谓的数据拷贝过程,特别是对于大型对象或数据结构来说效果更为显著。同时,引用传递还允许函数修改其参数对象的状态,因为函数操作的是原始对象而不是其副本。

面试题19:如何使用右值引用与 move 来优化性能

右值引用与 move 的配合使用可以将资源(如动态分配的内存)从一个对象转移到另一个对象,而不用进行深拷贝。这个移动过程是通过右值引用、移动构造函数以及移动赋值运算符来实现的。
move 的本质是将左值强制转换为右值引用,避免拷贝带来的性能损失,该函数对具有移动构造函数的类类型有效,但是对于一些基本类型(比如 int 、 float 等)使用时,仍然会发生拷贝( C++ 中所有容器都支持 move 操作)。
移动构造函数与移动赋值函数(重载 operator= 实现)都接受一个右值引用作为参数,并从该参数中移动资源(而非复制资源)。从而是将资源的所有权(如动态内存)从源对象转移到目标对象。
如下是一个使用 move 、移动构造函数以及移动赋值运算提高程序性能的实现过程:

#include <iostream>  

using namespace std;

class A
{
public:
	A()
	{
		printf("execute constructor function\n");
		m_val = new int(0);
	}
	A(A&& a) noexcept		//移动构造函数 
	{
		if (this != &a) {	// 防止自赋值  
			printf("execute move constructor function\n");
			m_val = a.getVal();	//内存转移
			a.initVal();	//源对象的内存做 nullptr 处理,防止其在析构时出现二次释放内存的行为
		}
	}
	A& operator=(A&& a) noexcept		//移动赋值函数 
	{
		if (this != &a) // 防止自赋值  
		{
			printf("execute operator=() function\n");
			m_val = a.getVal();	//内存转移
			a.initVal();	//源对象的内存做 nullptr 处理,防止其在析构时出现二次释放内存的行为
		}
		return *this;
	}
	~A()
	{
		printf("execute destructor function\n");
		if (nullptr != m_val)
		{
			delete m_val;
			m_val = nullptr;
		}
	}

public:
	int* getVal() const
	{
		return m_val;
	}

	void initVal()
	{
		m_val = nullptr;
	}

private:
	int* m_val = nullptr;
};

int main()
{
	A a1;
	A a2(move(a1));		//调用移动构造函数
	A a3;
	A a4;
	a4 = move(a3);		//调用移动赋值函数,注意:如果写成 A a4 = move(a3); 依然会调用移动构造运算符
	return 0;
}

上面代码输出为:

execute constructor function
execute move constructor function
execute constructor function
execute constructor function
execute operator=() function
execute destructor function
execute destructor function
execute destructor function
execute destructor function

上面代码中的移动构造函数以及移动赋值函数实现了将堆上的内存 m_val 转移到目标对象的功能。


http://www.kler.cn/a/228882.html

相关文章:

  • 大文件上传服务-后端V1V2
  • Python爬取豆瓣图书网Top250 实战
  • 【人工智能】Python中的自动化机器学习(AutoML):如何使用TPOT优化模型选择
  • 计算机网络-数据链路层
  • 什么是长连接?Netty如何设置进行长连接?
  • 蓝桥杯 Python 组知识点容斥原理
  • 嵌入式系统设计师之文件系统(3.2.5)
  • 学成在线:媒体资源管理系统(MAM)
  • 数据结构与算法:图论(邻接表板子+BFS宽搜、DFS深搜+拓扑排序板子+最小生成树MST的Prim算法、Kruskal算法、Dijkstra算法)
  • 编译器的实用调试技巧
  • 一分钟了解电脑关机快捷键是什么!
  • 如何以管理员身份删除node_modules文件
  • 思科模拟器实验合集
  • elastic-job VS xxl-job
  • (22)删除指定的数
  • 【UE 材质】扇形材质
  • 事件在状态流程图中的工作方式
  • 黑群晖安装教程-——传统优盘引导制作中问题
  • flask的基本使用 token插件(二)
  • ASP.NET Core WebAPI_解决跨域问题(前端后端)
  • docker 简单项目
  • SSH免密切换服务器案例-ssh协议(公钥和私钥)
  • 【C语言】static关键字的使用
  • 【蓝桥杯选拔赛真题63】python小马过河 第十五届青少年组蓝桥杯python 选拔赛比赛真题解析
  • CSS 闪电按钮效果
  • AI新宠Arc浏览器真可以取代Chrome吗?