int& getRef() {
int* ptr = new int;
*ptr = 42;
return *ptr;
}
int main()
{
int& ref = getRef();
std::cout << ref;
}
Is ref here a dangling reference after the call to getRef()?
This is how I typically reason about dangling references:
A reference is an alias to an object
If the object is destroyed, then the reference becomes a dangling reference.
However, in this case, it's not clear to me which object is the reference an alias to? And without knowing what the object is, I can't figure out whether that object is destroyed or not :).
Similar to the above, how about this piece of code?
int& getRef(std::shared_ptr<int> sharedPtr) {
return *sharedPtr;
}
int main()
{
auto sharedIntPtr = std::make_shared<int>(42);
int& ref = getRef(sharedIntPtr);
std::cout << ref;
}
Is ref here a dangling reference after the call to getRef()?
No. It references the object you created with new that has not been destroyed.
it's not clear to me which object is the reference an alias to? And without knowing what the object is, I can't figure out whether that object is destroyed or not
It's not destroyed so your program leaks. To destroy the object, you could end your program with:
delete &ref;
In you your second example you are referencing the int managed by the two shared_ptrs and there will be no leaks since the last shared_ptr that goes out of scope destroys the int.
A reference is not exactly an alias, it is a different variable that points to the same address that you initialized it with during all its lifetime.
If you want to know if a object is being destroyed or not in some conditions, you can take advantage of destructors.
#include <iostream>
class myClass
{
private:
int x;
public:
myClass(int _x): x(_x) {}
int getX() const {return x;}
~myClass(){std::cout << "Object distroyed! Value: " << x << std::endl;}
};
myClass& getRef() {
myClass* ptr = new myClass(42);
return *ptr;
}
int main()
{
myClass& ref = getRef();
std::cout << ref.getX() << std::endl;
}
Here we can see that the destructor never runs and therefore the object is not being destroyed. That is because you allocated it in the heap and it will not be destroyed unless you use delete &ref;.
Related
In the following code:
class Foo {
Object Bar;
Object& getBar1()
{
return Bar;
}
Object& getBar2()
{
return &Bar;
}
}
int main(void) {
Foo instance();
Object A = instance.getBar1();
Object& B = instance.getBar1();
Object X = instance.getBar2();
Object& Y = instance.getBar2();
}
What are the differences between getBar1() and getBar2(), if any?
Also, which is the correct way of getting the reference to Bar so that I could manipulate the contents of Bar?
Just because & appear in both cases does not mean that it does the same thing.
When you write SomeType & you are declaring "a reference to an object of type SomeType. When you write & someObject you are asking the a pointer that points to someObject.
Both getBar1 and getBar2 signatures say that they don't receive any argument and that they return a reference to an object of the type Object. That means that in their implementation you must return an object of type Object. But in the implementation of getBar2 you are returning a pointer to an object of type Object instead of an object. Thus, it won't even compile.
Now let's discuss about "storing references".
When you write Object A = instance.getBar1(); the getBar1 function does return a reference, however, it is copied to variable A (the copy constructor of Object will be used to initialize A).
Only when you write Object& B = instance.getBar1(); is that you "store a reference". In that case, B will be a reference to the object returned by getBar1, which must still exist. In that case no constructor will be called, and we can see B as just an alias for whatever object was returned by getBar1.
getBar2() will not compile, as return &Bar; returns an Object* pointer, not an Object& reference.
But, assuming you return a proper reference, then
Object A = instance.getBar1();
and
Object X = instance.getBar2();
are making a copy of the returned Object, so any changes you make to the Object will be on the copy, not on the original.
Using
Object& B = instance.getBar1();
and
Object& Y = instance.getBar2();
any changes made to the returned Object will be made on the original. Likewise if you fix getBar2() to return an Object* pointer:
#include <iostream>
class Object
{
int value;
public:
Object() : value(0) {}
void display() const
{
std::cout << value << std::endl;
}
void setValue(int val)
{
value = val;
}
};
class Foo
{
Object Bar;
public:
Object& getBar1()
{
return Bar;
}
Object* getBar2()
{
return &Bar;
}
};
int main()
{
Foo instance;
Object A = instance.getBar1();
Object& B = instance.getBar1();
Object* C = &(instance.getBar1());
A.setValue(1);
B.setValue(2);
C->setValue(3);
A.display();
B.display();
C->display();
Object X = *(instance.getBar2());
Object& Y = *(instance.getBar2());
Object* Z = instance.getBar2();
X.setValue(4);
Y.setValue(5);
Z->setValue(6);
X.display();
Y.display();
Z->display();
return 0;
}
Output:
1
3
3
4
6
6
Live Demo
I'm not sure to understand why I can modify an object from a const method, look:
#include <iostream>
struct Foo {
int a = 0;
void change() {
a = 3;
}
};
struct Test {
Foo* f;
Test(): f{new Foo} {}
void test() const {
f->change();
}
};
int main()
{
Test t;
std::cout << "before: " << t.f->a << "\n";
t.test();
std::cout << "after: " << t.f->a << "\n";
}
Not only it compiles but it prints:
0
3
So I was able to modify the logical state of an object from a const method. Is that because I used a pointer?
The const applies to the pointer itself, f, not to what this pointer is pointing to. The type of f inside your const-qualified member function, Test::test(), is Foo* const (i.e., const pointer to Foo), not const Foo* (i.e., pointer to const Foo). That's why you can modify what the pointer is pointing to in spite of the const qualification of the member function.
Note that the following sample member function, Test::test2(), does fail to compile since it is const-qualified and tries to modify the pointer data member, f:
void Test::test2() const {
f = nullptr; // <-- error
}
No, you did not modify the logical state of an object:
f->change();
f, the object's class member, is as it's always been, here. Its value hasn't changed. It's not pointing to some other object now. It's still pointing to the same object it's always been pointing to.
What you did modify is the object to which f points to. Which is a different object.
Yes, you modified the logical state of the object. That's allowed, because const prohibits modifying the physical state.
The object t has one data member, f, whose type is "pointer to Foo". When the t object is created, it's f member points at an object of type Foo. After the call t.test() the f member still holds the same address, so it points at the same object of type Foo as it did before.
If test() had tried to change the value stored in f (i.e., the pointer) the const would have prevented it.
void Test::test() const { f = new Foo; } // error: `f` is const in this context
This code below will result in memory loss because rA is initialized as invalid when it is constructed. When can I do to fix this problem?
Use shared_ptr or hope for future compiler versions to catch this bad code?
#include <memory>
using namespace std;
struct A {};
void use(const A& a) {};
unique_ptr<A> foo()
{
unique_ptr<A> pa(new A());
return pa;
}
int main()
{
const A& rA = *foo(); // rA is unusable, initialized with invalid reference (invalidated by destruction of temporary unique_ptr returned from foo)
use(rA);
}
Rewrite your main as:
int main()
{
auto a = foo();
use(*a);
}
As an aside I would rewrite foo as:
std::unique_ptr<A> foo()
{
return std::make_unique<A>();
}
When you return objects by value you return a temporary that will get destroyed immediately unless it is copied or bound to a variable from the caller's side.
What you are doing wrong is binding a reference to something the returned temporary object contains and not the returned object itself. By the time you access the thing you have bound a reference to it has been deleted by the temporary object's destructor when it was destroyed.
To illustrate what you are doing wrong I have written an equivalent example using a std::vector and binding a reference to one of its elements:
void use(const int& a) {}
std::vector<int> foo()
{
return {1, 2, 3};
}
int main()
{
const int& rA = foo()[0]; // bind to an element
// the vector itself is destroyed by the time we get here
use(rA); // whoops using a reference to an element from a destroyed vector
}
I have a function getA() which returns a const reference of base type A, since it's const, it cannot dynamic_cast it, so I make a copy of the const reference and then created a reference to the copied object, but when I call dynamic_cast to the reference of the copied object, it fails, the code is shown below:
struct A {
int c = -1;
virtual ~A() {}
};
struct B : A {int aa = 0;};
const A& getA(){
std::unique_ptr<A> ap(new B);
return *ap;
}
int main()
{
const A& a = getA();
A acopy = a;
acopy.c = -2;
A& acopyr = acopy;
std::cout << a.c << std::endl;
try{
B& b = dynamic_cast<B&>(acopyr);
std::cout << b.aa << std::endl;
}catch(std::bad_cast b){
std::cout << "bad" << std::endl;
}
}
The output is
-1
bad
acopy is an object of dynamic (and static) type A. Notice how it was declared: an object of type A. So of course it cannot be cast to a B&.
From your description, I take it you just want to dynamically cast getA() to a const reference to B. There's nothing stopping you from that:
const B& b = dynamic_cast<const B&>(getA());
Side note: I assume the getA implementation in your question is just for demonstration purposes, but it's very wrong. As soon as ap goes out of scope (that is, as soon as getA returns), it will destroy the object to which it points, so you're returning a dangling reference and thus invoking Undefined Behaviour.
Foo behaves like a circular iterator. Despite me being nervous about it, the code below compiles fine, but creates a run-time error. I receive the error even if I remove the consts from get_current(). Of course, I can return a pointer and it'll work; however, will I get better security returning a reference?
#include <iostream>
#include <array>
#include <memory>
class Foo
{
public:
Foo();
void next();
const int& get_current() const;
private:
std::array<std::unique_ptr<int>, 3> arr_;
unsigned i_;
};
Foo::Foo() : i_(0)
{
arr_[0] = std::unique_ptr<int>(new int(5));
arr_[1] = std::unique_ptr<int>(new int(6));
arr_[2] = std::unique_ptr<int>(new int(7));
}
void Foo::next()
{
++i_;
i_ %= 3;
}
const int& Foo::get_current() const
{
return *arr_[i_];
}
int main()
{
Foo foo;
int* p;
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
foo.next();
*p = foo.get_current();
//do something with p
std::cout << *p << std::endl;
return 0;
}
int* p;
That's an uninitialised pointer, not pointing to anything. Dereferencing it gives undefined behaviour.
*p = foo.get_current();
That dereferences the invalid pointer. Boom!
Perhaps you want it to point to the array element
p = &foo.get_current();
or perhaps you want a copy of the array element
int n;
n = foo.get_current();
foo.get_current(); may well be returning a const reference, but after that you're attempting to take a value copy of that when assigning to *p.
Assigning to *p is what's causing you trouble as p is uninitialised. That's undefined behaviour and is manifesting itself in your case as a runtime error.
You could use code like const int& p = foo.get_current(); but do be aware that a reference can only be bound once, so you'll have to be careful with scoping.
Or, you could use std::shared_ptr<int> and make that the return type of get_current(), and strip your code entirely of bare pointers.
*p = ... You dereference int* P without having it properly initialized.
Change your code in main to
int p; // Remove *
p = foo.get_current();
//do something with p
std::cout << p << std::endl;
or if you really meant to use a pointer
const int* p;
p = &foo.get_current();
// ^ Take the address