[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)