Вы находитесь на странице: 1из 3

What is a pitfall?

C++ code that

 compiles
 links
 runs
 does something different than you expect

Example:
if (-0.5 <= x <= 0.5) return 0;

Pitfall:
if (-0.5 <= x <= 0.5) return 0;

This expression doesnot test the mathematical condition


-1.5 <= x <= 1.5

Instead, it first computes -0.5 <= x, which is 0 or 1, and then compares the result with 0.5.

Moral: Even though C++ now has a bool type, Booleans are still freely convertible to int.

Since bool->int is allowed as a conversion, the compiler cannot check the validity of expressions.
In contrast, the Java compiler would flag this statement as an error.

Overloading pitfalls

Example:
class Complex
{
public:
Complex(double = 0, double = 0);
Complex operator+(Complex b) const;
Complex operator-(Complex b) const;
Complex operator*(Complex b) const;
Complex operator/(Complex b) const;
Complex operator^(Complex b) const;
// ...
private:
double _re, _im;
};

int main()
{ Complex i(0, 1);
cout << i^2 + 1; // i*i is -1
return 0;
}

Why won't it print (0,0)?

Pitfall:
cout << i^2 + 1;
Using the C/C++ operator precedence rules, we can add parentheses:
cout << (i ^ (2 + 1));

The ^ operator is weaker than + (but stronger than <<).

Moral: You cannot change the operator precedence when overloading operators. Do not overload
an operator if its precedence is not intuitive for the problem domain.

The precedence of ^ is fine for XOR but not for raising to a power.

Pitfalls in automatic type conversion


Because the compiler must choose how to quietly perform a type conversion, it can get into trouble if you don’t design your
conversions correctly. A simple and obvious situation occurs with a class X that can convert itself to an object of class Y with
an operator Y( ). If class Y has a constructor that takes a single argument of type X, this represents the identical type
conversion. The compiler now has two ways to go from X to Y, so it will generate anambiguity error when that conversion
occurs:

//: C12:TypeConversionAmbiguity.cpp
class Orange; // Class declaration

class Apple {
public:
operator Orange() const; // Convert Apple to Orange
};

class Orange {
public:
Orange(Apple); // Convert Apple to Orange
};

void f(Orange) {}

int main() {
Apple a;
//! f(a); // Error: ambiguous conversion
} ///:~

The obvious solution to this problem is not to do it. Just provide a single path for automatic conversion from one type to
another.

A more difficult problem to spot occurs when you provide automatic conversion to more than one type. This is sometimes
called fan-out:

//: C12:TypeConversionFanout.cpp
class Orange {};
class Pear {};

class Apple {
public:
operator Orange() const;
operator Pear() const;
};

// Overloaded eat():
void eat(Orange);
void eat(Pear);

int main() {
Apple c;
//! eat(c);
// Error: Apple -> Orange or Apple -> Pear ???
} ///:~
Class Apple has automatic conversions to both Orange and Pear. The insidious thing about this is that there’s no problem
until someone innocently comes along and creates two overloaded versions of eat( ). (With only one version, the code
in main( ) works fine.)

Again, the solution – and the general watchword with automatic type conversion – is to provide only a single automatic
conversion from one type to another. You can have conversions to other types; they just shouldn’t be automatic. You can create
explicit function calls with names like makeA( ) and makeB( ).

Вам также может понравиться