@ -1273,15 +1273,16 @@ Consider a function that manipulates a `Record`, using a `mutex` to avoid race c
// ... no m.unlock() ...
}
Here, we "forgot" to state that the `mutex` should be released, so we don't know if the failure to ensure release of the `mutex` was a bug or a feature. Stating the postcondition would have made it clear:
Here, we "forgot" to state that the `mutex` should be released, so we don't know if the failure to ensure release of the `mutex` was a bug or a feature.
Stating the postcondition would have made it clear:
void manipulate(Record& r) // better: hold the mutex m while and only while manipulating r
void manipulate(Record& r) // postcondition: m is unlocked upon exit
{
m.lock();
// ... no m.unlock() ...
}
The bug is now obvious.
The bug is now obvious (but only to a human reading comments)
Better still, use [RAII](#Rr-raii) to ensure that the postcondition ("the lock must be released") is enforced in code:
@ -5639,6 +5640,51 @@ Like other casts, `dynamic_cast` is overused.
Prefer [static polymorphism](#???) to hierarchy navigation where it is possible (no run-time resolution necessary)
and reasonably convenient.
##### Note
Some people use `dynamic_cast` where a `typeid` would have been more appropriate;
`dynamic_cast` is a general "is kind of" operation for discovering the best interface to an object,
whereas `typeid` is a "give me the exact type of this object" operation to discover the actual type of an object.
The latter is an inherently simpler operation that ought to be faster.
The latter (`typeid`) is easily hand-crafted if necessary (e.g., if working on a system where RTTI is - for some reason - prohibited),
the former (`dynamic_cast`) is far harder to implement correctly in general.
Consider:
struct B {
const char * name {"B"};
virtual const char* id() const { return name; }
// ...
};
struct D : B {
const char * name {"D"};
const char* id() const override { return name; }
// ...
};
void use()
{
B* pb1 = new B;
B* pb2 = new D;
cout <<pb1->id(); // "B"
cout <<pb2->id(); // "D"
if (pb1->id()==pb2->id()) // *pb1 is the same type as *pb2
if (pb2 == "D") { // looks innocent
D* pd = static_cast<D*>(pb1);
// ...
}
// ...
}
The result of `pb2=="D"` is actually implementation defined.
We added it to warn of the dangers of home-brew RTTI.
This code may work as expected for years, just to fail on a new machine or new compiler that does not unify character literals.
If you implement your own RTTI, be careful.
##### Exception
If your implementation provided a really slow `dynamic_cast`, you may have to use a workaround.
@ -10956,8 +11002,14 @@ And in general, implementations must deal with dynamic linking.
template<classT>
virtual bool intersect(T* p); // error: template cannot be virtual
};
##### Note
**Alternative**: ??? double dispatch, visitor, calculate which function to call
We need a rule because people keep asking about this
##### Alternative
Double dispatch, visitors, calculate which function to call
##### Enforcement
@ -12019,6 +12071,30 @@ Use of these casts can violate type safety and cause the program to access a var
derived2* p2 = static_cast<derived2*>(p); // BAD, tries to treat d1 as a derived2, which it is not