Suppose I have a C++ class with an attribute that is a reference:
class ClassB {
ClassA &ref;
public:
ClassB(ClassA &_ref);
}
Of course, the constructor is defined this way:
ClassB::ClassB(ClassA &_ref) : ref(_ref) { /* ... */ }
My question is: When an instance of class 'ClassB' is destroyed, is the object referenced by 'ClassB::ref' also destroyed?
A reference is nothing but an alias for a variable, the alias gets destructed, not the actual variable. You could consider it some kind of pointer, but there are reasons to refrain from this kind of (evil) thoughts :).
No. Reference members do not affect the lifetime of whatever they point to. This means the thing they alias may have a longer or a shorter lifetime than that of the reference.
On the other hand, const references can affect the lifetime of what they point to if they point to a temporary.
In your case it does not.
No. That's why you need a ~ClassB destructor if ClassB is responsible for the storage of ref which it might not be.
When an object is eliminated in C++, its memory is deallocated and thus everything that was embedded in it (such as member variables) is lost as well.
In the case of a pointer, the pointer is a member variable that contains an address, so the address is "destroyed" but the referenced object, if any, is not.
In the case of a reference member, the address is destroyed, but the target is not affected.
A class may define a destructor that could define special behaviors. One common such behavior is to invoke cleanup operations on members (if any), and to deallocate memory that was dynamically allocated earlier. Here, however, you already got an object so you should not be the one deallocating it.
No; references are merely an alternate syntax for pointers. The value they reference won't be modified if the reference is deallocated.
If you want it to be destroyed, you will have to encapsulate it (normally done via "smart" pointers, like std::shared_ptr or std::unique_ptr), that will automatically release the memory in an appropriate fashion on destruction of B. In-language references have no memory freeing behaviour associated with them, except the actual memory of the reference itself, as opposed to the referred.
You will have to build and understand your own memory model. People typically use shared_ptr and reference counting for basic uses.
I don't have the C++ spec on hand, but my guess is "No".
Pointers aren't deleted automatically when an object is destroyed, I see no reason that a reference should be different. Plus, having the reference automatically destroyed would be ripe for interesting bugs.
Related
Consider this code:
class A : public std::enable_shared_from_this<A>
{
public:
std::shared_ptr<A> f()
{
return shared_from_this();
}
};
int main()
{
A a;
std::shared_ptr<A> ptr = a.f();
}
This code terminated in Visual Studio 2017. I guess I am doing something wrong here. Can anyone help me with this? I want a shared_ptr on a created by shared_from_this().
Because a is not a owned by a shared pointer. From cppreference:
It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr. Otherwise the behavior is undefined (until C++17)std::bad_weak_ptr is thrown (by the shared_ptr constructor from a default-constructed weak_this) (since C++17).
You cannot use shared_from_this to generate a new shared pointer. You already have to have an existing shared pointer to get a new one:
std::shared_ptr a(new A());
auto ptr = a->f(); // ok, 'ptr' shares ownership of newed object with 'a'
The issue is fundamental in the design (not technical details)
Whatever the exact specification of that C++ standard version you are using, what you are trying to do is impossible. Knowing the fine details of the specification of shared_from_this isn't needed to conclude that the code contains a design contradiction: just understanding the intent, which is to obtain a shared_ptr<A> to this, inside a member function called on a, an automatic object, is enough to determine that the design is in error.
In fact, any attempt at making an owning smart pointer (incl. but not limited to unique_ptr, shared_ptr) that points to (that owns) an object with "scoped" lifetime, that is an object whose lifetime is defined by the scope of the declaration of the object and ended by exiting something:
automatic objects (lifetime ends when exiting the scope),
namespace scope objects, static object members of classes (lifetime ends at program exit),
non static members of classes (lifetime ends when the body of the destructor of the containing class object exits),
is a design error, because the:
these objects were not created with any variant new (plain operator new or nothrow variant) that permits the call of delete on the result);
the only case where C++ allows you to destroy such object is by destruction followed (preferably immediately) by reconstruction with placement new of an object with the same complete type, which is obviously not the job of an owning smart pointer;
the compiler will destroy that object when program execution reach the exit point (exits scope, exits destructor, or std::exit, or return from main), no matter what (even if your owning smart pointer already took care of it); trying to destroy an already destroy object is not OK.
This includes constructing a smart pointer that owns (i.e. that promises to delete) a member of a class instance that was dynamically allocated:
struct A {
int m;
};
#define OK 1
void f() {
A *p = new A;
#if OK
std::shared_ptr<A> own (p); // fine
#else
std::shared_ptr<int> own (&p->m); // bad
#endif
}
Here the lifetime of the object pointed to by p is managed dynamically; the timing of the destruction of determined explicitly, by program code, and the lifetime of the unique member m is intrinsically linked to the lifetime of the A object; but the member itself need not be destructed explicitly and shall not be deleted. If the OK preprocessor constant is 1, all is well; if it is 0, you are trying to manage explicitly the lifetime of a member, which is unsound.
About the term "explicit" call to delete: although th delete operator never appears in the code, its call is implicit on the use of std::shared_ptr; in other words, std::shared_ptr explicitly uses delete, so use of std::shared_ptr (or other similar owning smart pointers) are indirect use of delete.
Safely sharing ownership with a smart pointer
The only safe way to share ownership of a shared_ptr is to make one from another shared_ptr, directly, or indirectly. This is the fundamental property of shared_ptr: all instances pointing to the one object must be traceable back to the one instance that one constructed with a raw pointer (or alternatively with make_shared).
This is a direct consequence of the fact that the ownership information (usually a reference count, but could be a linked list if you love inefficient implementations) is not inside the managed object, but inside the information block created by shared_ptr. This is not a property of just std::shared_ptr, it's a fact of life of all these externally managed objects, without a global registry, it's impossible to find the manager.
The basic design decision of these smart pointer is that the managed object need not be modified to use a smart pointer; hence they can be used on existing data type (incl. fundamental types).
Importance of weak copies of a shared owning manager
The fundamental property of shared_ptr would create an issue: as every layer of code (that might need to call a function that needs an owning pointer) needs to keep a copy of the shared_ptr around, this can create a web of owning smart pointers, some of which might reside in an object whose lifetime is managed by another who lifetime is managed by that exact smart pointer; because the smart pointer basic specification is that the managed object is not destructed before all copies of the smart pointer in charge of its destruction are destroyed, these objects would never be destroyed (as specified; this is not a consequence of the particular implementation choice of reference counting). Sometimes a copy of a owning smart pointer of a specie that doesn't prevent influence the lifetime of the managed object is needed, hence the need for the weak smart pointer.
A (non null) weak smart pointer is always directly or indirectly a copy of an owning smart pointer, directly or indirectly a copy the original smart pointer that took ownership. That "weak reference" actually is a "strong" owning smart pointer to the information regarding the existence of other owning copies of the smart pointer: as long as there is a weak smart pointer, it will be possible to determine whether there is a live owning smart pointer, and if so to obtain a copy, that is make a shared smart pointer that an exact copy of the original (the lifetime of the original may have ended many generations of copies ago).
The only purpose of a weak smart pointer is to obtain such copies of the original.
The purpose of std::enable_shared_from_this
The only use of std::enable_shared_from_this is to obtain a copy of the original shared_ptr; that implies that such owning smart pointer must already exist. No new original (another smart pointer taking ownership) will be made.
Only use std::enable_shared_from_this for classes that are only intended to be managed by a shared_ptr.
Details of std::enable_shared_from_this
All that being said about the theoretical principles, it's useful to understand what std::enable_shared_from_this contains, how it can produce a shared_ptr when used correctly (and why it cannot be expected to work in any other case).
The "magic" of std::enable_shared_from_this may seem mysterious, and too magic so that users don't have to think about it, but it's actually extremely simple: it keeps a weak_ptr intended to be a copy of the original. Obviously it cannot be constructed as such copy, as the original cannot even be initialized when the std::enable_shared_from_this subobject is constructed: a valid owning smart pointer can only refer to a fully constructed object, because it owns it and is in charge of its destruction. [Even if by some cheating an owning smart pointer was made before the managed object was fully constructed, and hence destructible, the owning smart pointer would be a risk of premature destruction (even if during the normal course of events its lifetime is long, it could be shortened by an exception for example).]
So data member initialization in std::enable_shared_from_this is inherently default initialization: the "weak pointer" is null at that point.
Only when the original finally takes ownership if the managed object, it can collude with std::enable_shared_from_this: the constructing of the original shared_ptr will set once and for all the weak_ptr member inside std::enable_shared_from_this. Active collusion between these components is the only way to make the stuff work.
It's still the user's responsibility to only call shared_from_this only when it can possibly return a copy of the original, that is, after the original has been constructed.
About fake (non owning) owning smart pointers
A fake owning smart pointer is one that does no cleanup ever: owning smart pointer in name only. They are special case of "owning" smart pointers used in such a way that no destruction or cleanup is performed. This ostensibly means that they could be used for objects whose lifetime is predetermined (and long enough) and for which there is a need to have a pretend owning smart pointer; unlike a real owning smart pointer, keeping a copy will not extend the lifetime of the object, so that lifetime should better be really long. (Because a copy of a owning smart pointer could be stored in a global variable, the object could still be expected to be alive after a return from main.)
These non owning owners are obviously a contradiction in the terms and rarely safe (but can be proven safe in a few cases).
They rarely solve a legitimate problem (one that isn't the immediate consequence of a very bad design): a shared_ptr in an interface means that the receiver is expecting to be able to extend the lifetime of the managed object.
I am exploring the possibility of implementing true (partially) immutable data structures in C++. As C++ does not seem to distinguish between a variable and the object that variable stores, the only way to truly replace the object (without assignment operation!) is to use placement new:
auto var = Immutable(state0);
// the following is illegal as it requires assignment to
// an immutable object
var = Immutable(state1);
// however, the following would work as it constructs a new object
// in place of the old one
new (&var) Immutable(state1);
Assuming that there is no non-trivial destructor to run, is this legal in C++ or should I expect undefined behaviour? If its standard-dependant, which is the minimal/maximal standard version where I can expect this to work?
Addendum: since it seems people still read this in 2019, a quick note — this pattern is actually legally possible in modern (post 17) C++ using std::launder().
What you wrote is technically legal but almost certainly useless.
Suppose
struct Immutable {
const int x;
Immutable(int val):x(val) {}
};
for our really simple immutable type.
auto var = Immutable(0);
::new (&var) Immutable(1);
this is perfectly legal.
And useless, because you cannot use var to refer to the state of the Immutable(1) you stored within it after the placement new. Any such access is undefined behavior.
You can do this:
auto var = Immutable(0);
auto* pvar1 = ::new (&var) Immutable(1);
and access to *pvar1 is legal. You can even do:
auto var = Immutable(0);
auto& var1 = *(::new (&var) Immutable(1));
but under no circumstance may you ever refer to var after you placement new'd over it.
Actual const data in C++ is a promise to the compiler that you'll never, ever change the value. This is in comparison to references to const or pointers to const, which is just a suggestion that you won't modify the data.
Members of structures declared const are "actually const". The compiler will presume they are never modified, and won't bother to prove it.
You creating a new instance in the spot where an old one was in effect violates this assumption.
You are permitted to do this, but you cannot use the old names or pointers to refer to it. C++ lets you shoot yourself in the foot. Go right ahead, we dare you.
This is why this technique is legal, but almost completely useless. A good optimizer with static single assignment already knows that you would stop using var at that point, and creating
auto var1 = Immutable(1);
it could very well reuse the storage.
Caling placement new on top of another variable is usually defined behaviour. It is usually a bad idea, and it is fragile.
Doing so ends the lifetime of the old object without calling the destructor. References and pointers to and the name of the old object refer to the new one if some specific assumptions hold (exact same type, no const problems).
Modifying data declared const, or a class containing const fields, results in undefined behaviour at the drop of a pin. This includes ending the lifetime of an automatic storage field declared const and creating a new object at that location. The old names and pointers and references are not safe to use.
[Basic.life 3.8]/8:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or
released, a new object is created at the storage location which the original object occupied, a pointer that
pointed to the original object, a reference that referred to the original object, or the name of the original
object will automatically refer to the new object and, once the lifetime of the new object has started, can be
used to manipulate the new object, if:
(8.1)
the storage for the new object exactly overlays the storage location which the original object occupied,
and
(8.2)
the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
(8.3)
the type of the original object is not const-qualified, and, if a class type, does not contain any non-static
data member whose type is const-qualified or a reference type, and
(8.4)
the original object was a most derived object (1.8) of type
T
and the new object is a most derived
object of type
T
(that is, they are not base class subobjects).
In short, if your immutability is encoded via const members, using the old name or pointers to the old content is undefined behavior.
You may use the return value of placement new to refer to the new object, and nothing else.
Exception possibilities make it extremely difficult to prevent code that exdcutes undefined behaviour or has to summarially exit.
If you want reference semantics, either use a smart pointer to a const object or an optional const object. Both handle object lifetime. The first requires heap allocation but permits move (and possibly shared references), the second permits automatic storage. Both move manual object lifetime management out of business logic. Now, both are nullable, but avoiding that robustly is difficult doing it manually anyhow.
Also consider copy on write pointers that permit logically const data with mutation for efficiency purposes.
From the C++ standard draft N4296:
3.8 Object lifetime
[...]
The lifetime of an object of type T ends when:
(1.3) — if T is a class
type with a non-trivial destructor (12.4), the destructor call starts,
or
(1.4) — the storage which the object occupies is reused or
released.
[...]
4 A program may end the lifetime of any object by
reusing the storage which the object occupies or by explicitly calling
the destructor for an object of a class type with a non-trivial
destructor. For an object of a class type with a non-trivial
destructor, the program is not required to call the destructor
explicitly before the storage which the object occupies is reused or
released; however, if there is no explicit call to the destructor or
if a delete-expression (5.3.5) is not used to release the storage, the
destructor shall not be implicitly called and any program that depends
on the side effects produced by the destructor has undefined behavior.
So yes, you can end the lifetime of an object by reusing its memory, even of one with non-trivial destructor, as long as you don't depend on the side effects of the destructor call.
This applies when you have non-const instances of objects like struct ImmutableBounds { const void* start; const void* end; }
You've actually asked 3 different questions :)
1. The contract of immutability
It's just that - a contract, not a language construct.
In Java for instance, instances of String class are immutable. But that means that all methods of the class have been designed to return new instances of class rather than modifying the instance.
So if you would like to make Java's String into a mutable object, you couldn't, without having access to its source code.
Same applies to classes written in C++, or any other language. You have an option to create a wrapper (or use a Proxy pattern), but that's it.
2. Using placement constructor and allocating into an initialized are off memory.
That's actually what they were created to do in the first place.
The most common use case for the placement constructor are memory pools - you preallocate a large memory buffer, and then you allocate your stuff into it.
So yes - it is legal, and nobody won't mind.
3. Overwriting class instance's contents using a placement allocator.
Don't do that.
There's a special construct that handles this type of operation, and it's called a copy constructor.
Let's say I create a class called MyClass which contains a reference variable m_my_resource. This reference variable is essentially just a named alias associated with some other memory location.
MyClass
class MyClass
{
public:
MyClass(const MyResource& my_resource) :
m_my_resource(my_resource){}
private:
const MyResource& m_my_resource;
}
Now lets say I try to do the following:
main
{
MyClass my_class(utils::getMyResource());
//continue doing stuff
}
What exactly happens in this scenario? I have defined MyClass to only have a single constructor which takes in a reference (lvalue reference) to MyResource.
However, within my main function, I construct an instance of MyClass with a temporary object (rvalue). Why is my code able to compile? Doesn't my_class now contain a reference variable that is associated with some temporary memory location? Essentially the variable in which the reference variable was associated with has now 'died', what happens to the reference variable?
Furthermore, is this a case where I would want my class to have a constructor that accepts rvalue references?
Why is my code able to compile?
Just because your code compiles, doesn't mean that it will work correctly. Otherwise, every program in the world will be automatically bug-free, by the virtue of it successfully passing the compilation phase, and there wouldn't be any need for anyone to learn how to use a debugger.
Obviously, things don't work this way.
Doesn't my_class now contain a reference variable that is associated
with some temporary memory location?
Yes, it does.
Essentially the variable in which the reference variable was
associated with has now 'died', what happens to the reference
variable?
Nothing happens to your reference variable. It still exists. But referencing the object -- by that it means attempting to invoke its methods or access its members -- results in undefined behavior.
If getMyResource() returns a MyResource object, somewhere in getMyresource() you are allocating memory for the object (on the heap may be), so you have to release the allocated memory. For instance call the MyResource destructor for m_my_resource in the MyClass destructor. If you don't you are going to have memory leaks on your program. In C++ there is no garbage collector to release automatically allocated memory, you have to do it by yourself, but there is non apparent problem compiling, just executing, if memory leaks are a matter.
Generally, a shared_ptr's must be assigned by copy semantics to ensure increasing of the use count and to avoid a situation where a reference to a pointer exists, but the memory it tracks has already been deallocated in a different thread.
However, I remember that I once did come across a scenario (in multi-threaded code) where assigning a shared_ptr by reference was acceptable, desirable and safe. I don't have access to the code anymore and I can't recall the scenario. Can someone think of when such a use would be safe?
To avoid trivial scenario, I'll stipulate that it was a multi-threaded program and the pointer was used in more than one thread.
Similarly to any object, you can use a reference to a shared_ptr any time you know the shared_ptr you're referencing will continue to exist for the lifetime of the reference. This is really no different than any other reference. The object pointed to by the shared_ptr is irrelevant; if a reference is used after the referenced object's lifetime has ended, you're dealing with undefined behavior. It follows that if the referenced shared_ptr still exists, then the object it's managing has at least one shared_ptr still referencing it, and therefor will not be deleted.
Passing a shared_ptr by reference allows you to allocate an object in a function without the locking and reference counting that happens when you return a shared_ptr from a function and assign it to a shared_ptr owned by the caller.
I am confused between :
returning an object (but then the object is copied from the local variable in the function, which consumes memory)
returning a pointer (but then you have to remember to delete it, in the calling code, which is weird)
returning a reference (but this is not possible because this would be a reference to a local variable of the function, which would be deleted as soon as the function ends)
I am looking for the proper way to return an object from a C++ function, in basic C++ (no library handling pointers and freeing memory automatically). I want to know how this is supposed to be done.
Thank you
Modern compilers typically implement the (Named) Return Value Optimization, by which the copy you reference (and would logically expect) is not done.
Ever since Visual Studio 2005 (VC++ 8.0) I don't think twice about returning objects.
What about std::auto_ptr from <memory>? Or if C++0x is concerned std::unique_ptr?
A few rules of thumb regarding returning objects from functions:
Return per copy, except when
you return a non-local object (like a class member, static variable etc.) of a type that you would pass to a function per const reference; you can return this per const reference
you return a non-local object and callers should be able to invoke modifying members of the returned object, thereby manipulating an object stored elsewhere; return this per non-const reference
you return a derived class in a polymorphic class hierarchy, users of the object should only know the base class, and neither #1 nor #2 apply; return this per smart pointer
Assuming "no library handling pointers and freeing memory automatically" means no return-by-pointer, no boost::shared_ptr and no std::unique_ptr (std::auto_ptr is evil anyway), you have two choices:
Return by value:
Foo bar(quux)
{
Foo foo;
foo.frobnicate(quux);
return foo;
}
Foo foo = bar("fred");
Pass by reference:
void bar(Foo& foo, quux)
{
foo.frobnicate(quux);
}
Foo foo;
bar(foo, "fred");
Depends on the "semantics" of the object. Values should be returned by copy while entities should (or must, since they are ideally not copyable) be returned by pointer or reference.
References should be used when possible. But if you must return a pointer, using a smart pointer class such as std::auto_ptr or boost::shared_ptr is a good idea, because then the calling code don't have to wonder about freeing it when it is done with it.
Return by value unless you need subtype polymorphism. In the latter case, I would return an auto_ptr<T> (C++03) or a unique_ptr<T> (C++0x).
Return one of the smart pointer choices (either having to depends on extra libraries or wait until C++1x)
Use pass by reference
Personally I prefer the second option because it is clear that the user needs to allocate and delete memory.
returning an object (but then the
object is copied from the local
variable in the function, which
consumes memory)
Optimal compilers may not take significant time to create copy.
You may also need to implement copy constructor and overload assignment operator, depending upon your object contents.
returning a pointer (but then you have
to remember to delete it, in the
calling code, which is weird)
Yes, you have to remember to delete it as you do not want to consider automatic cleanup for this question.
returning a reference (but this is not
possible because this would be a
reference to a local variable of the
function, which would be deleted as
soon as the function ends)
Returning reference is useful when you are returning this object(*this) from member functions. Otherwise, like you mentioned its not possible to use.
Overall: it depends upon your need as described above regarding which one to choose when.