Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[niubbo] aggregate types built in a function and returned by value (on stack). Shallow or Deep copy ?

46 views
Skip to first unread message

Soviet_Mario

unread,
Jul 15, 2019, 6:01:03 PM7/15/19
to

I've been a long time far from C++ and I realize I have
forgotten most, and moreover don't understand "modern" C++
.... anyway

When in a function (member or not is irrelevant) I build an
aggregate object (class / struct, possibly nested with
other), and this aggregate has a DEFAULT public constructor
which does not resort to new or new [], but then the said
former function invokes some class methods that DO some
dynamic allocation, and then the function returns passing
outside a copy by-value of the built object to the caller,
placed "partially" on the stack, the final copy will be
shallow or deep ?
With shallow I mean : every automatic member not requiring
new or new [], with deep copy I mean exact copy (or even the
temporary itself ...).

another maybe relevant detail
is it relevant whether the COPY constructor is defined or
the default one ? How behaves a default generated copy
constructor ? Does it perform also new/ new [] operations ?

Is it really the COPY constructor that is called on such a
situation (returning a complex type by-value instead of
by-pointer) ?

or a COPY constructor is given, which does not do deep copy,
but just "automatic" data is replicated.


In case I defined a COPY constructor making dynamic
allocation, is it : 1) required, 2) optional or 3)
deprecated and possibly dangerous define also an explicit
DESTRUCTOR making cleanups ?

TY

--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
Soviet_Mario - (aka Gatto_Vizzato)

Öö Tiib

unread,
Jul 15, 2019, 6:40:27 PM7/15/19
to
On Tuesday, 16 July 2019 01:01:03 UTC+3, Soviet_Mario wrote:
> I've been a long time far from C++ and I realize I have
> forgotten most, and moreover don't understand "modern" C++
> .... anyway
>
> When in a function (member or not is irrelevant) I build an
> aggregate object (class / struct, possibly nested with
> other), and this aggregate has a DEFAULT public constructor
> which does not resort to new or new [], but then the said
> former function invokes some class methods that DO some
> dynamic allocation, and then the function returns passing
> outside a copy by-value of the built object to the caller,
> placed "partially" on the stack, the final copy will be
> shallow or deep ?

Long sentence that sounded too controversial to
understand. 1) An aggregate can't have ANY user-declared
constructors or destructors. 2) Returning objects of class
type results with RVO (no copies made). 3) Your concept
of shallow copies is also hard to understand:

> With shallow I mean : every automatic member not requiring
> new or new [], with deep copy I mean exact copy (or even the
> temporary itself ...).

What are automatic members? Snipping the rest since it
discusses more of making constructors to aggregate.

Soviet_Mario

unread,
Jul 15, 2019, 7:18:51 PM7/15/19
to
On 16/07/19 00:40, Öö Tiib wrote:
> On Tuesday, 16 July 2019 01:01:03 UTC+3, Soviet_Mario wrote:
>> I've been a long time far from C++ and I realize I have
>> forgotten most, and moreover don't understand "modern" C++
>> .... anyway
>>
>> When in a function (member or not is irrelevant) I build an
>> aggregate object (class / struct, possibly nested with
>> other), and this aggregate has a DEFAULT public constructor
>> which does not resort to new or new [], but then the said
>> former function invokes some class methods that DO some
>> dynamic allocation, and then the function returns passing
>> outside a copy by-value of the built object to the caller,
>> placed "partially" on the stack, the final copy will be
>> shallow or deep ?
>
> Long sentence that sounded too controversial to

sorry for my tangled english :\

> understand. 1) An aggregate can't have ANY user-declared

I realize now I have mis-used this word (that had a precise
meaning in C++!). So let replace it with "class/struct type
object"

> constructors or destructors. 2) Returning objects of class
> type results with RVO (no copies made).

could you explain this acronym ?

> 3) Your concept
> of shallow copies is also hard to understand:

only automatic (on stack) members, no "remote" (dynamic)
data allocated with new / new []

>
>> With shallow I mean : every automatic member not requiring
>> new or new [], with deep copy I mean exact copy (or even the
>> temporary itself ...).
>
> What are automatic members?

the ones completely contained in the object itself.
I took the term automatic from "automatic" variables (with
local scope, built on the stack) contrasted with "dynamic"
variables (allocated manually from the heap and whose
lifetime is to be manually managed too).


> Snipping the rest since it
> discusses more of making constructors to aggregate.

you are right, I mis-used the term !
I just meant some object containing both some automatic
members and also some dynamically allocated ones.

James Kuyper

unread,
Jul 15, 2019, 9:12:36 PM7/15/19
to
On 7/15/19 7:18 PM, Soviet_Mario wrote:
> On 16/07/19 00:40, Öö Tiib wrote:
>> On Tuesday, 16 July 2019 01:01:03 UTC+3, Soviet_Mario wrote:
...
>> constructors or destructors. 2) Returning objects of class type
>> results with RVO (no copies made).
>
> could you explain this acronym ?

"... the return statement initializes the glvalue result or prvalue
result object of the (explicit or implicit) function call by
copy-initialization (11.6) from the operand. [ Note: A return statement
can involve an invocation of a constructor to perform a copy or move of
the operand if it is not a prvalue or if its type differs from the return
type of the function. A copy operation associated with a return
statement may be elided or converted to a move operation if an automatic
storage duration variable is returned." (9.6.3p2)

RVO is abbreviation for "Return Value Optimization", and refers to cases
in which a call to a copy constructor could in principle occur, but may
be elided.

That clause uses a couple of terms that you might not be familiar with:

"A glvalue is an expression whose evaluation determines the identity of
an object, bit-field, or function.
A prvalue is an expression whose evaluation initializes an object or a
bit-field, or computes the value of the operand of an operator, as
specified by the context in which it appears." (6.10p1).

>> 3) Your concept of shallow copies is also hard to understand:
>
> only automatic (on stack) members, no "remote" (dynamic) data
> allocated with new / new []>
>>
>>> With shallow I mean : every automatic member not requiring new or
>>> new [], with deep copy I mean exact copy (or even the temporary
>>> itself ...).
>>
>> What are automatic members?
>
> the ones completely contained in the object itself. I took the term
> automatic from "automatic" variables (with local scope, built on the
> stack) contrasted with "dynamic" variables (allocated manually from
> the heap and whose lifetime is to be manually managed too).

The standard term that matches what you mean is "non-static data member"
(12.2p3). Note that the storage duration of a non-static data member is
always the same as the storage duration of the containing object
(6.7.5p1). Non-static data members can therefore have static, automatic,
dynamic, or thread storage duration.

>> Snipping the rest since it discusses more of making constructors to
>> aggregate.
>
> you are right, I mis-used the term ! I just meant some object
> containing both some automatic members and also some dynamically
> allocated ones. TY

A member is not dynamically allocated unless it is a non-static data
member of a dynamically allocated object, in which case all non-static
data members of the same object are dynamically allocated.

I suspect that what you mean is members that are pointers to dynamically
allocated memory. Pointers are not distinguished by the storage duration
of the objects that they point at, any pointer can point at any suitable
object, regardless of that object's storage duration. Therefore, it's
not appropriate for the compiler to automatically free the memory a
pointer points at. It is the responsibility of the programmer, to make
sure that the memory it points at is in fact deallocated, when
appropriate, and only if it was dynamically allocated. That's one of the
things that user-defined destructors are used for - to make sure the
memory is deallocated.

Smart pointers are specialized classes that have destructors that, in
some cases, automatically deallocate the memory that they contain a
pointer to.

Öö Tiib

unread,
Jul 15, 2019, 11:22:10 PM7/15/19
to
On Tuesday, 16 July 2019 02:18:51 UTC+3, Soviet_Mario wrote:
> On 16/07/19 00:40, Öö Tiib wrote:
>
> > constructors or destructors. 2) Returning objects of class
> > type results with RVO (no copies made).
>
> could you explain this acronym ?

I see James Kuyper made decent effort to explain it.
In practice the implementations pass the address for
return value of class type to function like other arguments.
The function is allowed to construct the object that it is going
to return at that address and so elide copying when returning.
It often surprises people who wrote side effects to copy or
move constructors or destructors.

> > 3) Your concept
> > of shallow copies is also hard to understand:
>
> only automatic (on stack) members, no "remote" (dynamic)
> data allocated with new / new []
>
> >
> >> With shallow I mean : every automatic member not requiring
> >> new or new [], with deep copy I mean exact copy (or even the
> >> temporary itself ...).
> >
> > What are automatic members?
>
> the ones completely contained in the object itself.
> I took the term automatic from "automatic" variables (with
> local scope, built on the stack) contrasted with "dynamic"
> variables (allocated manually from the heap and whose
> lifetime is to be manually managed too).

Hmm.

Making efficient manual management of resources with C++
has become quite complicated topic.
It can take more than week to read and to understand the
nuances of "RAII", "rule of three", "copy-swap idiom",
"rule of five", "rule of zero" and "rule of four (and half)".

People who write about one of those tend to oversimplify the issue.
For example they typically forget to tell that with dynamically
polymorphic class hierarchy (with virtual functions) all exposed
copying, moving or swapping may cause sliced objects or worse.
So protect or =delete all that and use factories and cloning
with such hierarchies instead.

Paavo Helde

unread,
Jul 16, 2019, 8:04:50 AM7/16/19
to
On 16.07.2019 1:00, Soviet_Mario wrote:
>
> I've been a long time far from C++ and I realize I have forgotten most,
> and moreover don't understand "modern" C++ .... anyway
>
> When in a function (member or not is irrelevant) I build an aggregate
> object (class / struct, possibly nested with other), and this aggregate
> has a DEFAULT public constructor which does not resort to new or new [],
> but then the said former function invokes some class methods that DO
> some dynamic allocation, and then the function returns passing outside a
> copy by-value of the built object to the caller, placed "partially" on

Your question is not very clear, consider to add some code examples to
make it clearer.

In general, defining classes which do their own own low-level dynamic
memory allocation is tricky. One typically needs to define custom
constructors, assignments and destructors.

Fortunately enough, there is near zero need to do such low-level dynamic
allocation nowadays in C++, as it's properly done and encapsulated in
standard library classes like std::vector. If some objects really need
to be dynamically allocated, like some OOP hierarchy objects, one should
use std::make_unique() (or maybe std::make_shared() in special
circumstances) and appropriate smartpointers.

That said, the answer to your question is probably that by default the
copies in C++ are "shallow" (a copy of a pointer or a standard
smartpointer is just another pointer with the same value), to make it
deep one needs to define custom copy and assignment operations. The
latter is done e.g. by std::vector, so copying std::vector objects
always copies the whole data array.

hth
Paavo

Soviet_Mario

unread,
Jul 16, 2019, 8:28:36 AM7/16/19
to
could you give me a reference of the manual you are
referring to with the notation "12.2p3" etc ?
I mean a reference to some online manual

>
>>> Snipping the rest since it discusses more of making constructors to
>>> aggregate.
>>
>> you are right, I mis-used the term ! I just meant some object
>> containing both some automatic members and also some dynamically
>> allocated ones. TY
>
> A member is not dynamically allocated unless it is a non-static data
> member of a dynamically allocated object, in which case all non-static
> data members of the same object are dynamically allocated.

I am referring to objects of a class derived from MyString,
that have some automatic and / or static data members,
existing even for "empty" MyStrings, and some dynamic
content (created and destroyed using new char [] / delege []

an empty MyString can be built without calling any new char
[] / delege [], but further elaboration, which assigns
content, requires it, or even reallocation.

So I was asking about the behaviour of returning data for
these objects whose memory "footprint" is hybrid and also
variable depending on context.

I need to understand of glvalue and grvalue, as I can only
recall the concepts of R-value and L-value, to one side, and
value-types vs reference-types on the other, but I surely
have to complete the definitions I understand

>
> I suspect that what you mean is members that are pointers to dynamically
> allocated memory.

exactly : some "rich" strings, that have a constant
framework of members and an "optional" content, on the HEAP,
pointed to by an automatic pointer member

> Pointers are not distinguished by the storage duration

yes I know pointers are not, but I have doubts for the
actual content pointed to.

Avoiding DEEP copying (just copying the pointer, and thus
having more than one pointer targeting the same content,
would be destructive, as each MyString must have is private
and locale space reserved, and modifying it must not affect
others)

> of the objects that they point at, any pointer can point at any suitable
> object, regardless of that object's storage duration.

ok, but my doubt was about : is it dynamic memory
automatically duplicated (skeptic) ? Is NEVER duplicated ?
(harmful). Is the whole regulated by invoking the
COPY-constructor, so that it can decide ?

> Therefore, it's
> not appropriate for the compiler to automatically free the memory a
> pointer points at.

yes, but my doubt was more limited, I try to repeat it.

In the CALLEE, building a return value (passed not by
address or reference, just by-value => an hint to copy
data), some dynamic allocation is done explicitly.
The copy received by the caller, then would ...
1) receive NO data at all (I'm not saying the compiler has
ever freed it, simply it remains lost, inaccessible,
resultin in a leak)
2) receive a link to the same content, copied in another pointer
3) receive a copy of the dynamic data placed elsewhere
(which seems not reasonable if it was up to the compiler to
reallocate some room, transfer the data, deallocate the
CALLEE copy, but maybe this would be done if a COPY
constructor is delegated, and this always makes a copy ...
well this scenario seems heavy and inefficient, it would be
good to just copy automatic members so that just one
reference to data would exist after return, the one of the
caller)

> It is the responsibility of the programmer, to make
> sure that the memory it points at is in fact deallocated, when
> appropriate,

and this is managed now overloading operator = and also in
the copy constructor.
It would be safe but inefficient to have this same code when
returning objects by value that were just built (in the case
of initialization it would be inevitable instead, as there
is no such duplicated operation)

> and only if it was dynamically allocated. That's one of the
> things that user-defined destructors are used for - to make sure the
> memory is deallocated.
>
> Smart pointers are specialized classes that have destructors that, in
> some cases, automatically deallocate the memory that they contain a
> pointer to.

I don't master them at all .... by now I prefer to just
resort to things I understand well.
And C++ has changed very very much in 10-15 years I had
almost abandoned it :\

Soviet_Mario

unread,
Jul 16, 2019, 8:37:32 AM7/16/19
to
On 16/07/19 05:22, Öö Tiib wrote:
> On Tuesday, 16 July 2019 02:18:51 UTC+3, Soviet_Mario wrote:
>> On 16/07/19 00:40, Öö Tiib wrote:
>>
>>> constructors or destructors. 2) Returning objects of class
>>> type results with RVO (no copies made).
>>
>> could you explain this acronym ?
>
> I see James Kuyper made decent effort to explain it.

sure, he was kind. I just have to collect a copy of the
document he was referring to

> In practice the implementations pass the address for
> return value of class type to function like other arguments.

do you mean the CALLER passes an "hidden" further parameter
to the CALLEE indicating where to place the (automatic part
of the) built object ?
In this case, the inefficient COPY-constructor would not be
invoked ...

> The function is allowed to construct the object that it is going
> to return at that address and so elide copying when returning.

intresting ... I knew nothing about this mechanism

> It often surprises people who wrote side effects to copy or
> move constructors or destructors.

I could not understand what you meant ...
I had to add the copy constructor to just clone-initialize
objects explicitly.
I thought (wrongly) it would have been invoked also in
returning objects on the stack. But if this does not happen,
much better !

>
>>> 3) Your concept
>>> of shallow copies is also hard to understand:
>>
>> only automatic (on stack) members, no "remote" (dynamic)
>> data allocated with new / new []
>>
>>>
>>>> With shallow I mean : every automatic member not requiring
>>>> new or new [], with deep copy I mean exact copy (or even the
>>>> temporary itself ...).
>>>
>>> What are automatic members?
>>
>> the ones completely contained in the object itself.
>> I took the term automatic from "automatic" variables (with
>> local scope, built on the stack) contrasted with "dynamic"
>> variables (allocated manually from the heap and whose
>> lifetime is to be manually managed too).
>
> Hmm.
>
> Making efficient manual management of resources with C++
> has become quite complicated topic.

I was afraid so, but I have to do some MyString class, and
there the dynamic management is unavoidable I fear

> It can take more than week to read and to understand the
> nuances of "RAII", "rule of three", "copy-swap idiom",
> "rule of five", "rule of zero" and "rule of four (and half)".

I had just read about resource acquisition is
initialization. Never heard of the other bibl.

>
> People who write about one of those tend to oversimplify the issue.
> For example they typically forget to tell that with dynamically
> polymorphic class hierarchy (with virtual functions) all exposed
> copying, moving or swapping may cause sliced objects or worse.
> So protect or =delete all that and use factories and cloning
> with such hierarchies instead.


I am still not using virtual classes/members.
And not even inheritance of the kind IS-A (I tend to use the
HAS-A pattern, which I'm more confident of)


actually I'm using C++ more like "a better C" and little
more, or C-with-templates.

Soviet_Mario

unread,
Jul 16, 2019, 8:42:22 AM7/16/19
to
It was my first attempt : std::vector and std::list, but I
was unable to find the methods to do both dynamic
reallocation (grow / shrink) with the former and also random
access BY INDEX with the latter.

> If some objects really need to be
> dynamically allocated, like some OOP hierarchy objects, one
> should use std::make_unique() (or maybe std::make_shared()
> in special circumstances) and appropriate smartpointers.
>
> That said, the answer to your question is probably that by
> default the copies in C++ are "shallow" (a copy of a pointer
> or a standard smartpointer is just another pointer with the
> same value), to make it deep one needs to define custom copy
> and assignment operations.

I have defined both, it seemed inevitable with read-write
Strings

> The latter is done e.g. by
> std::vector, so copying std::vector objects always copies
> the whole data array.

I'll rescan the std:: types you adviced though :\
TY

>
> hth
> Paavo

Paavo Helde

unread,
Jul 16, 2019, 9:16:09 AM7/16/19
to
On 16.07.2019 15:42, Soviet_Mario wrote:
> On 16/07/19 14:04, Paavo Helde wrote:
>> On 16.07.2019 1:00, Soviet_Mario wrote:
>>>
>>> I've been a long time far from C++ and I realize I have forgotten most,
>>> and moreover don't understand "modern" C++ .... anyway
>>>
>>> When in a function (member or not is irrelevant) I build an aggregate
>>> object (class / struct, possibly nested with other), and this aggregate
>>> has a DEFAULT public constructor which does not resort to new or new [],
>>> but then the said former function invokes some class methods that DO
>>> some dynamic allocation, and then the function returns passing outside a
>>> copy by-value of the built object to the caller, placed "partially" on
>>
>> Your question is not very clear, consider to add some code examples to
>> make it clearer.
>>
>> In general, defining classes which do their own own low-level dynamic
>> memory allocation is tricky. One typically needs to define custom
>> constructors, assignments and destructors.
>>
>> Fortunately enough, there is near zero need to do such low-level
>> dynamic allocation nowadays in C++, as it's properly done and
>> encapsulated in standard library classes like std::vector.
>
> It was my first attempt : std::vector and std::list, but I was unable to
> find the methods to do both dynamic reallocation (grow / shrink) with
> the former and also random access BY INDEX with the latter.

std::vector is perfectly capable to dynamically grow and shrink. Insert
and erase elsewhere than in the end might be a bit slow if you have
millions of elements, but one should not worry about it prematurely.


James Kuyper

unread,
Jul 16, 2019, 9:48:11 AM7/16/19
to
On 7/16/19 8:28 AM, Soviet_Mario wrote:
> On 16/07/19 03:12, James Kuyper wrote:
>> On 7/15/19 7:18 PM, Soviet_Mario wrote:
>>> On 16/07/19 00:40, Öö Tiib wrote:
>>>> On Tuesday, 16 July 2019 01:01:03 UTC+3, Soviet_Mario wrote:
...
>> The standard term that matches what you mean is "non-static data member"
>> (12.2p3). Note that the storage duration of a non-static data member is
>> always the same as the storage duration of the containing object
>> (6.7.5p1). Non-static data members can therefore have static, automatic,
>> dynamic, or thread storage duration.
>
> could you give me a reference of the manual you are
> referring to with the notation "12.2p3" etc ?
> I mean a reference to some online manual
It's not a "manual", it's n4659.pdf, a free draft version of the C++
standard: the draft is almost exactly the same as the official C++2017
standard, so it's hard for me to justify paying the significant extra
cost of buying the actual standard. You can find it at
<http://www.open-std.org/jtc1/sc22/wg21/docs/standards>

...
>> A member is not dynamically allocated unless it is a non-static data
>> member of a dynamically allocated object, in which case all non-static
>> data members of the same object are dynamically allocated.
>
> I am referring to objects of a class derived from MyString,
> that have some automatic and / or static data members,

Will you please stop using "automatic" for that purpose? The correct
term is "non-static". Given the following declarations, occurring inside
a function body:

MyString Automatic;
MyString *Dynamic = new MyString();
static MyString Static;
thread_local MyString ThreadLocal;

and assuming that MyString contains a non-static data member named
"data", then Automatic.data has automatic storage duration,
Dynamic->data has dynamic storage duration, Static.data has static
storage duration, and ThreadLocal.data has thread local storage
duration. Being "automatic" is NOT a characteristic of the "data"
member, only of it's containing object.

If that member is a pointer to dynamically allocated memory, then *data
has dynamic storage duration, regardless of what storage duration data
itself has.

...
> So I was asking about the behaviour of returning data for
> these objects whose memory "footprint" is hybrid and also
> variable depending on context.
>
> I need to understand of glvalue and grvalue, as I can only
> recall the concepts of R-value and L-value, to one side, and
> value-types vs reference-types on the other, but I surely
> have to complete the definitions I understand

glvalue and prvalue are refinements of the concepts of l-value and
r-value, respectively. Frankly, I haven't fully familiarized myself with
the details of that refinement.

...
>> of the objects that they point at, any pointer can point at any suitable
>> object, regardless of that object's storage duration.
>
> ok, but my doubt was about : is it dynamic memory
> automatically duplicated (skeptic) ?

No.

> Is NEVER duplicated ?

It's duplicated whenever you explicitly write code duplicating it.

> (harmful). Is the whole regulated by invoking the
> COPY-constructor, so that it can decide ?

Only if you explicitly define the copy-constructor, and your
user-written copy constructor makes that decision.

For example, there is a strategy, called Copy-On-Write (COW), which
means that all direct copying, whether by assignment, construction, or
conversion, involves copying over only a pointer to the actual memory
being managed. As a result, a single block of memory might be shared
between many different containers. However, as soon as a write needs to
be done, the container in which the write occurred would copy the shared
memory to a new location, and then stop participating in the share. Only
then would it write to the new location.

That's all managed by user-written code - default constructors and
default assignment operator overloads never do anything that sophisticated.

I gather that COW used to be common in implementations of std::string,
but that it has since gone out of favor. My purpose in bringing it up is
only to point out that there are more sophisticated alternatives to your
simple shallow/deep copy distinction.


>> Therefore, it's
>> not appropriate for the compiler to automatically free the memory a
>> pointer points at.
>
> yes, but my doubt was more limited, I try to repeat it.
>
> In the CALLEE, building a return value (passed not by
> address or reference, just by-value => an hint to copy
> data), some dynamic allocation is done explicitly.
> The copy received by the caller, then would ...
> 1) receive NO data at all (I'm not saying the compiler has
> ever freed it, simply it remains lost, inaccessible,
> resultin in a leak)

No.

> 2) receive a link to the same content, copied in another pointer

If the object contains a pointer to the content, then a default
constructor or a default assignment operator overload will simply copy
that pointer.

> 3) receive a copy of the dynamic data placed elsewhere

In order to get that behavior, you have to define your own non-default
constructors and non-default assignment operator overloads.

...
>> It is the responsibility of the programmer, to make
>> sure that the memory it points at is in fact deallocated, when
>> appropriate,
>
> and this is managed now overloading operator = and also in
> the copy constructor.
> It would be safe but inefficient to have this same code when
> returning objects by value that were just built (in the case
> of initialization it would be inevitable instead, as there
> is no such duplicated operation)

You misunderstand: returning objects by value is NOT a separate kind of
operation. It is performed by using constructors - a copy constructor is
sometime necessary; but constructing the return value directly in it's
final location is often a permitted optimization. What happens depends
entirely upon what you've defined those functions to do, or if you've
let them be defined by default, it depends upon what the default
versions do.

>> and only if it was dynamically allocated. That's one of the
>> things that user-defined destructors are used for - to make sure the
>> memory is deallocated.
>>
>> Smart pointers are specialized classes that have destructors that, in
>> some cases, automatically deallocate the memory that they contain a
>> pointer to.
>
> I don't master them at all .... by now I prefer to just
> resort to things I understand well.

I recommend learning about smart pointers when you have time, but I also
agree that you should not use them until you've had time to learn about
them.

> And C++ has changed very very much in 10-15 years I had
> almost abandoned it :\

Among the most important changes that are relevant to this discussion is
"move" semantics. I'm not fully up to speed on move semantics, but my
understanding is that if you move one string into another string, only
the pointer to the contained data is copied, not the data itself, and
the original string ends up empty. The actual semantics of "move"
depends upon how you define move constructors or move assignment
operator overloads. As a general rule, if it is permissible to leave the
source empty, doing a move rather than a copy is usually more efficient,
and seldom less efficient.

James Kuyper

unread,
Jul 16, 2019, 10:07:17 AM7/16/19
to
On 7/16/19 8:42 AM, Soviet_Mario wrote:
...
> It was my first attempt : std::vector and std::list, but I
> was unable to find the methods to do both dynamic
> reallocation (grow / shrink)

The relevant methods are various overloads to insert() and erase(), as
well as push_back() and pop_back(). If you simply want to expand the
allocated memory without filling it immediately, use reserve(). If you
want to shed unneeded allocated memory, use shrink_to_fit().

> with the former and also random
> access BY INDEX with the latter.

By it's vary nature std::list cannot support random access. If you need
random access, you shouldn't be using a list.

If you absolutely need use an index, std::next(list.begin(), n) will
return an iterator referring to the n'th element of the list. This isn't
true random access: it applies operator++() to the iterator n times, in
order to get to that location.

Soviet_Mario

unread,
Jul 16, 2019, 11:16:57 AM7/16/19
to
On 16/07/19 15:47, James Kuyper wrote:
> On 7/16/19 8:28 AM, Soviet_Mario wrote:
>> On 16/07/19 03:12, James Kuyper wrote:
>>> On 7/15/19 7:18 PM, Soviet_Mario wrote:
>>>> On 16/07/19 00:40, Öö Tiib wrote:
>>>>> On Tuesday, 16 July 2019 01:01:03 UTC+3, Soviet_Mario wrote:
> ...
>>> The standard term that matches what you mean is "non-static data member"
>>> (12.2p3). Note that the storage duration of a non-static data member is
>>> always the same as the storage duration of the containing object
>>> (6.7.5p1). Non-static data members can therefore have static, automatic,
>>> dynamic, or thread storage duration.
>>
>> could you give me a reference of the manual you are
>> referring to with the notation "12.2p3" etc ?
>> I mean a reference to some online manual
> It's not a "manual", it's n4659.pdf, a free draft version of the C++
> standard: the draft is almost exactly the same as the official C++2017
> standard, so it's hard for me to justify paying the significant extra
> cost of buying the actual standard. You can find it at
> <http://www.open-std.org/jtc1/sc22/wg21/docs/standards>

Thanks, I'm gonna get the DOC !

>
> ...
>>> A member is not dynamically allocated unless it is a non-static data
>>> member of a dynamically allocated object, in which case all non-static
>>> data members of the same object are dynamically allocated.
>>
>> I am referring to objects of a class derived from MyString,
>> that have some automatic and / or static data members,
>
> Will you please stop using "automatic" for that purpose? The correct
> term is "non-static".

mmm, the fact is : to me non-static seems a WIDER definition
than automatic, as dynamic also is non-static :)

> Given the following declarations, occurring inside
> a function body:
>
> MyString Automatic;
> MyString *Dynamic = new MyString();
> static MyString Static;
> thread_local MyString ThreadLocal;
>
> and assuming that MyString contains a non-static data member named
> "data", then Automatic.data has automatic storage duration,
> Dynamic->data has dynamic storage duration, Static.data has static
> storage duration, and ThreadLocal.data has thread local storage
> duration. Being "automatic" is NOT a characteristic of the "data"
> member, only of it's containing object.

uhm .... :\

>
> If that member is a pointer to dynamically allocated memory, then *data
> has dynamic storage duration, regardless of what storage duration data
> itself has.

this IS clear !

>
> ...
>> So I was asking about the behaviour of returning data for
>> these objects whose memory "footprint" is hybrid and also
>> variable depending on context.
>>
>> I need to understand of glvalue and grvalue, as I can only
>> recall the concepts of R-value and L-value, to one side, and
>> value-types vs reference-types on the other, but I surely
>> have to complete the definitions I understand
>
> glvalue and prvalue are refinements of the concepts of l-value and
> r-value, respectively. Frankly, I haven't fully familiarized myself with
> the details of that refinement.

OK ... no problem, maybe I won't need to refine too :)

>
> ...
>>> of the objects that they point at, any pointer can point at any suitable
>>> object, regardless of that object's storage duration.
>>
>> ok, but my doubt was about : is it dynamic memory
>> automatically duplicated (skeptic) ?
>
> No.
>
>> Is NEVER duplicated ?
>
> It's duplicated whenever you explicitly write code duplicating it.

OK : I have written the copy-constructor, yes

>
>> (harmful). Is the whole regulated by invoking the
>> COPY-constructor, so that it can decide ?
>
> Only if you explicitly define the copy-constructor, and your
> user-written copy constructor makes that decision.
>
> For example, there is a strategy, called Copy-On-Write (COW), which

yes...no ! No, I don't want COW even if it is faster and
maybe more parsimonious. I just need code safer and simpler
to read.

> means that all direct copying, whether by assignment, construction, or
> conversion, involves copying over only a pointer to the actual memory
> being managed. As a result, a single block of memory might be shared
> between many different containers.

yes it's like the SNAPSHOTS on virtual machine disks : they
actually record only the changes of status, not the entire
status of the file system. Also BTRFS snapshots work like that.

I'd prefer more "synced" status of each object than deferred
writes

> However, as soon as a write needs to
> be done, the container in which the write occurred would copy the shared
> memory to a new location, and then stop participating in the share. Only
> then would it write to the new location.
>
> That's all managed by user-written code - default constructors and
> default assignment operator overloads never do anything that sophisticated.
>
> I gather that COW used to be common in implementations of std::string,
> but that it has since gone out of favor. My purpose in bringing it up is
> only to point out that there are more sophisticated alternatives to your
> simple shallow/deep copy distinction.

agreed. I had not even thought about such more advanced
features as I just need to have simple and solid code : it
won't be performance intensive

>
>
>>> Therefore, it's
>>> not appropriate for the compiler to automatically free the memory a
>>> pointer points at.
>>
>> yes, but my doubt was more limited, I try to repeat it.
>>
>> In the CALLEE, building a return value (passed not by
>> address or reference, just by-value => an hint to copy
>> data), some dynamic allocation is done explicitly.
>> The copy received by the caller, then would ...
>> 1) receive NO data at all (I'm not saying the compiler has
>> ever freed it, simply it remains lost, inaccessible,
>> resultin in a leak)
>
> No.
>
>> 2) receive a link to the same content, copied in another pointer
>
> If the object contains a pointer to the content, then a default
> constructor or a default assignment operator overload will simply copy
> that pointer.
>
>> 3) receive a copy of the dynamic data placed elsewhere
>
> In order to get that behavior, you have to define your own non-default
> constructors and non-default assignment operator overloads.

OK clear: I should not expect strange hidden behaviour.

I'm getting maybe close to some compilable code .... then
I'll inspect all in the Debugger. Some times is more useful
to observe live than to try to figure out theoretically
mmm ... unheard of .... thanks for the hint !

James Kuyper

unread,
Jul 16, 2019, 1:30:35 PM7/16/19
to
On Tuesday, July 16, 2019 at 11:16:57 AM UTC-4, Soviet_Mario wrote:
> On 16/07/19 15:47, James Kuyper wrote:
> > On 7/16/19 8:28 AM, Soviet_Mario wrote:
...
> >> I am referring to objects of a class derived from MyString,
> >> that have some automatic and / or static data members,
> >
> > Will you please stop using "automatic" for that purpose? The correct
> > term is "non-static".
>
> mmm, the fact is : to me non-static seems a WIDER definition
> than automatic, as dynamic also is non-static :)

Yes, it is wider. All members are either member functions, static data
members, non-static data members, nested classes, or typedef members.

The problem is that "automatic", as that term is defined by the
standard, doesn't describe a property of a class member - it describes a
property of a particular instance of a class, and that property applies
to all of the non-static data members of that instance. It's not
meaningful to talk about some of the members being automatic and others
being dynamic.

This isn't just pedantry - it's directly relevant to the issue you're
worrying about. Consider a class containing just two members: a pointer
to a dynamically allocated array and a count of the number of elements
in that array. I suspect that you would describe the count as
"automatic"; I'm not sure exactly what you would say about the pointer.
The relevant facts are that both the count and the pointer are
non-static data members. They both always have the same storage duration
- which is the storage duration of the object that they are part of. The
memory that the pointer points at is dynamically allocated - but that's
not a feature of the pointer member, it's a feature of the way that the
class code uses that pointer.

A default copy will copy both the value of the count and the value of
the pointer, and will not do anything special regarding the memory that
the pointer points at. If that's not what you want to have happen,
you'll have to write a constructor that actually does do what you want
to have happen.

Soviet_Mario

unread,
Jul 16, 2019, 1:45:00 PM7/16/19
to
On 16/07/19 19:30, James Kuyper wrote:
> On Tuesday, July 16, 2019 at 11:16:57 AM UTC-4, Soviet_Mario wrote:
>> On 16/07/19 15:47, James Kuyper wrote:
>>> On 7/16/19 8:28 AM, Soviet_Mario wrote:
> ...
>>>> I am referring to objects of a class derived from MyString,
>>>> that have some automatic and / or static data members,
>>>
>>> Will you please stop using "automatic" for that purpose? The correct
>>> term is "non-static".
>>
>> mmm, the fact is : to me non-static seems a WIDER definition
>> than automatic, as dynamic also is non-static :)
>
> Yes, it is wider. All members are either member functions, static data
> members, non-static data members, nested classes, or typedef members.
>
> The problem is that "automatic", as that term is defined by the
> standard, doesn't describe a property of a class member - it describes a
> property of a particular instance of a class, and that property applies
> to all of the non-static data members of that instance. It's not
> meaningful to talk about some of the members being automatic and others
> being dynamic.
>
> This isn't just pedantry -

oh, friend, never said or even thought so !!
I am just old-fashioned minded, an era when autopointers or
worse AUTO determination of the return value by its context
was really far fetched. So I was just trying to justify
myself, my way of thinking.

> it's directly relevant to the issue you're
> worrying about. Consider a class containing just two members: a pointer
> to a dynamically allocated array and a count of the number of elements
> in that array. I suspect that you would describe the count as
> "automatic";

both the members are automatic. The pointer also IS. The
buffer pointed to, on the contrary, I would not call it
automatic, but dynamic.

> I'm not sure exactly what you would say about the pointer.

that in itself is automatic, as it still exist (it takes up
4 or 8 byte even if it contains NULLPTR)

> The relevant facts are that both the count and the pointer are
> non-static data members. They both always have the same storage duration

I agree in fact

> - which is the storage duration of the object that they are part of. The
> memory that the pointer points at is dynamically allocated - but that's
> not a feature of the pointer member,

yes it's not an intrinsical property of it, but this is not
really important for the passing out mechanism

> it's a feature of the way that the
> class code uses that pointer.

yes, now I have understood that I can rely on the fact that
the compiler itself generates no hidden code having side
effects on dynamic data, except for calling copy-constructor
and destructor, which I have to write myself.

And also that I cannot make assumptions whether or not the
copy constructor is actually called or some optimization
(like building the returned object in a location provided by
the CALLER itself, or copying it back by MY copy-ctor)
eliding copy happens.

>
> A default copy will copy both the value of the count and the value of
> the pointer, and will not do anything special regarding the memory that
> the pointer points at.

I have my hand written copy-ctor that copies data, so I
should be sure no overlapping ownership could take place

> If that's not what you want to have happen,
> you'll have to write a constructor that actually does do what you want
> to have happen.
>

yes. Many thanks to you and every one else. Some rust
removed successfully :) :)
CIAO

Juha Nieminen

unread,
Jul 17, 2019, 3:36:54 AM7/17/19
to
Soviet_Mario <Sovie...@cccp.mir> wrote:
> When in a function (member or not is irrelevant) I build an
> aggregate object (class / struct, possibly nested with
> other), and this aggregate has a DEFAULT public constructor
> which does not resort to new or new [], but then the said
> former function invokes some class methods that DO some
> dynamic allocation, and then the function returns passing
> outside a copy by-value of the built object to the caller,
> placed "partially" on the stack, the final copy will be
> shallow or deep ?

It's up that type returned by that function how it behaves
when copied. For example, if the returned type is std::vector,
copying it will make a deep copy (although in this case that
will most probably be skipped because of return value optimization).
If the returned type is std::shared_ptr, copying it will,
essentially, be a shallow copy (the managed object will not
be copied; all std::shared_ptr instances copied from the
original will share the same managed object).

If it's your own type, your own class, that's being returned,
then it's up to you how it behaves when copied.

The first part of your question is completely unrelated to this
and I don't really understand why you think it's relevant. It
doesn't matter *where* that function is called from.

Öö Tiib

unread,
Jul 17, 2019, 4:41:33 AM7/17/19
to
On Tuesday, 16 July 2019 15:37:32 UTC+3, Soviet_Mario wrote:
> On 16/07/19 05:22, Öö Tiib wrote:
>
> > In practice the implementations pass the address for
> > return value of class type to function like other arguments.
>
> do you mean the CALLER passes an "hidden" further parameter
> to the CALLEE indicating where to place the (automatic part
> of the) built object ?
> In this case, the inefficient COPY-constructor would not be
> invoked ...

Yes, there is hidden parameter pointing at
uninitialized memory.

> > The function is allowed to construct the object that it is going
> > to return at that address and so elide copying when returning.
>
> intresting ... I knew nothing about this mechanism

Also where copy elision is allowed but compiler does not find a
way to elide copy there it is typically required to move if
move constructors are available. Read that too, it is less
formal and accurate but bit easier to read than standard:
https://en.cppreference.com/w/cpp/language/copy_elision

>
> > It often surprises people who wrote side effects to copy or
> > move constructors or destructors.
>
> I could not understand what you meant ...
> I had to add the copy constructor to just clone-initialize
> objects explicitly.
> I thought (wrongly) it would have been invoked also in
> returning objects on the stack. But if this does not happen,
> much better !

I meant that people sometimes add side effects to their
copy/move constructors and destructors. For example output to some
log. Then are surprised to not see the log entries they expected.

> >
> >>> 3) Your concept
> >>> of shallow copies is also hard to understand:
> >>
> >> only automatic (on stack) members, no "remote" (dynamic)
> >> data allocated with new / new []
> >>
> >>>
> >>>> With shallow I mean : every automatic member not requiring
> >>>> new or new [], with deep copy I mean exact copy (or even the
> >>>> temporary itself ...).
> >>>
> >>> What are automatic members?
> >>
> >> the ones completely contained in the object itself.
> >> I took the term automatic from "automatic" variables (with
> >> local scope, built on the stack) contrasted with "dynamic"
> >> variables (allocated manually from the heap and whose
> >> lifetime is to be manually managed too).
> >
> > Hmm.
> >
> > Making efficient manual management of resources with C++
> > has become quite complicated topic.
>
> I was afraid so, but I have to do some MyString class, and
> there the dynamic management is unavoidable I fear

Usual advice is to use standard library classes (like std::string)
as data members (or sometimes as private bases) of your class.
These do memory management for you and so you have to write only
the special functionality of your MyString.

> > It can take more than week to read and to understand the
> > nuances of "RAII", "rule of three", "copy-swap idiom",
> > "rule of five", "rule of zero" and "rule of four (and half)".
>
> I had just read about resource acquisition is
> initialization. Never heard of the other bibl.

For historic reasons the C++ compiler generates some public
code for classes implicitly (if it can). That code can be correct
or incorrect depending on logic of class and so people have made
rules how to ensure that it does not cause any issues.

> > People who write about one of those tend to oversimplify the issue.
> > For example they typically forget to tell that with dynamically
> > polymorphic class hierarchy (with virtual functions) all exposed
> > copying, moving or swapping may cause sliced objects or worse.
> > So protect or =delete all that and use factories and cloning
> > with such hierarchies instead.
>
>
> I am still not using virtual classes/members.
> And not even inheritance of the kind IS-A (I tend to use the
> HAS-A pattern, which I'm more confident of)
>
>
> actually I'm using C++ more like "a better C" and little
> more, or C-with-templates.

When you work alone then it is fine, but success is easier to achieve
when using code written by others and so cooperating with others.

Rosario19

unread,
Jul 23, 2019, 3:26:53 AM7/23/19
to
On Tue, 16 Jul 2019 14:28:23 +0200, Soviet_Mario wrote:

>I am referring to objects of a class derived from MyString,
>that have some automatic and / or static data members,
>existing even for "empty" MyStrings, and some dynamic
>content (created and destroyed using new char [] / delege []
>
>an empty MyString can be built without calling any new char
>[] / delege [], but further elaboration, which assigns
>content, requires it, or even reallocation.
>
>So I was asking about the behaviou

in un debugger, puoi vedere le funzioni che vengono chiamate...
in generale ogni obj non tuo, ha propri costruttori e tutto va bene
no leak ecc

se ci sono object tuoi, che abbisognano memoria dinamica allora devi
stare attento... per quel che mi riguarda in qualche esercizio la cosa
che avrei fatto, per chiarire il fatto della memoria, è stato
(ri)costruire malloc free e new e vedere come la memoria veniva
utilizzata
nei obj che avevo costruito, in particolare vedere se c'erano leak

0 new messages