POV-Ray : Newsgroups : povray.off-topic : Undirected ramblings on C++ : Re: Undirected ramblings on C++ Server Time
28 Jul 2024 12:28:08 EDT (-0400)
  Re: Undirected ramblings on C++  
From: Orchid Win7 v1
Date: 28 Feb 2014 14:16:30
Message: <5310e08e@news.povray.org>
On 28/02/2014 02:16 PM, Warp wrote:
> Orchid Win7 v1<voi### [at] devnull>  wrote:
>>     struct Side
>>     {
>>       Pattern&  Pattern;
>>       Pattern::iterator Iterator;
>>     }
>
> I think you are misunderstanding what C++ references are.

Perhaps.

Basically, last time I read a book on C++, it said to never use pointers 
and always use references. So that's what I did. (And it worked - 
eventually. It just wasn't particularly easy...)

> You are clearly trying to use references to make something like a
> list/tree node. References cannot really be used for that in a sensible
> manner (because as said the node becomes non-assignable and you can't
> have "null references".) Such nodes are usually done with pointers instead.

Let me explain what I was *actually* trying to do...

I'm using an iterator to keep track of where I am in a collection. 
("Pattern" is actually a trivial alias to std::vector.) But as soon as 
you value-copy a container, all the iterators suddenly stop being valid. 
Ergo, I have to somehow avoid copying the vector at any cost.

What I would *actually* like to do is just throw iterators around. 
However, for reasons beyond my comprehension, nobody thought to include 
an "is_at_end()" method in the standard iterators. So without access to 
the original container, it's impossible to know if you've reached the 
end. If it weren't for this annoying detail, I wouldn't need references 
at all!

...well, OK, maybe I would. Because the whole *point* of the code I'm 
writing is that you call some function, passing a bunch of iterators, 
and when that function returns, the iterators have moved. That's not 
going to work if you pass iterators by value. (And yet, when I started 
searching the Internet, everybody seemed to think having a pointer to an 
iterator is 100% guaranteed to be a bug...)

> As you should be well aware, you shouldn't start messing with pointers
> in C++ unless you know what you are doing.

In the one place where I had to do that, it made me really nervous...

>> Every single line that declares a State variable gives me C2512. As you
>> can see, I clearly *have not* defined any constructors, so that isn't
>> the reason for a default constructor not existing.
>
> The compiler cannot create a constructor because it doesn't know what
> the reference should be initialized with.

I figured that out eventually. But it would have been nice if the 
*compiler* could have told me that!

>> Does that also mean that assigning one vector to another actually
>> *copies* the entire vector? As in, a deep copy??
>
> Like all standard data containers, they are handled by value. Assigning
> one container object to another makes a deep copy.

I always find it slightly surprising when C++ actually does stuff for 
you automatically. Everything seems to manual most of the time...

>> I didn't mean references specifically. I just meant the entire language.
>> Nobody would try to claim that C++ is small or simple or free of
>> surprises. For example,
>
>>      struct Side
>>      {
>>        Pattern&  ThePattern;
>>        Side(Pattern p) : ThePattern(p) {}
>>      }
>
> You should understand what's happening here.
>
> The constructor is taking an object *by value*, which means that inside
> the constructor 'p' is a temporary object (which will disappear when
> the constructor ends.) You are then making 'ThePattern' a reference to
> said temporary value. Then the constructor ends and... 'ThePattern' refers
> to a non-existeng object. Crash.

Yeah. I figured that out eventually. Not, of course, that you can ask 
the IDE to trap a segfault and pop up the line of code it was trying to 
run when the crash happened. I had to laboriously step through the code 
line by line until I discovered the problem.

> You seem to be programming this like it were Java. C++ isn't.

It would be more accurate to say "like it were C#".

> Try to stop using references or pointers if you can.

It would certainly be simpler if I could find a way to do that...

> Does 'ThePattern' need to point to some other object, or can it simply
> contain a *copy* of that object?

As I say, copying it stops all the iterators working.

>> Today it's references. Last time it was
>
>>     catch (std::exception e)
>>     {
>>       std::cout<<  e.what();
>>     }
>
>> which *always* says std::exception, even if that wasn't the exception
>> thrown.
>
> I don't understand where you are getting at.

Apparently you have to say "std::exception & e", otherwise whatever the 
exception was gets type-cast to a plain std::exception. (In particular, 
I was throwing std::runtime_error with an explanatory message, and 
tearing my hair out trying to figure out why the hell I can't see the 
message in the debug logs...)

>> The time before that, it was a memory leak due to an (empty)
>> destructor not being virtual.
>
> I think one of the major problems you are having is that you insist in
> allocating things dynamically and having pointers to them (instead of
> using eg. smart pointers or just using the standard data containers.)

The memory leak was due to some code that does actual run-time 
polymorphism. As I understand it, it's impossible to do dynamic dispatch 
in C++ without pointers.

> I code in C++ almost every day as my payjob. I write thousands and thousands
> of likes of C++ code every year. Do you know when was the last time I wrote
> the keywords 'new' or 'delete'? I don't even remember. It must have been
> years.

The code I'm fiddling with today doesn't contain *any* dynamic 
allocation at all. I'm merely trying to use references to avoid copying 
stuff, to keep iterators valid, and to allow a function to alter 
variables in the caller (i.e., "out parameters").



As an amusing aside, the other day I got to read some code where a guy 
did the opposite of what you accuse me of: he wrote C# as if it was C++. 
It was ugly! I especially enjoyed how EVERY SINGLE CLASS had an explicit 
destructor like this:

   public class Foo
   {
     private Bar _bar1, _bar2, _bar3;

     ...

     public ~Foo()
     {
       // Set fields to NULL to let the garbage collector know that
       // the memory can be reclaimed.
       _bar1 = null;
       _bar2 = null;
       _bar3 = null;
     }

...um, yeah, I'm not sure you're understanding this whole "automatic 
garbage collection" idea. :-P If this destructor ever runs, then it is 
highly likely that the fields you're nulling out have *already* been 
garbage collected! In fact, ironically, by writing an explicit 
destructor, you're actually keeping the object alive *longer*!

(In C#, any object that has a destructor gets added to a special list, 
and the destructor gets run on the *next* GC sweep. So normal objects 
get collected in one sweep, objects with a destructor take two 
consecutive GC sweeps to reclaim. Oops!)

In short, this code does the *opposite* of what the comment claims. If I 
just saw the code, I'd be puzzled. But the comment displays a 
fundamental misunderstanding of how the language actually works...


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.