Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Raw pointers not evil after all?

Reply
Thread Tools

Raw pointers not evil after all?

 
 
mike3
Guest
Posts: n/a
 
      05-01-2013
On May 1, 1:40*pm, Christopher Pisz <(E-Mail Removed)> wrote:
> On 5/1/2013 3:29 PM, Christopher Pisz wrote:

<snip>
> P.S
> In your example above void f() allocated the object and therefore should
> own it. Design problem. Write Owner in such a manner that it allocates
> the object and owns it.
>


So then the "owner" should be the same as the thing that allocates it?

> Owner::Add(params){...//allocates and stores}
>
> const Owned & Owner::Get(params){...//returns a const reference or
> reference to the stored object}
>


But what should Owner's internal vector contain? Raw pointers or some
kind of smart pointer?

> Better yet, since you are just wrapping a std::vector, implement
> Owner::iterator and Owner::const_iterator. Look at what the vector
> itself does. Does it return a pointer? Nope. Does it own its elements?
> Yep. Can users get and modify the contained object? Yep. How does it do
> that?


But with a real life object which might be more than just a "wrapped
vector", might such an interface be inappropriate? I was just using
that as an example...
 
Reply With Quote
 
 
 
 
Christopher Pisz
Guest
Posts: n/a
 
      05-02-2013
On 5/1/2013 6:27 PM, mike3 wrote:
> On May 1, 1:40 pm, Christopher Pisz <(E-Mail Removed)> wrote:
>> On 5/1/2013 3:29 PM, Christopher Pisz wrote:

> <snip>
>> P.S
>> In your example above void f() allocated the object and therefore should
>> own it. Design problem. Write Owner in such a manner that it allocates
>> the object and owns it.
>>

>
> So then the "owner" should be the same as the thing that allocates it?
>
>> Owner::Add(params){...//allocates and stores}
>>
>> const Owned & Owner::Get(params){...//returns a const reference or
>> reference to the stored object}
>>

>
> But what should Owner's internal vector contain? Raw pointers or some
> kind of smart pointer?
>
>> Better yet, since you are just wrapping a std::vector, implement
>> Owner::iterator and Owner::const_iterator. Look at what the vector
>> itself does. Does it return a pointer? Nope. Does it own its elements?
>> Yep. Can users get and modify the contained object? Yep. How does it do
>> that?

>
> But with a real life object which might be more than just a "wrapped
> vector", might such an interface be inappropriate? I was just using
> that as an example...
>



In my opinion yes, The owner should be the class that allocated the
object. This isn't always possible, but more often that not, it is
possible and should be designed that way. Google "Resource Allocation Is
Initialization" if you haven't already. What you will find is related.

In your example, you don't really describe what kinds of things users of
Owner would like to do. Assuming that they just want to look at the
object contained and read or modify it's values, I would personally
implement that vector to contain raw pointers to allocated Owned
objects, which get allocated on a call to Add. Make sure that the vector
is emptied and each object is released as it is emptied in the
Deconstructor and make sure you handle release in a Remove method if you
implement one. You may implement a Get that returns a reference to the
allocated object by dereferencing the pointer. In this manner, you are
using raw pointers, but no one outside your class is going to use or see
a pointer at all. It is all encapsulated within that class. This is all
assuming of course that you are not considering thread safety, but let's
keep it simple. I've done exactly this in production code all the time.

That is _if_ you expect the cost of copy constructing Owned and passing
it around by value to be greater than allocating and releasing it. Owned
might be so simple that you can forget using pointers and allocation at
all, just pass it around by value. Of course performance measurement and
microoptimization is another topic altogether!

-----
shared pointer is harder to explain and someone else might do it better,
but generally I've used it in cases where I want to make sure something
can be shared outside of my class. Shared is different than looked at. I
suppose shared might mean another class might actually use that shared
pointer as part of its state, or in other words, needs it for part or
all of its lifetime. If it just needs to get looked at, I use a
reference if possible.

I do not want to give someone a raw pointer outside my class if I can
help it. That would allow them to delete it or I might throw an
exception and the pointer is bad because I destroyed the object when I
got destroyed. In the other example we used raw pointers _inside_ the class.

When you do this though, you do indeed have to be very careful and look
at everything's lifetime, make sure you do not create cyclical
references, or if you can't help it, than use a weak_ptr where appropriate.

Perhaps a DatabaseConnectionPool hands out a connection that is to be
reused, to different classes that actually query for data for example.
They need a connection in order to perform queries perhaps for their
entire lifetime. The pool is responsible for actually allocating and
cleaning up the connections (even if that means forcing close on them at
destruction), while the other classes just use it to do their queries.

This is all my personal opinion. i don't think anyone has actually
called me out on when to use which option before.







 
Reply With Quote
 
 
 
 
Stefan Ram
Guest
Posts: n/a
 
      05-02-2013
Christopher Pisz <(E-Mail Removed)> writes:
>The owner should be the class that allocated the object.


One can have several different instances of the same class
at run-time each of which can own /different/ entities.

This shows that the entities which can own at run-time are
instances, not classes. Classes are the source-code
(blueprint) for instances.

Unless, of course, one does procedural non-OOP programming
where classes are used just with static members. But the
use of the word »object« above indicates that this was
related to OOP - altough, in C++, an object actually is
just a region of memory, such as an int object, which is
not an instance of a class.

When one hears some people talk about OOP, one could be lead
to believe that this was »class-oriented programming«! All
they see in the source code are classes. There are no
objects in the source code! So they start to believe that it
was all about classes. They still need to develop a correct
mental model of the run-time world of an OOP program, which
differs from the source-code world.

 
Reply With Quote
 
Nobody
Guest
Posts: n/a
 
      05-02-2013
On Wed, 01 May 2013 16:21:03 -0700, mike3 wrote:

>> Be a good programmer and use what is appropriate for the task rather
>> than trying to form a general rule and expect magical results. I use raw
>> pointers, shared_ptrs, weak_ptrs, and auto_ptrs in my code.

>
> So how should one determine which kind of pointer is appropriate for
> which kind of task? For what kinds of tasks are raw pointers appropriate?
> shared_ptr? weak_ptr? auto_ptr?


Raw pointers for transient references.

auto_ptr or unique_ptr where ownership is clear-cut.

shared_ptr where ownership is not clear-cut, but there can be no circular
references.

weak_ptr where you need to avoid circular references and the pointer is
not essential.

In all cases, you need to be aware of which operations can result in
destruction (blindly using shared_ptr for even the most transient
reference isn't a practical solution).

For anything which doesn't fit the above, you need garbage collection.

 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      05-02-2013
On Thursday, 2 May 2013 02:03:00 UTC+1, Christopher Pisz wrote:

[...]
> The owner should be the class that allocated the
> object.


If the owner should be the class which allocated it, why bother
with dynamic allocation. There are some exceptions, when
a class implements a dynamically sized structure (like
`std::vector`), but most of the time, the reason you're
allocating dynamically is that the object has an arbitrary
lifetime of its own, which may excede that of the object which
creates it.

And why this insistence on ownership. In many application, the
most important objects aren't (or shouldn't be) owned by anyone.
The object itself manages its lifetime, in response to whatever
external events it waits one. (Who "owns" the window object in
a GUI application?) Or the objects are created by some sort of
factory (which certainly doesn't "own" them).

--
James
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      05-02-2013
On Thursday, 2 May 2013 00:21:03 UTC+1, mike3 wrote:
> On May 1, 1:29*pm, Christopher Pisz <(E-Mail Removed)> wrote:
> > On 5/1/2013 2:19 AM, mike3 wrote:

> <snip>
> > Be a good programmer and use what is appropriate for the
> > task rather than trying to form a general rule and expect
> > magical results. I use raw pointers, shared_ptrs, weak_ptrs,
> > and auto_ptrs in my code.


> So how should one determine which kind of pointer is
> appropriate for which kind of task? For what kinds of tasks
> are raw pointers appropriate? shared_ptr? weak_ptr?
> auto_ptr?


Raw pointers are the default. They probably represent 90% or
more of the pointers in a normal application. The various smart
pointers fulfil special needs: a typical example is when an
object is logically constructed in several steps: you hold it in
an auto_ptr until the construction is finished, and it is ready
to be handles however fully constructed objects are handled.
If it's an entity object responding to external events, for
example, you would hold it in an auto_ptr until it has been able
to register for those events (and if it can register for those
events in the constructor, you probably don't need any smart
pointer for it ever).

I might add that if you need weak_ptr, you're using shared_ptr
in a context where it isn't appropriate.

--
James

 
Reply With Quote
 
Stefan Ram
Guest
Posts: n/a
 
      05-02-2013
James Kanze <(E-Mail Removed)> writes:
>And why this insistence on ownership.


I think this is about reference-counting: The object counts
its owners and disposes itself when there is no owner left.

Ownership is created by holding a reference to the object
and intending to possibly dereference this in the future.
»Reference« not in the sense of the C++ reference types,
but in the sense of a C reference.

Thus, when a reference is copied, there is one more owner,
++refcount, when an ownership is moved, the number of owners
does not change, when an owner does not want to use his
reference any more, it is decremented: --refcount, and
dispose self if refcount < 1 now.

 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      05-02-2013
On Thursday, 2 May 2013 14:20:43 UTC+1, Nobody wrote:
> On Wed, 01 May 2013 16:21:03 -0700, mike3 wrote:
>
> >> Be a good programmer and use what is appropriate for the task rather
> >> than trying to form a general rule and expect magical results. I use raw
> >> pointers, shared_ptrs, weak_ptrs, and auto_ptrs in my code.


> > So how should one determine which kind of pointer is appropriate for
> > which kind of task? For what kinds of tasks are raw pointers appropriate?
> > shared_ptr? weak_ptr? auto_ptr?


> Raw pointers for transient references.


> auto_ptr or unique_ptr where ownership is clear-cut.


Most of my uses of auto_ptr are for "temporary" ownership. The
code ends with a call of release on the auto_ptr. Except, of
course, if there is an exception (which would prevent the code
from executing to its term, at which point, the object is
managed otherwise, often by itself).

Another case where auto_ptr is appropriate is when you don't
want to be able to access the object once you've passed it on.
I use them in the interface of my interthread queues: once the
object has been posted to another thread, it cannot be accessed
by the initial thread.

> shared_ptr where ownership is not clear-cut, but there can be no circular
> references.


> weak_ptr where you need to avoid circular references and the pointer is
> not essential.


In which case, shared_ptr probably isn't the appropriate
solution.

> In all cases, you need to be aware of which operations can result in
> destruction (blindly using shared_ptr for even the most transient
> reference isn't a practical solution).


> For anything which doesn't fit the above, you need garbage collection.


Garbage collection doesn't really solve the same problems
(although some people do try to use shared_ptr as a garbage
collector). Garbage collection doesn't address object lifetime
issues, and if programmers never made mistakes, it probably
wouldn't be necessary in a C++ context (where value semantics
rule, and the only dynamically allocated objects are those which
application determined lifetimes). Since programmers aren't
perfect, however, robust applications do need garbage
collection. In order to be able to detect use of "dead"
objects: if you delete immediately, and the memory is recycled,
any use of a remaining stray pointers to the object will result
in who knows what. Garbage collection ensures that the memory
won't be recycled as long as it is reachable, so you can set an
identifiable state, check for it, and handle the error
gracefully (which may mean an assertion failure---but cannot
result in the program doing other dammage).

--
James
 
Reply With Quote
 
Stefan Ram
Guest
Posts: n/a
 
      05-02-2013
James Kanze <(E-Mail Removed)> writes:
>collector). Garbage collection doesn't address object lifetime
>issues, and if programmers never made mistakes, it probably
>wouldn't be necessary in a C++ context (where value semantics


One might implement a graph editor in C++, where there is a
person (the user of the software) who can add or remove
other persons he likes. He also might add like-relations
beetween arbitrary two persons (»A likes B«, »B likes A«, »B
likes C«), but when there is no »likes-path« anymore from
the user to a persons, the person can be deleted from the graph.
Due to cycles, reference counting is difficult here, so one
effectively needs to write a subroutine to find whether
after a modification to the graph a person is still reachable
from the user via a path of likes, i.e., a garbage collector.

 
Reply With Quote
 
Larry Evans
Guest
Posts: n/a
 
      05-02-2013
On 05/02/13 12:40, Stefan Ram wrote:
> James Kanze <(E-Mail Removed)> writes:
>> collector). Garbage collection doesn't address object lifetime
>> issues, and if programmers never made mistakes, it probably
>> wouldn't be necessary in a C++ context (where value semantics

>
> One might implement a graph editor in C++, where there is a
> person (the user of the software) who can add or remove
> other persons he likes. He also might add like-relations
> beetween arbitrary two persons (»A likes B«, »B likes A«, »B
> likes C«), but when there is no »likes-path« anymore from
> the user to a persons, the person can be deleted from the graph.
> Due to cycles, reference counting is difficult here, so one
> effectively needs to write a subroutine to find whether
> after a modification to the graph a person is still reachable
> from the user via a path of likes, i.e., a garbage collector.
>

Also, in this use case, I don't think the combination of
shared_ptr and weak_ptr would solve the problem because
there's no easy way to detect when a cycle is created.
I guess you could, whenever a smart pointer is created,
traverse the graph to detect whether it creates a cycle,
and if it does, then create a weak_ptr, if not, create
a shared_ptr. However, that seems pretty expensive.

-regards,
Larry

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Do not cast pointers to functions to pointers to primitive types ajaybgr C Programming 18 09-07-2012 04:34 AM
pointers, pointers, pointers... cerr C Programming 12 04-07-2011 11:17 PM
Evil, evil wxPython (and a glimmer of hope) vivainio@gmail.com Python 12 02-17-2006 07:49 PM
Adobe PS RAW convertor vs Nikon RAW convertor Vlad Gunko Digital Photography 8 01-25-2005 07:43 PM
How raw is RAW format? Editor www.nutritionsoftware.org Digital Photography 4 12-22-2003 07:33 PM



Advertisments