|
![](/i/fill.gif) |
On 28/02/2014 02:16 PM, Warp wrote:
> Orchid Win7 v1<voi### [at] dev null> 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
|
![](/i/fill.gif) |