I'm fairly new to Pass By Reference, and I HAVE to make sure I understand this correctly. I have to convert all my Heap memory to Stack memory because my professor said so, and I'm stuck on two concepts.
What is the best way to store a reference in a class? I originally had member objects as non pointers, but noticed the deconstructor would be called on the member variable when the object (not member object) was popped off the stack. This makes me think it was a copy, and not actually a reference.
Here is an example of what I had originally:
class B
{
public:
B();
~B();
};
class A
{
private:
B b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = bVar;
}
My solution was to change it to a pointer so it didn't call the deconstructor, but I'M NOT SURE IF THIS IS THE CORRECT WAY TO DO IT. Here was my solution:
class B
{
public:
B();
~B();
};
class A
{
private:
B* b;
public:
A();
~A();
setB(B& bVar);
};
void A::setB(B& bVar)
{
b = &bVar;
}
My second question is kind of related. I'm not sure what exactly happens when you have:
object1 = object2&.
Is object1 a copy or is it actually another identifier for object2?
References behave like symbolic aliases to instances, and are in some respects like "pointers" that can't (shouldn't) be null. For the sake of this explanation, I'll refer to them below as though they were pointers.
When you have a T&, it means that it is pointing to a T, and is not itself a copy.
When you have a T = T&, it means you'll get a copy (or a copy of a copy) depending on how the constructor or assignment operator are defined.
When you have an R& = L, it means you'll get copy of L into whatever the R& is pointing to (provided the assignment operator of R permits this).
Concerning the "correct" way of storing references, I would ask at least these questions:
Is it acceptable for the member reference to remain the same throughout the containing object's lifetime?
Will instances of the containing type always be destroyed before the object(s) pointed to by the member reference?
If both are true, then simply declaring and appropriately initializing a member T& should suffice:
class B
{
// details...
};
class A
{
B &_b;
public:
A(B &b) :
_b(b)
{}
};
Otherwise, despite the requirements imposed upon you, the situation might call for something like shared_ptr<> or similar.
References to objects living on the stack, in-turn held by other objects that themselves may be constructed in such a way that they will outlive their reference's lifespan are merely pointers waiting to dangle.
Consider copying, or arguing that heap-allocated memory is the better option.
If you are uncertain of the reference network induced by your program, you need to redesign it.
EDIT:
It is important to note that, when passing a reference to a function (const T& in particular), there are certain situations in which it can be elided by the compiler. For example: When such a function is inlined, references can be replaced by more efficient addressing logic than if they were required to be pointers.
In this respect, they are not pointers.
Related
I have a vector of class instances. Each of those instances has a unique_ptr to another class.
Since I never try to copy the class instance or even share the pointer, I felt like unique_ptr are more appropriate than shared_ptrs since the pointer is not shared, but only accessible through the class instance.
Is it bad practice? And why wouldn't this work? I understand that copying an instance to a unique pointer would be ill-formed, but since I move it, I do not understand why this would not be allowed?
Would I have to create a custom move constructor? And what should it do?
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Code Example for better understanding:
class A {
private:
int number;
public:
void f() {
std::cout << "The number is: " << number;
}
A(int i) : number{i} {}
~A() = default;
};
class B {
std::unique_ptr<A> good_a;
B() : good_a{ std::make_unique<A>(1) } {}
~B() = default;
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
This answer focuses on compilation error you seem to be having. Bad or good practice is left for others to chime in.
Your code have several errors there, but the main one seems to be that your custom B( ) constructor inhibits default move constructor. If you add it, your code becomes well-formed.
Here is a full working code for the reference:
#include <memory>
#include <vector>
class A {
private:
int number;
public:
void f();
A(int i) : number{i} {}
~A() = default;
};
struct B {
std::unique_ptr<A> good_a;
B(int k) : good_a{ std::make_unique<A>(k) } {}
B(B&& ) = default;
B& operator=(B&& ) = default; // not needed for the example, but good for completeness
};
int main()
{
std::vector<B> all;
B my_b(123);
all.emplace_back(std::move(my_b));
}
And why wouldn't this work?
What you described could work.
I do not understand why this would not be allowed?
What you described would be allowed.
Would I have to create a custom move constructor?
No, that wouldn't be necessary, unless you define other special member functions, or have other members (beside the unique pointer) that have deleted or private move constructor.
The unique ptr should be deleted as soon as the object instance is being removed from the vector as there are no references left, right?
Members are destroyed when the super object is destroyed. And the destructor of the unique pointer invokes the deleter on its owned pointer.
Whether there are references to the pointed object has no effect on whether it is deleted or not. Anything referring to the deleted object will be left dangling.
Is it bad practice?
There isn't necessarily anything particularly bad about what you described in general, but that depends on exact details.
One potential issue is that dynamic allocation can be expensive in some cases, and using it unnecessarily would then be unnecessarily expensive. As such, you should to have some reason to allocate the pointed objects dynamically rather than storing them directly as members.
Bugs in your example:
You attempt to initialise B(123) but B has no constructor accepting an integer.
You attempt to initialise a B outside a member function of B, but its constructors and the destructor have private access.
You have user declared destructor for B, but no user declared move constructor or assignment operators and therefore the class isn't movable, which is a requirement for storing in std::vector.
Here is a fixed version that doesn't use unnecessary dynamic allocation:
struct A {
int number;
};
struct B {
A good_a;
};
B my_b{123};
all.push_back(my_b);
Please read this answear.
Depending what is explicitly declared respective constructors with default implementation are implicitly defined or dropped. Rules are described by this table:
Since you have used explicit defined destructor (as default) you have disabled ("not declared") move constructor.
So to fix it you have to explicitly define move constructor or remove definition of destructor: https://godbolt.org/z/dr8KrsTfq
Suppose I have a class A, the class is copyable, but it will take a lot to copy it.
Suppose I have a class B, it can return a const reference of A.
class B
{
public:
...
const A& obtainA() const
{
return a;
}
private:
A a;
};
When I want to use A from B, I think the best practice is accessed A in B by const reference
B b;
const A& a = b.obtainA(); // do not needs to the heavy copy operation of A, good.
However, I always forget the const reference, my code looks like follow.
B b;
A a = b.obtainA(); // leads to the heavy copy operation of A, not good
It not only leads to the heavy copy operation but also the unsafety.
If the a call the non-const member function by mistake, it will leads to the member a in b be changed out of class.
Is there exists any way can help me to return const reference?
I want something like, if not returned by reference, the code can not be compiled.
Or any other advice is OK.
Thanks for your time.
Concrete applications
A in my application is the shared array. The data stored in the array is shared, it just something like the std::shared_ptr, but manage array of object but not single object.
The array is designed can be shared to reduce the copy overhead. The A is copyable because the array should be copyable.
The array is a widely used class, thus it is often a member of Array's user, that is the B in this question.
You could make A non-copyable, and provide a method to do the copy explicitly.
class A {
private:
A(A const &); // expensive copy
public:
A make_copy() { return *this; }
};
The make_copy method will create the expensive copy, but other code will no be able to make copies implicitly, since the copy-constructor is private.
If you do not want an object to be modified, make the object itself constant:
B const b;
A a = b.obtainA(); // nope, not allowed
I personally believe that everything should be constant unless there is a good reason for it not to be.
In the code below, I made p const because it will never point to any other int during Foo's lifetime. This doesn't compile, as the unique_ptr's copy constructor is called, which is obviously deleted. Are there any solutions besides making p non-const? Thanks.
#include <memory>
using namespace std;
class Foo
{
public:
//x is a large struct in reality
Foo(const int* const x) : p(x) {};
Foo(Foo&& foo) : p(std::move(foo.p)) {};
private:
const unique_ptr<int> p;
};
The semantics of your move constructor are contradictory.
You have declared a const std::unique_ptr which will (uniquely) own the value it is initialised with.
But you've declared a move constructor that should move that value into another object at construction.
So what do you think should happen to the std::unique_ptr in the 'temporary' being move constructed from?
If you want it to be release()ed you've violated its constness.
If you want it to retain its value you've violated the constraint of std::unique which requires no more than one such object to own any given object.
Checkmate.
This problem reveals a subtle limitation of the C++ language. It requires move semantics to leave the copied to and from as valid objects.
There are several quite reasonable proposals for 'destructive move' which would in truth better reflect what most uses of move are doing - take a value to here from there 'invalidating' what was there.
Google them. I haven't made a literature survey so don't want to recommend one.
Your alternatives here are to remove const or cast it way.
I strongly recommend removing it. You can make sure the semantics of your class ensure the appropriate const-ness with no impact and no 'ugly suspect' const_cast.
#include <iostream>
#include <memory>
class Foo
{
public:
Foo(const int x) : p(new int(x)) {};
Foo(Foo&& foo) :
p(std::move(foo.p)) {
};
int get(void)const{
return *(this->p);
}
private:
std::unique_ptr<int> p;
};
Foo getMove(){
return Foo(88);
}
int main(){
Foo bar(getMove());
std::cout<<bar.get()<<std::endl;
return EXIT_SUCCESS;
}
To understand why your code does not compile, reflect how you have declared Foo class and how move semantics is generally implemented.
Declaring a const unique_ptr<T> p, you mean that p itself will be never modified, but you could still modify the pointed-to object because of T is not const.
But move works on an opposite assumption. This feature uses the idea that is allowed stealing resources from objects and leave them in a empty state (if an empty state make sense). If can be useful, think move as a sort of 'destructive' copy for the moved object.
Writing std::move(foo.p), basically you steal the resource pointed by foo.p and leave it in a safe state, that means assign foo.p to NULL. But foo.p was declared as const, so the operation is not permitted.
Please consider that in your case you don't need to declare p as a const unique_ptr<int>. Simply declare it as unique_ptr<int> and make sure that member functions are declared as const and non-member functions take it as
const unique_ptr<int> p& parameter. In this way you are sure that p will never change along the object lifetime (except in case of move operation).
It is because unique_ptr has only the move-constructor, which means the initialization argument to p cannot be const, while p is const. I think what you wanted was to declare
unique_ptr p;
instead of
const unique_ptr p;
class Foo {
public:
// x is a large struct in reality
Foo(const int* const x) : p(x) {};
Foo(Foo&& foo) : p(std::move(foo.p)) {};
private:
const unique_ptr<int> p;
};
Concept of using std::unique_ptr is representing a sole ownership of an object. What you're trying to achieve is having a Foo class own an object (which is expressed by std::unique_ptr) and making it movable (your move constructor) which makes a contradiction. I would stick with std::unique_ptr or make it shared using std::shared_ptr.
You might want to read this:
Smart Pointers: Or who owns you baby?
If you want to prevent transfer of ownership, you can use a const std::unique_ptr<T>. This is not very useful.
If you want to prevent modifying the object it holds, you can use a std::unique_ptr<const T>.
Given the new C++11 standard and the move semantics introduced in it, could it be possible to create default values for constructor references? Here Default value to a parameter while passing by reference in C++ it is said that no, but perhaps the new standard allows some trickiness.
Basically what I want is to use a default object for the normal usage and to pass a mock object that records all the calls the host object has done in the testing phase. It should be something like
class A {
B& b;
public:
A(B & b = B()){} // This does not work
}
and when testing what I want is
BMock bMock;
A a(bMock);
bMock.getStatistics();
Any ideas?
How about this:
class Foo
{
B default_B;
public:
std::reference_wrapper<B> b;
Foo() : b(std::ref(default_B)) { }
Foo(B & br) b(std::ref(br)) { }
};
Here's an alternative idea that might be lighter-weight, at the expense of dynamic allocation in the default case:
#include <memory>
class Foo
{
std::unique_ptr<B> pB;
public:
std::reference_wrapper<B> b;
Foo() : pb(new B), b(std::ref(*pb)) { }
Foo(B & br) pb(), b(std::ref(br)) { }
};
Yes, the default initializer expression is a temporary object, and it will bind to an rvalue reference. However, you need to pass non-temporary objects through std::move to get them to bind to the reference.
Usually passing something through std::move indicates that it will be destroyed by the called function. The standard goes so far as to call the result of std::move an "expiring value;" although there's no real semantic necessity for it to expire, it's a pretty strong convention.
So, you can do this, but be careful.
Looking closer at your example, you want to keep a reference to b inside a, which means that there's nowhere to permanently store the proposed default value if no argument were used. You would be retaining a reference to a destroyed temporary. So unfortunately I can't really adapt your example…
Just an idea, what about this:
class A
{
std::shared_ptr<B> m_b;
public:
A() : m_b(new B) {}
A(B& b) : m_b(&b, [](B*){}) {}
};
On another note, I'm against storing off anything you take as a reference, I don't feel it connotes your usage at all. I'm also against members which are references, as they disallow assignment operators.
I've got a class A, which consists of objects B and C. How to write a constructor of A that gets B and C objects? Should I pass them by value, by (const) reference, or a pointer? Where should I deallocate them?
I thought about pointers, because then I could write:
A a(new B(1,2,3,4,5), new C('x','y','z'))
But I don't know whether it's a good practice or not. Any suggestions?
Usually you pass by const reference:
A a(B(1,2,3,4,5), C('x','y','z'))
No need for pointers here.
Usually you store values unless copying is too inefficient.
The class definition then reads:
class A {
private:
B b;
C c;
public:
A(const B& b, const C& c): b(b), c(c) { }
};
Should I pass them by value, by (const) reference, or a pointer?
By const reference if object is big
by value if object is small
by const pointer if it is an optional argument that can be zero (i.e. "NULL")
by pointer if it is an optional argument that can be zero but will be owned (i.e. deallocated) by constructed class.
Please note that if your class have internal instances of B and C, then passing them by reference, value or const reference, will most likely involve using copy constructor or assignment operator. Which won't be necessary with pointers.
A a(new B(1,2,3,4,5), new C('x','y','z'))
Normally(i.e. not always) it is a bad idea, because:
if A doesn't deallocate arguments, you have a memory leak.
If A takes ownership of arguments and deallocates them, then you won't be able to pass values allocated on stack as arguments. Still, depending on your code design this may be acceptable (Qt 4 frequently takes ownership of objects created with new)
Where should I deallocate them?
The best idea is to make sure that compiler deallocates arguments automatically for you.
This means passing by reference, const reference or by value. Or using smart pointers.
What you pass in depends on your needs.
Do you need a copy of the thing you are passing in? Then pass by const-reference.
struct A
{
A(const B& b, const C& c) : m_b(b), m_c(c) {}
private:
B m_b;
C m_c;
};
And construct it like this:
A myA(B(1,2,3), C(4,5,6));
If you want your A object to refer to some other B and C objects (but not own them) then use pointers (or possibly references).
Edit: The examples given here do not respect the rule of the Big Three (thanks #Philipp!). If the definition of A is used as given below, the code will crash on copy construction for A, or on assignment for A. To define the code correctly, the assignment operator and copy constructor should be explicitly defined for A (or explicitly forbidden - declared as private and never implemented). (end Edit)
Should I pass them by value, by
(const) reference, or a pointer?
If A uses B and C, then hold them by reference or pointer inside of A. To choose between reference and pointer, see how B and C are allocated.
If they are local stack objects constructed in the same scope as A, then pass them by const reference.
If they are dynamically allocated objects that A uses, make A own them: pass them by pointers, and have A's destructor delete them.
If they are optional components of A, pass them by pointer (that can be null).
If A is not responsible of deleting them, pass them by * const.
Where should I deallocate them?
Usually where you no longer need them :).
If they are needed past the scope of A (if they are external objects that A uses) then delete them when A's scope is complete.
If they are owned by A, delete them in the destructor for A. It may make sense to also delete them during the lifetime of A, if the pointers should be changed.
Here's an example, where B is a replaceable component injected into A (and owned by A) and C is an optional component owned by A (but injected into A also).
("owned by" means A is responsible for deleting both objects)
class B;
class C;
class A
{
B* b;
C* c;
public:
A(B* const bb, C* const cc = 0) // cc is optional
: b(bb), c(cc)
{
}
void resetB(B* const bb = 0)
{
delete b;
b = bb;
}
~A()
{
resetB();
delete c;
}
};
{
A a(new B, new C);
a.resetB(); // delete B
a.resetB(new B); // delete former B and set a new one
} // both members of A are deleted
But I don't know whether it's a good
practice or not. Any suggestions?
It's up to you really, but you can write A a(B(1, 2, 4), C(1, 2, 3)) as easy as A a(new B(1, 2, 4), new C(1,2,3)); (in the former case - the one without new - the A::b and A::c should be references or objects/values inside the class, and A should not delete them at all).
The question should not be if you want to write the statement with dynamic allocation for B and C but if you need to. Dynamic allocation is slow and if you don't have a requirement for it you shouldn't do it.