[HN Gopher] C++ Constructors, Memory, and Lifetimes
       ___________________________________________________________________
        
       C++ Constructors, Memory, and Lifetimes
        
       Author : blackhole
       Score  : 44 points
       Date   : 2021-05-02 11:35 UTC (11 hours ago)
        
 (HTM) web link (erikmcclure.com)
 (TXT) w3m dump (erikmcclure.com)
        
       | zabzonk wrote:
       | This is full of misunderstandings. When variables are allocated
       | on the stack, the space for them with all compilers I have ever
       | used (or written) is allocated on the stack once by adjusting the
       | stack pointer - i.e. for all of the variables of the function.
       | There is no pushing or popping - function variables have
       | addresses on the stack. When the function ends, the stack pointer
       | is reset to it's original value, possibly with destructors for
       | the things on the stack being called.
       | 
       | Also, in modern compilers, parameter variables are mostly passed
       | to functions via registers, with the stack not being involved at
       | all.
        
         | jcelerier wrote:
         | > When variables are allocated on the stack, the space for them
         | with all compilers I have ever used (or written) is allocated
         | on the stack once by adjusting the stack pointer - i.e. for all
         | of the variables of the function.
         | 
         | it's not a given                   int algo(int*, int n);
         | int computation(int i, int n) {             int alloc[n];
         | return algo(alloc, n);             }
         | 
         | on some compilers (Keil for instance afair), alloc will be
         | heap-allocated (and freed when leaving the function) even if
         | it's "automatic storage" (what most people call the stack).
        
           | divingdragon wrote:
           | Isn't variable sized array non-standard in C++? Variable
           | sized array is kind of a special thing anyway so I wouldn't
           | be surprised if some compilers do something special with it.
           | 
           | Yes, the term "automatic storage" is more accurate here.
        
         | andi999 wrote:
         | Since this would be too easy for c++, there is also (sometime
         | mandatory) copy elision.
        
         | mhh__ wrote:
         | > Also, in modern compilers, parameter variables are mostly
         | passed to functions via registers, with the stack not being
         | involved at all.
         | 
         | Given that this is a C++ thread I'll be pedantic and say that
         | this is really the ABI rather than the compiler as per se.
        
           | zabzonk wrote:
           | Given that the C++ Standard has nothing to say about an ABI
           | (neither does the C Standard, for that matter), then what a
           | compiler does on a particular platform, and what a platform
           | ABI specifies (if it does) are possibly kind of linked.
        
             | mhh__ wrote:
             | The word compiler is only mentioned 12 times in the whole
             | standard give or take.
             | 
             | My point was that it's not a question of whether the
             | compiler is modern or not, i.e. I work with an old compiler
             | backend on a modern system and it still gets the ABI
             | correct even if it ends up spilling back to memory too
             | often.
        
           | gumby wrote:
           | ABI is a factor in every language;* so I'm not sure I
           | understand the point you're trying to make.
           | 
           | * even interpreted ones, though the calling conventions and
           | layout are quite different from what is typically written up
           | in the documentation of most compiled languages.
        
             | mhh__ wrote:
             | The term "modern compiler" was used, which implies that an
             | old compiler might (say) not do so despite complying with
             | some ABI, i.e. the registers aren't an optimization.
        
               | gumby wrote:
               | I have never used a compiler that didn't allocate frames
               | in a single move of the sp, and I've been using compilers
               | since the 1970. Surely "modern" has to mean some change
               | more recent than that.
        
               | mhh__ wrote:
               | That isn't the part of your comment I was block-quoting.
        
         | brundolf wrote:
         | I took it as a "conceptual abstraction". It presented a mental
         | model focused on the relationship between variables' lifetimes,
         | intentionally glossing over the implementation details of any
         | particular compiler.
        
           | Someone wrote:
           | If it is glossing over implementation details, why does it
           | mention the stack? That _is_ an implementation detail.
           | 
           | AFAIK (I never looked at an official standard, only at
           | drafts) the standard doesn't mention the word "stack" at all
           | 
           | A C++ compiler is free to allocate environment frames for
           | every block on the heap.
        
             | brundolf wrote:
             | There are different tiers of abstraction. This post picked
             | a level of granularity that the author felt would be
             | helpful for a certain subset of readers.
             | 
             | Whether the "stack" is formalized or not, it's the mental
             | model (and the vocabulary) that's most often used when
             | working with this stuff. I don't think there's anything
             | wrong with the fact that they focused on the forest and not
             | the trees.
        
       | dxuh wrote:
       | As you are officially discouraged to use new and delete directly
       | (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines...)
       | and I fully agree with this guideline, I was disappointed to see
       | new being mentioned so early and not immediately being followed
       | up with std::unique_ptr and std::shared_ptr. Especially because
       | you introduced the constructor/destructor subsection with how
       | destructors can be used to free memory. A general mention of
       | std::shared_ptr would have been expected as well, I think.
       | 
       | It is unfortunate that many learning resources don't show modern
       | C++ so the language is perceived worse than it is and also more
       | bugs are written, than is necessary.
        
         | klyrs wrote:
         | > The pointer returned by new should belong to a resource
         | handle (that can call delete). If the pointer returned by new
         | is assigned to a plain/naked pointer, the object can be leaked
         | 
         | Unless I'm reading this wrong (and I'm seeking to be corrected
         | here), it's still fine to use new/delete in certain contexts
         | struct bla {         int n;         int[] mem;         bla(int
         | n) : n(n), mem(new int[n]{}) {}         ~bla() { delete[] mem;
         | }         /* forbid copies (for uniqueness) and moves (to keep
         | this short) */         bla(bla &) = delete;         bla(bla &&)
         | = delete;         bla &operator=(bla &&) = delete;       };
         | 
         | Edit to remove unnecessary null-checking
        
           | f00zz wrote:
           | You could just use std::vector<int> there
        
             | klyrs wrote:
             | Of course you could. That wasn't my question -- my question
             | was about the wording of the recommendation; "...should
             | belong to a resource handle (that can call delete)"
        
           | makecheck wrote:
           | Note above, the "delete" operator is safe to call on nullptr
           | values, and it is also not necessary to set pointers to
           | nullptr in destructors (since the object is going away
           | anyway). The example above therefore could just say "delete
           | [] mem;" in the destructor with nothing else.
           | 
           | Yet this is also a good example of why you wouldn't need
           | "delete" or a destructor at all, if "mem" changes to
           | std::unique_ptr<int[]> type.
           | 
           | And even then, you should _probably_ seriously consider
           | std::vector or std::array before trying a unique_ptr array.
        
           | plorkyeran wrote:
           | That struct should be just struct bla { int n;
           | std::unique_ptr<int[]> mem; bla(int n) : n(n),
           | mem(std::make_unique<int[]>(n)) { } }. There is no reason to
           | use new/delete there.
        
           | zabzonk wrote:
           | Is:                    int[] mem;
           | 
           | even legal C++?
        
             | klyrs wrote:
             | no, that was me being sloppy typing code on my phone
        
             | lights0123 wrote:
             | Not standard C++.
             | 
             | If the [] was after the variable name, because it is the
             | last member, most C++ compilers (GCC, Clang, and MSVC) have
             | a non-standard extension:
             | https://en.wikipedia.org/wiki/Flexible_array_member
        
               | zabzonk wrote:
               | Not even non-standard C++ - my g++ compiler errors with:
               | 
               | a.cpp:2:13: error: expected unqualified-id before '['
               | token
        
               | lights0123 wrote:
               | > If the [] was after the variable name
        
           | detaro wrote:
           | _should_. It 's a guideline to avoid mistakes, it doesn't
           | mean you can't do memory manually in C++, it's just
           | recommended you avoid it unless you have a good reason.
        
         | zabzonk wrote:
         | In my opinion, the site you linked to has a major problem -
         | many of the example functions have a "void" return type. This
         | indicates that they must have side-effects (or why else call
         | them?) and we would normally not want to write functions that
         | have side effects.
         | 
         | A rule of thumb: if your function doesn't have a return type,
         | consider that your design maybe wrong in some way.
        
           | detaro wrote:
           | What the functions return is utterly irrelevant for the
           | examples, so adding return types and statements just makes
           | the example more complex for no good reason (and distracts
           | from examples where the example _is_ about the returns)
        
             | zabzonk wrote:
             | The site is about writing "good" C++. Having loads of
             | functions that implement "bad" C++ cannot help.
        
               | josefx wrote:
               | Good C++ code shouldn't throw around heap allocations,
               | shared or otherwise. Smart pointers are like duct tape,
               | they help keeps things together but most of the time
               | there are better solutions. Your complaint about void
               | will be noted once you fix your programs ownership
               | semantics.
               | 
               | Note: Any negative reactions to smart pointers on my side
               | may be fueled by a disturbing amount of cargo culting
               | that I had to witness since their inclusion in the
               | standard.
        
               | zabzonk wrote:
               | > Your complaint about void will be noted
               | 
               | By whom?
               | 
               | > once you fix your programs ownership semantics.
               | 
               | How do you heck know how my programs need fixing?
               | 
               | > Any negative reactions to smart pointers on my side may
               | be fueled by a disturbing amount of cargo culting that I
               | had to witness since their inclusion in the standard.
               | 
               | Any positive reactions to smart pointers on my side may
               | be fuelled by over 30 years experience in using them.
        
               | josefx wrote:
               | > How do you heck know how my programs need fixing?
               | 
               | You where the one complaining that sample one liners with
               | void return types where bad code. In my experience
               | shared_ptr is overkill in nearly all trivial code
               | examples, so any one liner containing one is bad code.
               | 
               | > Any positive reactions to smart pointers on my side may
               | be fuelled by over 30 years experience in using them.
               | 
               | Hope you never had to deal with code reviews that
               | suggested putting scoped std::vector uses in shared_ptr s
               | to avoid stack overflows. Or really any code that put
               | objects into smart pointers because it could not because
               | it should.
        
           | mhh__ wrote:
           | This is true, but two points: Firstly, don't teach too much
           | at the same time, and secondly sometimes you have to just
           | launch the missiles.
        
       | layoutIfNeeded wrote:
       | _> This means it allocates memory from the heap, named after the
       | heap data structure_
       | 
       | Oh my god, please don't write articles about things that you
       | barely understand!
        
         | minipci1321 wrote:
         | Another one:
         | 
         | > the exact order that C++ evaluates expressions is extremely
         | complicated and not always defined, ... "
         | 
         | "(Un)defined" and "unspecified" (which order of evaluation of
         | fun-args really is) are two distinct terms in the C++ spec
         | meaning different things, they cannot be used interchangeably.
         | 
         | But I guess it's just yet another thing of "Pedantic assembly-
         | code analysts"...
        
       ___________________________________________________________________
       (page generated 2021-05-02 23:02 UTC)