通过C++中的引用声明传递
Pass by reference declarations in C++
我想确定我对一些基本C++参考原则的理解是否正确。据我了解,声明;
void foo(string &arg); //Means get the memory reference of passed argument
{
cout << arg;
}
string arg;
string& arg1; //Means pass the memory reference of arg
我说的对吗?
编辑
区别在于&
限定符适用于什么:类型,还是变量?
假设您有一个类型 T
.
对于声明/参数(&
是类型限定符(:
T v1 = 13 ; // v1 is a variable of type T, whose value is 13
T v2 = 42 ; // v2 is another variable of type T, whose value is 42
T * v3 ; // v3 is an uninitialized pointer to a variable of type T
T * v4 = &v1; // v4 is pointer pointing to v1 (the value of v4 is the
// address of v1)
T & v5 = v1 ; // v5 is an alias of v1
T & v6 ; // WON'T COMPILE. An alias MUST be initialized.
对于操作(&
是运算符(:
&v1 ; // returns the address of v1 (e.g. 0x00ABCDEF)
v4 ; // returns 0x00ABCDEF (because v4 was initialized to point to
// v1)
*v4 ; // returns the dereferenced value of pointer v4, that is: 13
v5 ; // returns the value inside v1 (the aliased variable of v5),
// that is: 13
我们现在可以混合使用这两种符号:
// We can reattribute the variables pointed by pointers
T * v7 = &v1 ; // v7 is a pointer to the variable v1
*v7 ; // returns v1's value, that is 13
v7 = &v2 ; // v7 is now pointing to v2
*v7 ; // returns v2's value, that is 42
// We cannot reattribute the aliases referencing variables
// because once initialized, aliases **are** the variable they
// were initialized to.
v5 = v2 ; // v5 is an alias of v1, so this won't reattribute it
// instead, it will put the value of v2 into v5 and
// thus v1
// this, writing v5 = v2 is like writing v1 = v2
v2 ; // v2 is still 42
v1 ; // v1 has a value of 42 (since the v5 = v2 line above)
v5 ; // v5 is still the alias of v1, and thus, its value is 42
v2 = 57 ; // v2's value is now 57
v1 ; // v1's value is still 42 (changing v2 has no impact on
// v1 because they are NOT aliases. They are distinct
// variables
v5 ; // v5 is still the alias of v1, and thus, its value is
// still 42
细节 : 关于C
C 语言只有值的概念,以及指向值的指针(以及指向值的指针,以及指向...等(,这意味着您有一个使用一元运算符&
和*
引用引用/取消引用(与C++引用无关...(的概念。
T ** p ; // is the declaration of a pointer to a pointer
// to a value of type T
p ; // is still the pointer to pointer
&p ; // returns the address of the p variable
// meaning you can put that address in a variable
// of type T ***:
T *** pp = &p ;
&&p ; // This has no meaning whatsoever in C and C++
// because an address is a simple raw number, and a
// a number has no address: Only variables have
// addresses
*p ; // this is p, dereferenced once, meaning you get
// to have the value at the address given by p, which
// is still an address (a pointer of type T *)
**p ; // this is p, dereferenced twice, meaning you get
// to have the value at the address given by *p,
// which is of type T
问题在于一元运算符&
和*
并不是真正对称的。例如:
T t = v ;
T * p = &t ;
T * p2 = &t ; // p and p2 are two different pointers containing
// the same address, and thus pointing at the same
// value v
p == p2 ; // is true, because both different pointers contain
// the same address
*p == *p2 ; // is true, because both different pointers point
// to the same value (as they contain the same
// address)
&p == &p2 ; // is false, because each variable p and p2 is
// distinct from the other, and thus, have different
// addresses
所以,在 C 中:
- 一元运算符
*
将在指针变量包含的地址处检索值 - 一元运算符
&
将检索变量的地址
细节 : 关于C++
在C++中,出于多种原因(但首先发现了运算符的需求,但还有许多其他原因,例如值构造函数,并且主要避免用指针和无用的 NULL 测试污染代码(,存在 (C++( 引用的概念,即值的别名:
在C++中,除了将 &
限定符应用于变量(检索其地址(之外,还可以将其应用于类型(使其变量成为引用/别名(。
因此,当您有:
T t = v ;
T * p = &t ; // p is a pointer containing the address of the t
// variable
T ** pp = &p ; // pp is a pointer containing the address of the p
// variable
T & r = t ; // r is a reference to/an alias of t. It behaves as
// if it was t in all aspects
T *& r = p ; // If you understand that line, then you're ready for
// C++ references (i.e. r is an alias of a pointer to T,
// here, an alias of p)
T **& rr = pp ; // rr is an alias of a pointer to a pointer to T,
// here, an alias of pp)
我在这里猜测,但很可能r
和rr
的引用在编译时被优化(即只剩下t
和p
(
细节:约C++11(以前称为C++0x(
由于这个问题被标记为C++0x
,我将讨论它,以及新的 &&
r 值引用。
引用/别名没有从 C++ 更改为 C++11。但是除了C++简单的引用/别名之外,还引入了另一种类型的"引用"(作为 &&
类型限定符(,即 r 值引用。
由于C++具有值语义,因此某些处理可能非常昂贵。例如,如果您以错误的方式编写代码,您可能会有很多无用的临时人员。
添加了移动语义来处理这个问题:如果最终我们将副本转储到垃圾箱,只保留最后一个,为什么要创建同一对象的大量副本?
例如,以下代码:
1 | T foo()
2 | {
3 | T a ;
4 | // put some values in T
5 | return a ;
6 | }
7 |
8 | void bar()
9 | {
10 | T b = foo() ;
11 | }
除非进行优化(想到返回值优化,但也要内联(,否则此代码将创建一个值a
(第 3 行(或键入 T
。当它返回一个类型T
(第 5 行(时,它将创建 a
的临时副本,我们称之为 x
,然后销毁a
。
在第 10 行,值 b
将使用临时值 x
(即所谓的 r 值(进行初始化,然后,x
将被销毁。
这意味着要初始化b
,你创建了两个变量,一个显式(a
(,一个隐式x
(,它们很快就被销毁了,如果构造类型T
很昂贵,这可能会很昂贵。
(作为一个有趣的侧节点,我不得不为这个例子增加很多复杂性,以阻止 g++ 通过 r-v-o 进行优化,并演示移动语义对我的示例代码的影响......
解决方案是创建一个移动构造函数(可能还有一个移动operator =
,以确保完整性(,即具有以下原型的东西:
T::T(T && p_t) ; // move constructor
T & T::operator = (T && p_t) ; // move operator =
这可以与C++通常的复制构造函数/operator =
进行比较:
T::T(const T & p_t) ; // copy constructor
T & T::operator = (const T & p_t) ; // operator =
所以回到上面的例子,我们为T添加移动语义:
class T
{
V * m_v ; // some complex data, expensive to create
// and expensive to destroy
// etc.
}
// destructor :
// Clean its internals if needed
T::~T()
{
delete this->m_v ; // potentially expensive if m_v is not NULL
}
// copy constructor :
// Do not modify the original, and make a copy of its internals
T::T(const T & p_t)
{
this->m_v = new V(p_t.m_v) ; // potentially expensive
}
// move constructor
// the original is a temporary (guaranteed by the compiler)
// so you can steal its internals, as long as you keep the
// temporary clean, no one cares
T::T(T && t)
{
this->m_v = t.m_v ; // stealing the internals of the temporary
t.m_v = NULL ; // the temporary is now "empty"
}
这样,上面的代码(带有 foo
和 bar
,没有任何变化(将避免创建两个 T 类型的临时对象,因为 T 支持移动语义。
PS:添加一个移动构造函数意味着你也应该添加一个移动operator =
。
string str;
string &arg1 = str;
string& arg2 = str;
string *ptr = &str;
表示 arg1
& arg2
是对类型 string
的变量str
的引用,这意味着它们只是变量 str
的别名。
它们本质上都声明了一个引用变量,如上所述,这只是放置&
的样式问题。
ptr
是指向类型为 string
的变量str
的指针。
注意:
引用必须在创建时初始化为变量,并且在初始化后不能引用任何其他变量。引用始终保持同一变量的别名。所以你不应该只是做:
string& arg2;
编译器会为此给你一个错误,如下所示:
错误:"arg2"声明为引用但未初始化
号,您的所有示例看起来都不合法。但忽略那些
- 看起来根本不合法
- 假设 arg 是一个指针,那么 *arg 表示事物的值被指向(严格来说,这是对值的引用,但忽略目前(
- 看起来像一个声明,如果是这样,则声明 arg 作为对字符串
- 和3一样,空格无所谓
没有上下文就不可能说出含义。因此,如果这不清楚,请发布更完整的代码。
- 错误:使用通用引用的声明冲突
- C++(和 ROS) - 包含与前向声明引用,设置默认值和类型定义
- 关于隐式声明的复制构造函数的引用在逻辑上不清楚
- 使用 enable_if 在按值传递与按引用传递之间更改函数声明
- 在C++中,如果成员引用在其声明中初始化,为什么需要存储空间?
- 为什么转发声明的好友类不能在类中引用?
- 如何在方法主体中返回声明向量的引用?
- using声明不能引用类成员
- 是否可以在 using 声明中引用用户定义的转换模板?
- 是否可以在引用另一个派生类的派生类中声明复制构造函数?
- 如果使用返回引用的函数初始化"auto"var,为什么它不声明引用类型?
- C++中未初始化的引用(使用 extern 声明)
- 字符串声明/引用参数(c++)的困难
- 如何声明引用自身的typedef
- 在C++中,我们不能在没有初始化的情况下声明引用。为什么?
- 声明引用适用于类,但不适用于主函数
- 除了类型 &var=var 之外,还有哪些方法可以在C++中声明引用?
- 对派生类的构造函数的未声明引用
- 声明引用的C++开销
- 在循环中声明引用变量