C++ ref和引用(&)

本文深入探讨了C++标准库中的std::ref函数的作用与使用场景,特别是在函数式编程和多线程环境下如何避免对象的浅拷贝,确保引用传递的正确性。通过实例对比了直接使用引用和std::ref的区别,解析了bind和thread中std::ref的应用。
1.简介:

std::ref只是尝试模拟引用传递,并不能真正变成引用,在非模板情况下,std::ref根本没法实现引用传递,只有模板自动推导类型时,ref能用包装类型reference_wrapper来代替原本会被识别的值类型,而reference_wrapper能隐式转换为被引用的值的引用类型。

  • bind

std::ref主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用。

  • thread

其中代表的例子是thread
比如thread的方法传递引用的时候,必须外层用ref来进行引用传递,否则就是浅拷贝。

2.使用
class TestRef
{
public:
	TestRef():i(0)//默认构造函数 
	{
		cout << "TestRef默认构造函数" << this << endl;
	} 	
	TestRef(const TestRef&testR) :i(0) //拷贝构造函数
	{
		cout << "TestRef拷贝构造函数" << this << endl;
	}
	~TestRef()//析构函数
	{
		cout << "TestRef析构函数" << this << endl;
	}
	mutable int i ;
};


class TestRef1
{
public:
	TestRef1() :i(0)//默认构造函数 
	{
		cout << "TestRef1默认构造函数" << this << endl;
	}
	TestRef1(const TestRef1& testR) :i(0) //拷贝构造函数
	{
		cout << "TestRef1拷贝构造函数" << this << endl;
	}
	~TestRef1()//析构函数
	{
		cout << "TestRef1析构函数" << this << endl;
	}
	mutable int i=0;
};


void funs(const TestRef&test1, const TestRef1&test2)
{
	cout << "function " << "test1:" << &test1 <<" "<< test1.i << endl;
	cout << "function " << "test2:" << &test2 <<" "<< test2.i << endl;
	test1.i++;
	test2.i++;
}
void testFunction()
{
	TestRef t1;
	TestRef1 t2;
	cout << "testFunction " << "t1:" << &t1 << " " << t1.i << " t2:" << " " << &t2 << " " << t2.i << endl;
	funs(t1,t2);
	cout << "testFunction " << "t1:" << &t1 <<" "<< t1.i << " t2:" <<" " << &t2 << " " << t2.i <<endl;
}
int main()
{
	testFunction();
}

在这里插入图片描述
直接用引用 &是可以改变i的值的,那么为什么还要用ref呢?

再上一段代码。


void testFunction()
{
	TestRef t1;
	TestRef1 t2;
	function<void()> f1 = bind(funs, t1, ref(t2));
	f1();
	cout << "testFunction " << "t1:" << &t1 << " " << t1.i << " t2:" << " " << &t2 << " " << t2.i << endl;
}

在这里插入图片描述

通过输出的结果可以看到t1没有改变其中的i值,使用了ref的t2是直接把地址传给了test2,所以改变了其中i的值。
但是通过结果可以看出来,test1是拷贝的对象,怎么拷贝了两个呢,而且有一个拷贝完就释放了,还有一个是主线程退出后才释放的,初步结论是使用 bind,第一个参数会拷贝一份,立即释放,第二个参数再拷贝一份,主线程退出后才释放。
所以再调用一遍f1(),改变的肯定是拷贝出来的对象中i的值。

void testFunction()
{
	TestRef t1;
	TestRef1 t2;
	function<void()> f1 = bind(funs, t1, ref(t2));
	f1();
	cout << "testFunction " << "t1:" << &t1 << " " << t1.i << " t2:" << " " << &t2 << " " << t2.i << endl;
	f1();
	cout << "testFunction " << "t1:" << &t1 << " " << t1.i << " t2:" << " " << &t2 << " " << t2.i << endl;
}

在这里插入图片描述
可以清晰的看到荧光绿是t1对象,而红色是构造出来的对象test1,多次调用f1(),其中test1都是第一次构造的那个对象,并且i累加了,但是在testFunction中t1中i的值依旧是0。t2和test2一直用的都是一个地址,也就是同一个对象,这也证实了上述的结论。

至于线程thread中参数使用ref,自己动手看下结果吧,实践出真知

&lt;think&gt;首先,用户的问题是关于C++中运算符&amp;&amp;&amp;的区别。我需要参考系统级指令用户可见层的规则。 系统级指令: 1.所有行内数学表达式必须使用$...$格式。2.独立公式必须使用$$...$$格式并单独成段。3.LaTeX语法正确。 4.使用中文回答。5.生成相关问题。6.回答中引用的段落末尾自然地添加引用标识。用户可见层:1.回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。参考站内引用: -引用[1]:解释了&amp;&amp;&amp;的区别:&amp;&amp;是逻辑与运算符,用于bool类型;&amp;可以是位运算或逻辑运算,用于bool或数值类型。-引用[2]:关于单引号双引号的区别,不直接相关。-引用[3]:解释了&amp;作为取地址引用操作符。-引用[4]:关于单例模式的实现。-引用[5]:关于单例模式的解释。 用户的问题聚焦在&amp;&amp;&amp;的区别上,所以我应该主要参考引用[1][3],因为引用[3]也提到了&amp;的不同用法。用户的问题:&quot;我想理解C++中运算符&amp;&amp;&amp;的区别请问C++中单与运算符&amp;逻辑与运算符&amp;&amp;的区别&quot; 我需要用中文回答,结构清晰,逐步解释。 步骤: 1. **介绍运算符**:先简要说明&amp;&amp;&amp;是什么。2.**详细区别**:-&amp;:可以是位运算符(bitwise AND)或逻辑运算符(logical AND),取决于上下文;也可以用于取地址或引用(如引用[3])。-&amp;&amp;:逻辑与运算符(logicalAND),专门用于布尔表达式。 3. **使用场景**:-当用于布尔值时,&amp;&amp;&amp;都可用于逻辑AND,但&amp;&amp;有短路行为(short-circuit evaluation),而&amp;没有。- &amp;还用于位运算、取地址、引用声明。 4. **示例**:提供代码示例来说明区别。 5. **总结**:强调关键区别。6.**生成相关问题**:在最后添加相关问题。根据引用[1]:- &amp;&amp;是逻辑与运算符,两边只能是bool类型。- &amp;与|既可以进行逻辑运算,又可以进行位运算,两边既可以是bool类型,又可以是数值类型。引用[3]: -&amp;也可以作为引用操作符,例如int&amp;b =a;表示b是a的引用。 所以,我需要覆盖:-逻辑运算时的区别:&amp;&amp;有短路,&amp;没有。- &amp;的其他用途:位运算、取地址、引用。 回答结构:1.**开头**:直接回答问题,概述区别。2.**详细解释**:-解释&amp;&amp;:逻辑与,短路行为。-解释&amp;:位运算、逻辑运算、取地址、引用。-比较在逻辑运算中的行为。 3. **代码示例**:展示两者的使用。 4. **总结**:重申区别。 5. **相关问题**:生成3-5个相关问题。确保使用中文。生成相关问题: 1. C++中逻辑运算符的短路行为是什么?2.如何在C++中使用位运算符?3.C++引用指针的区别是什么?4.为什么在逻辑运算中推荐使用&amp;&amp;而不是&amp;?5.C++中其他运算符的区别,如|||?数学表达式:用户问题中没有明显的数学,但如果有,比如位运算解释时,可能需要使用$...$。例如,位运算如$ a&amp; b$,但在这个上下文中,可能不需要复杂的数学,保持简单。引用标识:在引用站内内容时添加[^1],例如基于引用[1]。最终回答框架: -引言:运算符&amp;&amp;&amp;在C++中有多重角色。 -部分1:&amp;&amp;作为逻辑与运算符。-部分2:&amp;作为位运算符、逻辑运算符、取地址运算符、引用声明符。-部分3:在逻辑运算中的关键区别:短路行为。 -示例代码。-总结。- &sect;&sect;相关问题&sect;&sect;&lt;/think&gt;### C++中运算符&amp;&amp;&amp;的区别详解 在C++中,运算符`&amp;``&amp;&amp;`虽然符号相似,但功能用途有本质区别。我将逐步解释它们的核心差异,确保回答清晰可靠。以下内容基于C++标准,并结合站内引用[^1][^3]进行说明。 #### 1. **逻辑与运算符`&amp;&amp;`** - **定义**:`&amp;&amp;`是纯逻辑与运算符(logical AND),专门用于布尔(`bool`)表达式。它要求两侧操作数必须是布尔类型(`true`或`false`)。 - **行为特点**: - 具有短路行为(short-circuit evaluation):如果左侧操作数为`false`,右侧操作数不会被求值,直接返回`false`。这提高了效率并避免不必要的计算。 - 示例:表达式`(a &gt; 0) &amp;&amp; (b &lt; 10)`中,如果`a &lt;= 0`,则`b &lt; 10`不会执行。 - **使用场景**:主要用于条件语句(如`if`、`while`)中组合布尔条件。 ```cpp if (is_valid &amp;&amp; (value &gt; threshold)) { // 仅当is_valid为true时,才检查value // 执行操作 } ``` #### 2. **单与运算符`&amp;`** - **定义**:`&amp;`是一个多功能运算符,根据上下文扮演不同角色: - **位运算符(bitwise AND)**:对整数类型(如`int`、`char`)的二进制位进行逐位与操作。例如,$ a \&amp; b $ 计算每个对应位的AND结果。 ```cpp int result = 5 &amp; 3; // 二进制: 0101 &amp; 0011 = 0001, 结果为1 ``` - **逻辑运算符(logical AND)**:当操作数为布尔类型时,`&amp;`可执行逻辑与,但无短路行为。两侧操作数总是被求值。 ```cpp bool flag = (a &gt; 0) &amp; (b &lt; 10); // 无论a的值如何,b &lt; 10都会被计算 ``` - **取地址运算符**:用于获取变量的内存地址,返回指针。 ```cpp int num = 10; int* ptr = &amp;num; // ptr指向num的内存地址 ``` - **引用声明符**:在类型声明中,表示引用(别名),如引用[^3]所述。 ```cpp int a = 10; int&amp; ref = a; // ref是a的引用,操作ref等同于操作a ``` #### 3. **关键区别总结** | 特性 | `&amp;&amp;` (逻辑与) | `&amp;` (单与) | |--------------------|----------------------------------------|----------------------------------------| | **操作数类型** | 必须是布尔类型(`bool`) | 可以是布尔类型或数值类型(如`int`) | | **短路行为** | 有(右侧可能不求值) | 无(总是求值两侧) | | **主要用途** | 逻辑条件组合 | 位运算、取地址、引用声明、逻辑运算 | | **效率** | 高效(避免冗余计算) | 较低(无短路优化) | | **示例区别** | `if (cond1 &amp;&amp; cond2)` 安全高效 | `if (cond1 &amp; cond2)` 可能引发副作用 | **代码示例对比**: ```cpp #include &lt;iostream&gt; using namespace std; int main() { bool a = false; int b = 0; // 使用&amp;&amp;:短路行为,b++不会执行 if (a &amp;&amp; (b++ &gt; 0)) { cout &lt;&lt; &quot;&amp;&amp; executed&quot; &lt;&lt; endl; } cout &lt;&lt; &quot;b after &amp;&amp;: &quot; &lt;&lt; b &lt;&lt; endl; // 输出 b = 0 // 使用&amp;:无短路行为,b++总是执行 if (a &amp; (b++ &gt; 0)) { cout &lt;&lt; &quot;&amp; executed&quot; &lt;&lt; endl; } cout &lt;&lt; &quot;b after &amp;: &quot; &lt;&lt; b &lt;&lt; endl; // 输出 b = 1 return 0; } ``` 在此示例中: - `&amp;&amp;` 因短路行为未执行`b++`,`b`保持为0。 - `&amp;` 强制求值右侧,导致`b`增加到1,即使条件不成立。 #### 4. **最佳实践建议** - **逻辑运算中优先使用`&amp;&amp;`**:因其短路行为更安全高效,避免不必要的计算或副作用(如函数调用)。 - **谨慎使用`&amp;`**:仅在需要位运算、取地址或引用声明时使用。在布尔上下文中,`&amp;`可能导致性能问题或逻辑错误。 - **类型安全**:编译器会检查`&amp;&amp;`的操作数是否为布尔类型,而`&amp;`在数值类型上执行位运算(如$ 7 \&amp; 3 = 3 $),在布尔类型上执行逻辑运算[^1]。 #### 总结 运算符`&amp;&amp;`专用于逻辑与,具有短路行为;`&amp;`则多功能,涵盖位运算、取地址、引用逻辑运算,但无短路优化。理解上下文是正确使用的关键[^1][^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

离水的鱼儿

一分也是爱

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

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

打赏作者

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

抵扣说明:

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

余额充值