Kóder vagy sorozatgyilkos?
Can you tell a coder from a cannibal? Try to work out which of the following spent their time hacking computers and which preferred hacking away at corpses instead.
Ruby is a language like Java, everything is reference to object. An assignment does not produce copy of the value, but the copy of the reference. So if you have an a
object, and you say b=a
then you will have a b
object reference pointing to the same place as a
.
The naive approach of a string is that if I say a='x12345y'; b=a; b.sub!('234','---');
will result in an a
which's value is 'x12345y'
and a b
which's value is 'x1---5y'
.
The Java guys invented the immutable pattern which means that after a string is created it cannot be modified. The sensation of modification comes from the construction of new strings from old ones, like s = s.concat("TEST")
where s.concat("TEST")
creates a new string which's reference may or may not be stored back at s
itself.
But Ruby has weird behavior:
original = '|123|123|123|123|' s = original s['123']='TEST' print(s,"\n") s = original s.sub!('123','TEST') print(s,"\n")will output
|TEST|123|123|123| |TEST|TEST|123|123|
You do not know enough- they say,
There are immutable objects in ruby too!. I just have to call the
freeze
method and the object will be immutable. Let's try it.
original = '|123|123|123|123|' original.freeze # <--- NEW GUY s = original s['123']='TEST' print(s,"\n") s = original s.sub!('123','TEST') print(s,"\n")will output
stringtest.rb:4:in `[]=': can't modify frozen string (TypeError) from stringtest.rb:4
That is just plain wonderful. We are still on the same place: nowhere. What would be the solution? Nothing sane is available. You have to use s = String.new(original)
instead of simple assignment. This is a terrible looking pain in the ass solution.
Who the hell knows where was my string declared at the first time? Who knows what will be broken if I change a string I got from somewhere? Who will find the real problem for an error message like File not found: 'TEST'
?
The general rule is that the const
keyword affects its immediate left neighbor. Sic! So the correct syntax for defining a pointer to a constant integer is int const * pInteger
. The history proved that the traditional style is not sufficient, but is still maintained: If the const
keyword is the leftmost in a type specification then it affects its immediate right neighbor.
#include <iostream> class A { public: A(){ std::cout << "Risen #" << (idx=++counter) << std::endl; } ~A(){ std::cout << "Fallen #" << idx << std::endl; } private: int idx; static int counter; }; int A::counter = 0; int main(){ A const first; return 0; }
will output (as expected)
Risen #1 Fallen #1
As we can observe, the destructor of the const-qualified first
variable was called. That extrapolates that the const
qualifier does not protect from calling the destructor on a pointer which is a weird looking thing for the first time.
#include <iostream> class A { /* same as the above */ }; int A::counter = 0; void f(A const * pTest){ delete pTest; } int main(){ A *pFirst = new A, second; f(pFirst); A const third; return 0; }
will compile flawlessly and will produce
Risen #1 Risen #2 Fallen #1 Risen #3 Fallen #3 Fallen #2
Let's make it impossible to call a delete
on pointers to constants! Bad idea. The following example must be possible.
Vector2D const * pVector = new Vector2D(3,2); // ... delete pVector; // <--- This is it.
And the deletion of a pointed value must be delegable to functions. That's why the f()
was able to delete its parameter.
One may feel unsafe at this moment, but it is not really a problem in the industry: almost nobody has the clue that it is even possible, and nobody wants to do that anyways, except when it is the main goal. But then it's not a trap.