I'm having some trouble trying to understand proper usage of returned references in C++.
I have a class with a "big" object inside. I want to be able to use this object outside the class "read only" mode by returning a const reference to it.
The problem is I don't quite understand when objects get copied and when they don't.
Most of the questions about returning references where about returning objects allocating on the stack, which is not my particular problem, so I've prepared this little example:
class foo {
int a;
public:
foo() {
a = 3;
}
int& getInt() {
return a;
}
const int& useInt() {
return a;
}
void print() {
cout << "Inside class: a = " << a << endl;
}
};
int main() {
foo foo1;
int& goodRef = foo1.getInt();
int badRef = foo1.getInt();
goodRef = 4;
badRef = 5;
foo1.print();
foo1.getInt() = 6;
foo1.print();
int usingIt = 10*foo1.useInt();
}
What I understand is:
In int& goodRef = foo1.getInt(); nothing is copied, only the class owned a exists.
In int badRef = foo1.getInt(); badRef is a copy of a, so there are 2 separate objects, a and badRef.
So depending on what type of object catches the return value it is copied or it is not, so my question is:
When I use the reference like in int usingIt = 10*foo1.useInt(); is it copied and then used to multiply or only the value inside the class is used?
In this example it doesn't matter since it is only an int, but if it was a big object it would be a big deal.
Thanks!
Edit: Thanks to all the answers, but I get that having such methods inside a class is bad, I only put them for the sake of the example.
My actual class has a bunch of objects inside and an interface and bla bla, but one particular object is a glm::mat4. What i want is to be able to write something like glm::vec4 finalVec = foo1.getMatrix() * vec. But I dont want the whole matrix to be copied and then multiplied, rather I want to use the matrix that is already inside my class to perform the multiplication and at the same time not let the user modify it. I supposed something similar to useInt but with the mat4 would work but I wasn't sure and that's why I asked the question.
The glm specificatioon is very confuse for me, but I think the operator * is described as:
glm::mat4 operator* (glm::mat4 const & m, glm::vec4 const & v);
In your example int usingIt = 10*foo1.useInt();, the operator*(), depending on it's argument signature and internals, could cause a copy of your object to take place, at which point it would then be assigned (rather than copied again) into the value of usingIt. If your object was an aggregate or class object type, the copy using the object type's assignment operator would typically be elided using a compiler optimization step.
In general, anytime you are copying a l-value reference type (i.e., T& or const T&) to an aggregate or class object to a non-reference type (i.e., type T), a copy constructor, constructor, assignment operator, or conversion operator is invoked on the returned reference to the object.
If getInt returns int& why are you catching the return value in an int? What do you expect, that the compiler changes your source code to use a reference to int? It can't do anything else than copying the value of the object pointed by the reference.
On the other hand I think you have a bad example. Either you define a single method that returns a const reference:
int const & getInt() { return a; }
Or you provide two methods, one const and one non const:
int& getInt() { return a; }
int const & getInt() const { return a; }
Having both getInt and useInt does not stop anyone from using getInt and actually changing the value of the object in an way that is not intended.
When you use const reference you can only use const methods of this object. E.g. when you have STL vector you can get size, but you can't push_back elements to it.
vector<int> a;
vector<int> &c = a;
const vector<int> &b = a;
a.size();
b.size();
a.push_back(4);
c.push_back(4);
//our vector is now 4 4
//b.push_back(4); compilation error
In C++ you copy when you want to copy: for example
Function which copies:
int x(vector<int> b)
Function which doesn't copy:
int x(vector<int> &b)
You can read more here
Related
#include <iostream>
class class1
{
public:int number = 2;
};
class class2
{
public:
void method(class1 &obj)
{
obj.number;
std::cout << obj.number;
}
};
int main()
{
class1 c1;
class2 c2;
c2.method(c1);
}
What is the difference between running the void method(class1 &obj) with or without &?
Does it change use of memory?
I've heard it's nice to use & so you don't get a NullReferenceException.
I can still access the variable without &, so should i use & in this dumb example?
To make it very simple,
Pass by value means the actual value is passed on. Thus you need to copy this value to a variable to use it.
void method(class1 obj)
Pass by reference means a number (called an address) is passed on which defines where the value is stored.
The address is passed and then no copy is done and the actual object is modified.
void method(class1 &obj)
Pass-by-references is considered to be more efficient than pass-by-value, because it does not copy the arguments to a new variable of the same type (as it uses the actual object).
Also beware as pass-by-value does not modify the real object (only the copy). A great example to look at for instance is the ̀swap two variables`
as follows:
void swap(int a, int b)
{
int t;
t = b;
b = a;
a = t;
}
If you call by value in a main function using `swap(x,y)̀, the inital value (before the call) and the final value (after the call) does not swap.
However, if you define the function instead by reference void swap(int& a, int& b) (nothing changes in the body of the function), you will see that calling swap(x,y), will indeed swap the two variables as the operation.
Why does the inner elements of the vector are copied when the vector is passed by value?
#include<vector>
using namespace std;
// this func won't modify v[2].
// Meaning v[2] (and the whole inner array) was copied
// when v is passed to the func?
void modify(vector<int> v) {
v[2] = 100;
}
// this func modify v[2]
void modify(vector<int>& v) {
v[2] = 100;
}
int main() {
vector<int> v = {1,2,3,4,5,6};
// still same
modify(v);
// modified
modified2(v);
}
I find that it's strange that the actual content of the vector is copied when the vector is passed by value. I picture that the std::vector implementation must have a pointer field which maps to an address on heap, where the actual array is located. So when the vector is passed, even by value, the address should stay the same, pointing to the same content. Something like this:
#include<iostream>
using namespace std;
// a dummy wrapper of an array
// trying to mock vector<int>
class vector_int {
public:
int* inner_array; // the actual array
vector_int(int *a) {
inner_array = a;
}
int* at(int pos) {
return inner_array+pos;
}
};
// this passes the "mocked vector" by value
// but 'inner_array' is not copied
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a);
modify(v); // v[2] is modified
}
Is this assumption about the std::vector implementation correct? What makes the vector content being copied when passed by value?
EDIT
Thanks to alter igel's answer and UnholySheep's comment, I figured out the reason why std::vector has value sementics (or why the inner array got copied).
If the copy constructor class is defined explicitly in the class definition, the copy constructor will determine how the struct/class instance is copied when the variable is passed in a function call. So I can define a copy constructor to my vector_int, in which I copy the whole inner_array, like
#include<iostream>
using namespace std;
class vector_int {
public:
int* inner_array;
int len;
vector_int(int *a, int len) {
inner_array = a;
this->len = len;
}
int* at(int pos) {
return inner_array+pos;
}
// this is the copy constructor
vector_int(const vector_int &v2) {
inner_array = new int;
for (int i =0; i < v2.len; i++) {
*(inner_array+i) = *(v2.inner_array+i);
}
}
};
// Yay, the vector_int's inner_array is copied
// when this function is called
// and no modification of the original vector is done
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a,3);
//
modify(v);
}
I checked the source code of the stdlib implementation on my local computer (g++ Apple LLVM version 10.0.0). The std::vector defines a copy constructor which looks like this
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vector& __x)
: __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
size_type __n = __x.size();
if (__n > 0)
{
allocate(__n);
__construct_at_end(__x.__begin_, __x.__end_, __n);
}
}
which looks like it does an malloc for the actual copied array + copy the array.
C++ allows class types to provide their own code for what it means to be created, copied, moved, and destroyed, and that code is called implicitly, without any obvious function calls. This is called value semantics and it's what C++ uses where other languages resort to things like create_foo(foo), foo.clone(), destroy_foo(foo) or foo.dispose().
Each class can define the following special member functions:
Constructors, for putting the object into a valid initial state
A Destructor, for cleaning up responsibly
A Copy Constructor, for creating a new object that is a duplicate of another
A Move Constructor, for creating a new object by transferring the data of another
A Copy Assignment Operator, for duplicating an object into an existing object
A Move Assignment Operator, for transferring data between two existing objects
These are all functions that you can define to do whatever you want. But they are called implicitly, meaning that users of such a class don't see these function calls in their code, and they expect them to do predictable things. You should make sure that your classes behave predictably by following the Rule of Three/Five/Zero.
There are of course other tools for sharing data, like pass-by-reference, which you already know about.
Lots of classes in the standard library use these special member functions to implement special behaviors that are very useful and help users write safe, correct code. For example:
std::vector when copied, will always have identical elements, though the underlying array and objects contained will be separate.
std::unique_ptr wraps a resource that has only one owner. To enforce this, it can't be copied.
std::shared_ptr wraps a resource that has many owners. It's not totally clear when to clean up such a resource, so copying a shared_ptr performs automatic reference counting, and the resource is cleaned up only when the last owner is done with it.
This is because vector has value semantics: when you copy it you get a true copy of all the elements.
In the library I am designing, I sometimes need read-access to large member variables of classes. Because of their size I don't want to make a getter that returns by copying the member. I don't want them to be modifiable from the outside, so I can't have them public or return references to them. So I thought I would use a "reader":
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
const double& readD() const { return d_; }
private:
double d_;
};
(this is not really meant for doubles)
But here somebody could const_cast the reference and access the data directly. Even without assuming malicious intent, somebody could safe a reference to the data-member and keep it around after the original object has gone out of scope. I know const references can keep a temporary viable, but that doesn't remove the const_cast-problem.
So I came up with a workaround:
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}
I have several questions to this:
Q1) How necessary is this from an efficiency POV? the largest objects I am returning are dense complex matrices with dimensions of maybe 1000x1000. These copies may happen frequently
Q2) Are my concerns about returning by const& valid?
Q3) Does this seem like a good solution? which drawbacks does it have?
Any solution that attempts to fight the language itself is not a good solution.
They should get a wrap on the knuckles if they used a const_cast in that way: the behaviour on trying to change an object via a const_cast on an object that was originally declared as const is undefined. Even if you manage to conjure up a solution to prevent that, a hostile programmer could still take the address of your object, offset that address (using unsigned char* pointer arithmetic) and modify a data member through that pointer!
So if I were you I wouldn't fight the language. Return a const reference if I were you, as per your original suggestion.
Code static analysis tools / compiler warnings / code reviews / human resource departments will help you keep other collaborative programmers on the straight and narrow.
I have two pieces of code:
int f1(int b)
{
return b;
}
int & f2(int b)
{
return b;
}
What is the difference between these functions? I know that the second one returns a reference, but since I can use both functions in the same way, what's the difference?
Edit: Is this function better?
int && f2(int b)
{
return b;
}
And when should I use functions which return references?
Edit2: Then when should I use functions which return Rvalue references?
Consider a simple class that wraps an array solely for the purpose of providing an example of what the OP can do with a returned reference.
class example
{
private:
int array[]= {1,2,3,4,5,6,7,8,9,0};
public:
int get(int index)
{
return array[index];
}
int & get2(int index)
{
return array[index];
}
}
Now we have an example that will not go into the badlands of undefined behaviour and can show you the power of this fully armed and operational reference.
Say we have
example x;
We can call either get function to retrieve a value
int val1 = x.get(1);
int val2 = x.get2(2)
but we can also
x.get2(3) = 30;
because get2 returns a reference we can assign to it and make the assignment stick.
This is invaluable should you want to add an index operator to example
int & operator[](int index)
{
return array[index];
}
because it allows the expected array behaviour
int val = x[5];
x[6] = 10;
EDIT
Tony D brings up another important feature. Returning a reference returns by reference. In addition to allowing modification of the returned object, this does not make a copy and saves whatever effort would have been consumed by making a copy. For the example case of integers this is moot. The cost of passing an integer and a reference to an integer will either be the same or so close that it shouldn't matter. This is not true of a larger, more complex object that could take a significant amount of effort to copy or an object that cannot or should not be copied.
BigFreakingObject & getBigFreakingObject();
will allow the caller to operate on a BigFreakingObject without incurring the costs of duplicating it. This hands over the keys to the kingdom however and allows the caller to do to BigFreakingObject whatever BigFreakingObject's permissions will allow, and this may conflict with the requirements of BigFreakingObject's owner.
Declaring the reference as const with
const BigFreakingObject & getBigFreakingObject();
or
BigFreakingObject const & getBigFreakingObject();
will provide a reference to a BigFreakingObject but not allow the caller to modify its state, protecting the owner of BigFreakingObject from any unpleasant surprises.
For more details on this, read up on Const Correctness.
int f1(int b) {
return b;
}
returns the integer b.
int & f2(int b) {
return b;
}
returns a reference to an integer b that was destroyed when the function returned. In other words, you passed b by value to the function, which means b has an address in the stack frame of the function. Once the function returns, anything in that function's stack frame, including the b that you returned a reference to, no longer exists. So, you have no idea what the reference actually refers to, so you can't use it.
Edit: Your edited function is not better. This would be more correct:
int& f2(int& b) {
return b;
}
Unless you have a situation like the example #user4581301 gave, you should never return a reference to an object created within the function you are returning from, for the reasons described above!
If you want to pass an object to a function, and have the function do something to that object without ever making a copy of the object, do the following:
void f2(int& b) {
... do stuff to b
}
In...
int & f2(int b)
{
return b;
}
...the argument b is an automatic (stack-hosted) copy of the caller-provided value: when f2 returns that stack space is reclaimed. When returning an int&, you're passing back a reference to f2's b variable even as it's memory is made available for reuse. If you make any attempt to access the value using the returned reference, you have undefined behaviour.
I'm learning C++ and I'm still confused about this. What are the implications of return a value as constant, reference and constant reference in C++ ? For example:
const int exampleOne();
int& exampleTwo();
const int& exampleThree();
Here's the lowdown on all your cases:
• Return by reference: The function call can be used as the left hand side of an assignment. e.g. using operator overloading, if you have operator[] overloaded, you can say something like
a[i] = 7;
(when returning by reference you need to ensure that the object you return is available after the return: you should not return a reference to a local or a temporary)
• Return as constant value: Prevents the function from being used on the left side of an assignment expression. Consider the overloaded operator+. One could write something like:
a + b = c; // This isn't right
Having the return type of operator+ as "const SomeType" allows the return by value and at the same time prevents the expression from being used on the left side of an assignment.
Return as constant value also allows one to prevent typos like these:
if (someFunction() = 2)
when you meant
if (someFunction() == 2)
If someFunction() is declared as
const int someFunction()
then the if() typo above would be caught by the compiler.
• Return as constant reference: This function call cannot appear on the left hand side of an assignment, and you want to avoid making a copy (returning by value). E.g. let's say we have a class Student and we'd like to provide an accessor id() to get the ID of the student:
class Student
{
std::string id_;
public:
const std::string& id() const;
};
const std::string& Student::id()
{
return id_;
}
Consider the id() accessor. This should be declared const to guarantee that the id() member function will not modify the state of the object. Now, consider the return type. If the return type were string& then one could write something like:
Student s;
s.id() = "newId";
which isn't what we want.
We could have returned by value, but in this case returning by reference is more efficient. Making the return type a const string& additionally prevents the id from being modified.
The basic thing to understand is that returning by value will create a new copy of your object. Returning by reference will return a reference to an existing object. NOTE: Just like pointers, you CAN have dangling references. So, don't create an object in a function and return a reference to the object -- it will be destroyed when the function returns, and it will return a dangling reference.
Return by value:
When you have POD (Plain Old Data)
When you want to return a copy of an object
Return by reference:
When you have a performance reason to avoid a copy of the object you are returning, and you understand the lifetime of the object
When you must return a particular instance of an object, and you understand the lifetime of the object
Const / Constant references help you enforce the contracts of your code, and help your users' compilers find usage errors. They do not affect performance.
Returning a constant value isn't a very common idiom, since you're returning a new thing anyway that only the caller can have, so it's not common to have a case where they can't modify it. In your example, you don't know what they're going to do with it, so why should you stop them from modifying it?
Note that in C++ if you don't say that something is a reference or pointer, it's a value so you'll create a new copy of it rather than modifying the original object. This might not be totally obvious if you're coming from other languages that use references by default.
Returning a reference or const reference means that it's actually another object elsewhere, so any modifications to it will affect that other object. A common idiom there might be exposing a private member of a class.
const means that whatever it is can't be modified, so if you return a const reference you can't call any non-const methods on it or modify any data members.
Return by reference.
You can return a reference to some value, such as a class member. That way, you don't create copies. However, you shouldn't return references to values in a stack, as that results in undefined behaviour.
#include <iostream>
using namespace std;
class A{
private: int a;
public:
A(int num):a(num){}
//a to the power of 4.
int& operate(){
this->a*=this->a;
this->a*=this->a;
return this->a;
}
//return constant copy of a.
const int constA(){return this->a;}
//return copy of a.
int getA(){return this->a;}
};
int main(){
A obj(3);
cout <<"a "<<obj.getA()<<endl;
int& b=obj.operate(); //obj.operate() returns a reference!
cout<<"a^4 "<<obj.getA()<<endl;
b++;
cout<<"modified by b: "<<obj.getA()<<endl;
return 0;
}
b and obj.a "point" to the same value, so modifying b modifies the value of obj.a.
$./a.out
a 3
a^4 81
modified by b: 82
Return a const value.
On the other hand, returning a const value indicates that said value cannot be modified. It should be remarked that the returned value is a copy.:
For example,
constA()++;
would result in a compilation error, since the copy returned by constA() is constant. But this is just a copy, it doesn't imply that A::a is constant.
Return a const reference.
This is similiar to returning a const value, except that no copy is return, but a reference to the actual member. However, it cant be modified.
const int& refA(){return this->a;}
const int& b = obj.refA();
b++;
will result in a compilation error.
const int exampleOne();
Returns a const copy of some int. That is, you create a new int which may not be modified. This isn't really useful in most cases because you're creating a copy anyway, so you typically don't care if it gets modified. So why not just return a regular int?
It may make a difference for more complex types, where modifying them may have undesirable sideeffects though. (Conceptually, let's say a function returns an object representing a file handle. If that handle is const, the file is read-only, otherwise it can be modified. Then in some cases it makes sense for a function to return a const value. But in general, returning a const value is uncommon.
int& exampleTwo();
This one returns a reference to an int. This does not affect the lifetime of that value though, so this can lead to undefined behavior in a case such as this:
int& exampleTwo() {
int x = 42;
return x;
}
we're returning a reference to a value that no longer exists. The compiler may warn you about this, but it'll probably compile anyway. But it's meaningless and will cause funky crashes sooner or later. This is used often in other cases though. If the function had been a class member, it could return a reference to a member variable, whose lifetime would last until the object goes out of scope, which means function return value is still valid when the function returns.
const int& exampleThree();
Is mostly the same as above, returning a reference to some value without taking ownership of it or affecting its lifetime. The main difference is that now you're returning a reference to a const (immutable) object. Unlike the first case, this is more often useful, since we're no longer dealing with a copy that no one else knows about, and so modifications may be visible to other parts of the code. (you may have an object that's non-const where it's defined, and a function that allows other parts of the code to get access to it as const, by returning a const reference to it.
Your first case:
const int exampleOne();
With simple types like int, this is almost never what you want, because the const is pointless. Return by value implies a copy, and you can assign to a non-const object freely:
int a = exampleOne(); // perfectly valid.
When I see this, it's usually because whoever wrote the code was trying to be const-correct, which is laudable, but didn't quite understand the implications of what they were writing. However, there are cases with overloaded operators and custom types where it can make a difference.
Some compilers (newer GCCs, Metrowerks, etc) warn on behavior like this with simple types, so it should be avoided.
I think that your question is actually two questions:
What are the implications of returning a const.
What are the implications of returning a reference.
To give you a better answer, I will explain a little more about both concepts.
Regarding the const keyword
The const keyword means that the object cannot be modified through that variable, for instance:
MyObject *o1 = new MyObject;
const MyObject *o2 = o1;
o1->set(...); // Will work and will change the instance variables.
o2->set(...); // Won't compile.
Now, the const keyword can be used in three different contexts:
Assuring the caller of a method that you won't modify the object
For example:
void func(const MyObject &o);
void func(const MyObject *o);
In both cases, any modification made to the object will remain outside the function scope, that's why using the keyword const I assure the caller that I won't be modifying it's instance variables.
Assuring the compiler that a specific method do not mutate the object
If you have a class and some methods that "gets" or "obtains" information from the instance variables without modifying them, then I should be able to use them even if the const keyword is used. For example:
class MyObject
{
...
public:
void setValue(int);
int getValue() const; // The const at the end is the key
};
void funct(const MyObject &o)
{
int val = o.getValue(); // Will compile.
a.setValue(val); // Won't compile.
}
Finally, (your case) returning a const value
This means that the returned object cannot be modified or mutated directly. For example:
const MyObject func();
void func2()
{
int val = func()->getValue(); // Will compile.
func()->setValue(val); // Won't compile.
MyObject o1 = func(); // Won't compile.
MyObject o2 = const_cast<MyObject>(func()); // Will compile.
}
More information about the const keyword: C++ Faq Lite - Const Correctness
Regarding references
Returning or receiving a reference means that the object will not be duplicated. This means that any change made to the value itself will be reflected outside the function scope. For example:
void swap(int &x, int &y)
{
int z = x;
x = y;
y = z;
}
int a = 2; b = 3;
swap(a, b); // a IS THE SAME AS x inside the swap function
So, returning a reference value means that the value can be changed, for instance:
class Foo
{
public:
...
int &val() { return m_val; }
private:
int m_val;
};
Foo f;
f.val() = 4; // Will change m_val.
More information about references: C++ Faq Lite - Reference and value semantics
Now, answering your questions
const int exampleOne();
Means the object returned cannot change through the variable. It's more useful when returning objects.
int& exampleTwo();
Means the object returned is the same as the one inside the function and any change made to that object will be reflected inside the function.
const int& exampleThree();
Means the object returned is the same as the one inside the function and cannot be modified through that variable.
Never thought, that we can return a const value by reference and I don't see the value in doing so..
But, it makes sense if you try to pass a value to a function like this
void func(const int& a);
This has the advantage of telling the compiler to not make a copy of the variable a in memory (which is done when you pass an argument by value and not by reference). The const is here in order to avoid the variable a to be modified.