What is the relation between C storage-class and C++ destructor - c++

I am very new to C/C++ programming.
Storage class in C signifies the visibility and life cycle of a variable.
In C++, Constructor and Destructor are used to initialize & release-resources the object occupied.
Yes, constructor helps reducing much of repetitive code but destructors are used to release and/or free resources (once an object goes out of scope).
Are these concepts coupled in some way in their implementation?

For C Storage classes see: https://stackoverflow.com/a/2661411/8740349
Let's not talk about implementions, as each compiler works differently, but if you ask about C++ spec, most keywords mean the same.
Except that:
register keyword was removed since C++17 (after being deprecated in C++11), without any alternative.
auto means to auto-detect type, like:
auto myVariable = myFunction("blablabla (how old are you, in Chinese)");
Is destructor related to storage-class?
No, C++ Class and/or Struct is destructed before it's storage is deallocated.
As mentioned in comments: Handling deallocation of storage isn't the subject of the destructor but the subject of the respective "deallocator" (provided by the compiler e.g. for global and local variables or the delete for memory allocated with new).

Related

Is double-construction undefined behaviour?

In our codebase, a pool of memory chunks is used and upon 'allocation' the object gets constructed using a "placement new". I'm missing the destructor call though, finding it odd to allow "double construction", and wonder if it is undefined behaviour to call the constructor another time on the same object.
In C++11 3.8.4 [basic.life] it reads
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.
Does this mean that missing out the destructor call is actually ok, as long as we talk about types which have destructors without side-effects?
Edit: Context is: embedded SW without heap, own container implementations doing placement-new on c-array-elements or byte-arrays.
You can double construct without UB. The new object is a completely different one, and following pointers/references to the old one is UB.
It is really easy to trip over UB when doing placement new, let alone when double constructing.
Not calling a destructor just means the object is not cleaned up. If the destructor is trivial, this is not going to have much risk. Constructing an object over another object end an objects lifetime, as does calling the destructor.
If the object is in automatic storage with the proper type, constructing a different object over it and exiting the scope (causing the no longer existing original object to be destroyed) is usually UB (you can avoid UB here with a trivial destructor of the original object; this is why byte-buffers can have placement new done in them).
This answer already answers the question well.
Although, I would add that using placement new in production code is usually a bad practice, error prone and/or a mistake. Many excellent alternatives exist (including some libraries). So you should look to simpler alternatives (could be as simpel as using a std::vector instead - calling reserve() if absolutely needed!) - also sometimes the use and introduction of placement new in codebase in the first place is oftentime misguided, legacy or sometimes a case of premature optimization.
Albeit it's hard to give concrete advice - one method could be just remove placement new or replace it with some kind of standard container - and then check if any problems arises (usually performance).
In my experience just removing the use of placement new in legacy code leads to no issues since the performance problem that it (maybe) used to fix has become largely obsolete by a wide range of 'system' optimizations (from the c++ level down to the hardward level).

How to make destructors be called on going out of scope?

With GC on, is it possible for destructors to be called right after going out of scope.
Is it possible for destructors to be called on going out of scope for all objects [of any type]?
Why aren't destructors called when going out of scope anyway?
In this post "scope on a local variable" is said to be "unsafe". Why is it considered unsafe?
And the rationale for deprecating the feature is
scope as a type constraint was a quirk in the language without a compelling use case.
No compelling use case? Like placing objects on the stack(this feature does that, right?) isn't faster than on the heap.
Generally, the D GC does not call destructors when returning from a function. The GC is triggered when an allocation occurs, and when GC.collect is called.
D has scoped, which wraps a non-RAII type in a struct, which has RAII behavior. This way, the struct's destructor can take care of cleaning up the memory. Note that, while this generally works, there are some corner cases and things to be aware of that a GC will handle automatically. This allows destructors to be called on any object when leaving a scope.
Destructors are not called on all objects when they leave scope because there may be other references to the objects. Consider this code:
int* global;
void func() {
int* p = new int;
global = p;
}
If the int pointed to by p was destructed when func() returned, then global would point to destructed memory.
The article you link is almost ten years old, and D has changed a bit in the meantime. scope now has better semantics, which meaningfully limit what you can do with variables marked such:
return ref
return scope
ref return scope
scope local variables may still be assigned to globals, which seems like an oversight. I'll file a bug if I can't find an existing one.
With GC on, is it possible for destructors to be called right after going out of scope.
Is it possible for destructors to be called on going out of scope for all objects [of any type]?
Destructors are called for any object on the stack, this applies to structs and classes allocated as scope Foo f = new Foo().
In this post "scope on a local variable" is said to be "unsafe". Why is it considered unsafe?
Because one could escape a reference to the stack allocated instance which might outlive the current function call. (But DIP 25 / 1000 detect's most of these issues)
And the rationale for deprecating the feature is [...]
No compelling use case? Like placing objects on the stack(this feature does that, right?) isn't faster than on the heap.
Only scope attached to the type declaration is deprecated.
But as a general recommendation, use structs if your object requires deterministic destruction (e.g. File handles).

Do Primitive Types in C++ have destructors?

This question comes from me trying to understand the motivation for smart pointers where you make a wrapper class around the pointer so that you could add a custom destructor. Do pointers (and ints, bools, doubles, etc.) not have a destructor?
Technically speaking, non-class types (C++ term for what often called 'primitive type' in layman words) do not have destructors.
C++ Standard only speaks of real destructors in context of classes, see [class.dtor] in C++ standard. Aside from that, C++ also allows to call a destructor on a non-class object using the same notation, i.e. following code is valid:
void foo(int z) {
using T = int;
z.~T();
}
This is called 'pseudo-destructor' and exists exclusively to allow writing generic templated code to deal in the same manner with class and non-class types. This call does nothing at all. This syntax is defined in [expr.prim.id] in C++ standard.
Primitive types (and compounds thereof) have trivial destructors. These don't do anything, and have special wording that allows them to be skipped altogether in some cases.
This, however, is orthogonal to why C++ has smart pointers. A raw pointer is non-owning: it points at another object, but does not affect its lifetime. Smart pointers, on the other hand, own (or share ownership of) their pointee, tying its lifetime to their own. This is what is implemented inside, among other special functions, their destructor.
In addition to the answers given here so far, since C++20 the pseudo-destructor call on a non-class object will always end its lifetime. Consequently accessing the object's value after the call will have undefined behavior. This does not mean however that the compiler has to emit any code for such a call. It still effectively does nothing.
No, pointers don't have destructors. An object referenced through a plain old pointer has to be deleted to avoid memory leaks, and the object's destructor is called then, but the compiler won't call delete automatically, even when a pointer goes out of scope - what if another part of your program also had a pointer to the same object?
Smart pointers aren't about calling a custom destructor, they're about ensuring that things get cleaned up automatically when they go out of scope. This 'cleaning up' might be deleting owned objects, freeing any malloced memory, closing files, releasing locks, etc.
Destructors are used to free the resources that an object may have used.
For pointers, you don't need delete if you are not allocating new memory from the heap.
C and C++ have two ways to store a variable: stack and heap.
Stack is for static memory, and the compiler takes care of that. Heap is for dynamic memory, and you have to take care of this if you are using it.
When you do primitive type declarations, stack memory is allocated for the variables.
When you use new to declare an object, this object is stored on the heap, which you need to delete it when you are finishing using it, or it would be a memory leak.
Basically, you only need delete if you new something.

What is the C++ equivalent of an 'allocated object having no declared type' in C?

I'm writing a memory manager for my VM in C++. Well, more exactly the VM instructions will be compiled into C++ with an embedded memory manager. I'm much more comfortable in handling C, but now I do need native support of exception handling, pretty much the only reason I'm using C++.
Both C and C++ have the strict aliasing rule that two objects of incompatible types shall not overlap, with a small exception in C for unions. But to define the behaviour of memory allocation functions such as malloc, calloc, alloca etc., the C standard has the following paragraph.
6.5-6 The effective type of an object for an access to its stored value is the declared type of the object, if any. Allocated objects
have no declared type. If a value is stored into an object having no
declared type through an lvalue having a type that is not a character
type, then the type of the lvalue becomes the effective type of the
object for that access and for subsequent accesses that do not modify
the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access.
This effectively makes using raw allocated memory for any type a well defined behaviour in C. I tried to find a similar paragraph in the C++ standard document but could not find one. I think C++ has a different approach in this regard. What is the C++ equivalent of an 'allocated object having no declared type' in C, and how does the C++ standard define it?
For C++, this is is described in Object lifetime [object.life]; in particular:
The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-trivial initialization, its initialization is complete.
Lifetime continues until the storage is reused or the object is destructed:
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.
This has the rather odd implication that unused allocated storage (returned from operator new) contains an object of every trivial type that fits in that block of storage, at least until the block of storage is used. But then, the standard cares more about getting the treatment of non-trivial types right than this minor wart.
I'm not sure such an analog exists, or is needed in C++. To allocate memory you can do one of three things
Foo f;
This will allocate sizeof(Foo) amount of memory on the stack. The size is known at compile-time, which is how the compiler knows how much space to allocate. The same is true of arrays, etc.
The other option is
Foo* f = new Foo; // or the smart pointer alternatives
This will allocate from the heap, but again sizeof(Foo) must be known, but this allows allocation at run-time.
The third option, as #BasileStarynkevitch mentions, is placement new
You can see that all of these allocation mechanisms in C++ require knowledge of the type that you are allocating space for.
While it is possible to use malloc, etc in C++, it is frowned upon as it goes against the grain for typical C++ semantics. You can see a discussion of a similar question. The other mechanisms of allocating "raw" memory are hacky. For example
void* p = operator new(size);
This will allocate size number of bytes.
I think "an allocated object with no declared type" is effectively allocated storage that is not yet initialised and no object has yet been created in that memory space. From the global allocation function ::operator new(std::size_t) and family (§3.7.4/2);
§3.7.4.1/2
The allocation function attempts to allocate the requested amount of storage. If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. There are no constraints on the contents of the allocated storage on return from the allocation function.
The creation of an object, whether it is automatic or dynamic, is governed by two stages, the allocation itself and then the construction of the object in that space.
§3.8/1
The lifetime of an object is a runtime property of the object. An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its members is initialized by a constructor other than a trivial default constructor. [Note: initialization by a trivial copy/move constructor is non- vacuous initialization. — end note] The lifetime of an object of type T begins when:
storage with the proper alignment and size for type T is obtained, and
if the object has non-trivial initialization, its initialization is complete.
Correspondingly;
The lifetime of an object of type T ends when:
if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
the storage which the object occupies is reused or released.
C++ WD n4527.
I think, the approach of C++ in this regard can be summarized as: "malloc() is evil. Use new instead. There is no such thing as an object without a declared type." Of course, C++ implementations need to define the global operator new(), which is basically the C++ version of malloc() (and which can be provided by the user). The very existence of this operator proves that there is something like objects without a declared type in C++, but the standard won't admit it.
If I were you, I'd take the pragmatic approach. Both the global operator new() and malloc() are available in C++, so any implementation must be able to use their return values sensibly. Especially malloc() will behave identical in C and C++. So, just handle these untyped objects the same way as you would handle them in C, and you should be fine.

Does C++ require a destructor call for each placement new?

I understand that placement new calls are usually matched with explicit calls to the destructor. My question is: if I have no need for a destructor (no code to put there, and no member variables that have destructors) can I safely skip the explicit destructor call?
Here is my use case: I want to write C++ bindings for a C API. In the C API many objects are accessible only by pointer. Instead of creating a wrapper object that contains a single pointer (which is wasteful and semantically confusing). I want
to use placement new to construct an object at the address of the C object. The C++ object will do nothing in its constructor or destructor, and its methods will do nothing but delegate to the C methods. The C++ object will contain no virtual methods.
I have two parts to this question.
Is there any reason why this idea will not work in practice on any production compiler?
Does this technically violate the C++ language spec?
If I understand your question correctly you have a C object in memory and you want to initialize a C++ object with the same layout "over the top" of the existing object.
CppObject* cppobject = new (cobject) CppObject;
While there is no problem with not calling a destructor for the old object - whether this causes resource leaks or other issues is entirely down to the type of the old object and is a user code issue, not a language conformance issue - the fact that you reuse the memory for a new object means that the old object is no longer accessible.
Although the placement form of operator new must just return the address that it was given, there is nothing to stop the new expression itself wiping the memory for the new object before any constructor (if any) is called. Members of the new object that are not initialized according to C++ language rules have unspecified contents which definitely does not mean the same as having the contents of any old object that once lived in the memory being reused.
If I understand you correctly, what you are trying to do is not guaranteed to work.
I think the answer is that if your class is POD (which it is, if it's true that it does nothing in the con/destructor, has no virtual member functions, and has no non-static data members with any of those things), then you don't need to call a constructor or a destructor, its lifetime is just the lifetime of the underlying memory. You can use it the same way that a struct is used in C, and you can call its member functions regardless of whether it has been constructed.
The purpose of placement new is to allow you to create object pools or align multiple objects together in contiguous memory space as with std::vector.
If the objects are C-structs then you do not need placement new to do this, you can simply use the C method of allocating the memory based on sizeof(struct Foo) where Foo is the struct name, and if you allocate multiple objects you may need to multiple the size up to a boundary for alignment.
However there is no need to placement-new the objects there, you can simply memcpy them in.
To answer your main question, yes you still need to call the destructor because other stuff has to happen.
Is there any reason why this idea will not work in practice on any production compiler?
You had damn well be sure your C++ object fits within the size of the C object.
Does this technically violate the C++ language spec?
No, but not everything that is to spec will work like you want.
I understand that placement new calls are usually matched with explicit calls to the destructor. If I have no need for a destructor (no code to put there, and no member variables that have destructors) can I safely skip the explicit destructor call?
Yes. If I don't need to fly to New York before posting this answer, can I safely skip the trip? :) However, if the destructor is truly unneeded because it does nothing, then what harm is there in calling it?
If the compiler can figure out a destructor should be a no-op, I'd expect it to eliminate that call. If you don't write an explicit dtor, remember that your class still has a dtor, and the interesting case – here – is whether it is what the language calls trivial.
Solution: destroy previously constructed objects before constructing over them, even when you believe the dtor is a no-op.
I want to write C++ bindings for a C API. In the C API many objects are accessible only by pointer. Instead of creating a wrapper object that contains a single pointer (which is wasteful and semantically confusing). I want to use placement new to construct an object at the address of the C object.
This is the purpose of layout-compatible classes and reinterpret_cast. Include a static assert (e.g. Boost's macro, 0x static_assert, etc.) checking size and/or alignment, as you wish, for a short sanity check, but ultimately you have to know a bit of how your implementation lays out the classes. Most provide pragmas (or other implementation-specific mechanisms) to control this, if needed.
The easiest way is to contain the C struct within the C++ type:
// in C header
typedef struct {
int n;
//...
} C_A;
C_A* C_get_a();
// your C++
struct A {
void blah(int n) {
_data.num += n;
}
// convenience functions
static A* from(C_A *p) {
return reinterpret_cast<A*>(p);
}
static A const* from(C_A const *p) {
return reinterpret_cast<A const*>(p);
}
private:
C_A _data; // the only data member
};
void example() {
A *p = A::from(C_get_a());
p->blah(42);
}
I like to keep such conversions encapsulated, rather than strewing reinterpret_casts throughout, and more uniform (i.e. compare call-site for const and non-const), hence the convenience functions. It's also a bit harder to modify the class without noticing this type of use must still be supported.
Depending on the exact class, you might make the data member public.
The first question is: why don't you just use a cast? Then there's no issue of the placement new doing anything, and clearly no issue of failing to use a destructor. The result will work if the C and C++ types are layout compatible.
The second question is: what is the point? If you have no virtual functions, you're not using the constructor or destructor, the C++ class doesn't seem to offer any advantages over just using the C type: any methods you write should have been global functions anyhow.
The only advantage I can imagine is if you want to hide the C representation, you can overlay the C object with a class with all private members and use methods for access. Is that your purpose? [That's a reasonable thing to do I think]