通过C ++中的引用声明传递

时间:2011-08-21 10:58:08

标签: c++ reference c++11

我想确定我对某些基本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

我说错了吗?

EDITED

3 个答案:

答案 0 :(得分:13)

不同之处在于&限定符适用于什么:类型或变量?

假设您有类型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语言只有value的概念,指向value的指针(以及指向value的指针和指向...等的指针),这意味着你有一个引用/解除引用的概念(与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)

我在这里猜测,但很有可能在编译时优化引用rrr(即仅剩tp)< / p>

详细信息:关于C ++ 11(formerly known as 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  |  }

除非考虑优化(return-value-optimization,还要考虑inlining),否则此代码将创建值a(第3行)或类型T。当它返回类型T(第5行)时,它会生成a的临时副本,我们将其称为x,然后销毁a

在第10行,将使用临时值b(即所谓的r值)初始化值x,然后,x将被销毁。< / p>

意味着要初始化b,你创建了两个变量,一个是显式的(a),一个是隐含的x),很快被销毁,如果构建类型T很贵。

(作为一个有趣的侧节点,我不得不为这个例子添加了很多复杂性来阻止g ++通过rvo进行优化并演示我的示例代码的移动语义效果......) < / p>

解决方案是创建一个移动构造函数(为了完整性,可能需要移动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"
}

这样,上面的代码(使用foobar,没有任何更改)将避免创建两个T类型的临时对象,因为T支持移动语义。

P.S。:添加移动构造函数意味着您也应该添加移动operator =

答案 1 :(得分:7)

string str;
string &arg1 = str; 
string& arg2 = str; 
string *ptr = &str;

意味着arg1&amp; arg2是对str类型的变量string的引用,这意味着它们只是变量str的别名。
它们基本上都是如上所述声明一个引用变量,它只是放置&的样式问题。

ptr是指向变量str的指针,其类型为string

注意:
必须在创建时将引用初始化为变量,并且在初始化之后不能引用任何其他变量。 Reference始终是同一变量的别名。所以你不应该只是做:

string& arg2;

编译器会给你一个错误,例如:

  

错误:'arg2'声明为引用但未初始化

答案 2 :(得分:0)

由于分号,您的所有示例都不合法。但忽略那些

  1. 看起来不合法
  2. 假设arg是一个指针,那么* arg表示该事物的值 被指出(严格地说是对价值的引用而忽略了 那个)
  3. 看起来像一个声明,如果是,则声明arg作为对a的引用 串
  4. 与3相同,空白无关紧要
  5. 没有上下文,这是不可能说出意义的。因此,如果不清楚发布更完整的代码。