- Modern C++:Efficient and Scalable Application Development
- Richard Grimes Marius Bancila
- 626字
- 2021-06-10 18:28:07
Passing by value and passing by reference
By default, the compiler will pass parameters by value, that is, a copy is made. If you pass a custom type, then its copy constructor is called to create a new object. If you pass a pointer to an object of a built-in type or custom type, then the pointer will be passed by value, that is, a new pointer is created on the function stack for the parameter and it is initialized with the memory address passed to the function. This means that, in the function, you can change the pointer to point to other memory (this is useful if you want to use pointer arithmetic on that pointer). The data that the pointer points to will be passed by a reference, that is, the data remains where it is, outside of the function, but the function can use the pointer to change the data. Similarly, if you use a reference on a parameter then it means that the object is passed by the reference. Clearly, if you use const on a pointer or reference parameter then this will affect whether the function can change the data pointed to or referenced.
In some cases, you may want to return several values from a function, and you may choose to use the return value of the function to indicate if the function executed correctly. One way to do this is to make one of the parameters an out parameter, that is, it is either a pointer or a reference to an object or container that the function will alter:
// don't allow any more than 100 items
bool get_items(int count, vector<int>& values)
{
if (count > 100) return false;
for (int i = 0; i < count; ++i)
{
values.push_back(i);
}
return true;
}
To call this function, you must create a vector object and pass it to the function:
vector<int> items {};
get_items(10, items);
for(int i : items) cout << i << ' ';
cout << endl
Because the values parameter is a reference it means that when get_values calls push_back to insert a value in the values container it is actually inserting that value into the items container.
If an out parameter is passed via a pointer it is important to look at the pointer declaration. A single * means that the variable is a pointer, two means that it is a pointer to a pointer. The following function returns an int through an out parameter:
bool get_datum(/*out*/ int *pi);
The code is called like this:
int value = 0;
if (get_datum(&value)) { cout << "value is " << value << endl; }
else { cout << "cannot get the value" << endl;}
This pattern of returning a value indicating success is frequently used, particularly with code that accesses data across process or machine boundaries. The function return value can be used to give detailed information about why the call failed (no network access?, invalid security credentials?, and so on), and indicates that the data in the out parameters should be discarded.
If the out parameter has a double * then it means the return value is itself a pointer, either to a single value or to an array:
bool get_data(/*in/out*/ int *psize, /*out*/ int **pi);
In this case, you pass in the size of the buffer you want using the first parameter and on return you receive the actual size of the buffer via this parameter (it is in/out) and a pointer to the buffer in the second parameter:
int size = 10;
int *buffer = nullptr;
if (get_data(size, &buffer))
{
for (int i = 0; i < size; ++i)
{
cout << buffer[i] << endl;
}
//delete [] buffer;
}
Any function that returns a memory buffer must document who has the responsibility of deallocating the memory. In most cases, it is usually the caller, as assumed in this example code.