简体   繁体   中英

C++: using & operator for pass-by-reference

I'm teaching myself C++, and in the process I'm writing simple little programs to learn basic ideas. With respect to "pass-by-reference", I'm confused why the following piece of code works (some of the code is just there to practice overloading constructors):

#include <iostream>
#include <string>
using namespace std;

class Dude
{
public:
  string x;
  Dude();                    // Constructor 1
  Dude(const string &a);     // Constructor 2
};

Dude::Dude() : x("hi") {}
Dude::Dude(const string &a) : x(a) {}

int main()
{
  Dude d1;
  Dude d2 = Dude("bye");

  cout << d1.x << endl;
  cout << d2.x << endl;

  return 0;
}

In "main()", I create an object "d2" of type "Dude", and use Constructor 2 to set "x" to be the string "bye".

But in Constructor 2's declaration, I told it to accept an address of a string, not a string itself. So why can I pass it "bye" (which is a string). Why don't I have to create a variable string, and then pass the address of that string to Constructor 2 of Dude?

This actually illustrates one of the coolest and most useful features of C++: Temporary variables. Since you specified that the string reference is const , the compiler allows you to pass a reference to a temporary value to that function. So, here's what's happening behind the scenes with Dude d2 = Dude("bye"); :

  • The compiler determines that the best constructor to use is Dude::Dude(const string &) . How this choice is made is a whole different topic.
  • However, in order to use that constructor you need a string value. Now, "bye" is a const char[4] , but the compiler can trivially convert that to a const char * , and that can be turned into a string . So, an anonymous temporary variable (call it temp1 ) is created.
  • string::string(const char *) is invoked with "bye" , and the result is stored in temp1
  • Dude::Dude(const string&) is invoked with a reference to temp1 . The result is assigned to d2 (actually, it is assigned to another temporary variable and the copy constructor for Dude is invoked with a const reference to it and that is assigned to d2. But in this case the result is the same.)
  • temp1 is discarded. This is where the string destructor string::~string() is run on temp1
  • Control passes to the next statement

I think you're misunderstanding what the & operator does in this context. Taking the address of a variable ( &var ) is different from signifying that a parameter is to be passed as a reference (as you have, in const string &a ).

What your code is actually doing is implicitly creating a new string object that's initialized with the string "bye" , and then that object is passed by reference to the Dude constructor. That is, your code is essentially:

Dude d2 = Dude(string("bye"));

and then the constructor receives that string object by reference and assigns it to x via a copy constructor.

在这种情况下, string有一个构造函数,它接受一个const char*并且没有声明为explicit ,因此编译器将创建一个临时string (使用string("bye")创建,前面提到的构造函数)然后你的const string&设置为指那个临时的。

Two things:

1) There's no such thing as an "address" in your code. const string& means "constant reference to a string ".

You're possibly confused by the fact that the symbol & is also used in an entirely different context as the "address-of" operator to create a pointer: T x; T * p = &x; T x; T * p = &x; . But that has nothing to do with references.

2) You're not actually necessarily using the constructor that you claim for d2 ; rather, you're creating a temporary object with your constructor #2, and then you construct d2 via the copy constructor from the temporary. The direct construction reads Dude d2("bye"); .

当您使用字符串参数调用第二个构造函数时,将创建一个引用该字符串副本的临时变量并将其传递给构造函数。

Constructor 2 is not taking an address to a string, const string& a means a constant reference to an std::string object. The reason why you can pass the constructor a string literal is because the std::string class contains a non-explicit constructor that takes a const char * . So the compiler implicitly converts your string literal to an std::string first before calling Constructor 2.

So the following 2 lines are equivalent

Dude d2 = Dude("bye");
Dude d2 = Dude( std::string("bye") );

Also, when writing constructors, prefer initializing member variables in the initializer list instead of within the body of the constructor

Dude(const string &a) : x(a) {}

temporaries can be bound to a const reference, probably for this reason.

When you call Dude("bye") , the compiler sees if that is a perfect match ( char[4] ) for any constructors. Nope. Then it checks certain conversions ( char* ) still nope. Then it checks user conversions, and finds that std::string can be implicitly constructed from a char* So it creates a std::string from the char* for you, and passes it by reference to Dude 's constructor, which makes a copy. At the end of the statement Dude d2 = Dude("bye"); the temporary string is automatically destroyed. It would be irritating if we had to do the explicit casts ourselves for every single function parameter.

Variables passed to a reference parameter will automatically pass their address instead. This is nice, because it allows us to treat objects with value semantics. I don't have to think about passing it an instance of a string, I can pass it the value "bye" .

Constructor #2 accepts a reference to a const string . That allows it to accept a reference to either a pre-existing object or a temporary object (without the const qualifier, a reference to a temporary would not be accepted).

std::string has a constructor that accepts a pointer to char. The compiler is using that to create a temporary std::string object, and then passing a reference to that temporary to your ctor.

Note that the compiler will only (implicitly) do one conversion like this for you. If you need more than one conversion to get from the source data to the target type, you'll need to specify all but one of those conversions explicitly.

While "&" is an addressof operator, when declared in as part of method definition/declaration, it means that the reference is passed to the method. The reference in this case is d2. Note that D2 is not a pointer, it is a reference. In the constructor, "a" represents the string object with contents "hi". This is a typical example of a pass by reference on a method in C++.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM