C++ for Scientists - Technische Universität Dresden

C++ for Scientists - Technische Universität Dresden C++ for Scientists - Technische Universität Dresden

math.tu.dresden.de
from math.tu.dresden.de More from this publisher
03.12.2012 Views

72 CHAPTER 3. CLASSES • Compilers might find more optimizations. There are cases where the default copy constructor does not work, especially when the class contains pointers. Say we have a simple vector class with a copy constructor: class vector { public: vector(const vector& v) : size(v.size), data(new double[size]) { for (unsigned i= 0; i < size; i++) data[i]= v.data[i]; } // ... private: unsigned size; double ∗data; }; If we omit this copy constructor the compiler would not complain and voluntarily built one for us. We are glad that our program is shorter and sexier but sooner or later we find that it behaves bizarrely. Changing one vector, modifies another one as well and when we observe this strange behavior we have to find the error in our program. This is particularly difficult because there is no error in what we have written but in what we have omitted. Another problem we can observe is that the run-time library will complain that we freed the same memory twice. 4 The reason for this is the way pointers are copied. Only the address is copied and the result is that both pointers point to the same memory. This might be useful in some cases but most of the time it is not, at least in our domain. Some pointer-addicted geeks might see this differently. 3.3.1 Explicit and implicit constructors In C ++ we distinguish implicit and explicit constructors. Implicit constructors enable in addition to object initialization implicit conversions and assignment-like notation for construction. Instead of: complex c1(3.0); we can also write: or complex c1= 3.0; complex c1= pi∗pi/6.0; This notation is for many scientifically educated people more readable. Older compilers might generate more code in initializations using ‘=’ (the object is first created with the default constructor and the value is copied afterwards) while current compiler generate the same code for both notations. 4 This is an error message every programmer experiences at least once in his/her life (or he/she is not doing serious business).

3.3. CONSTRUCTORS 73 The implicit conversion kicks in when one type is needed and another one is given, e.g. a double instead of a complex. Assume we have a function: 5 double inline complex abs(complex c) { return std::sqrt(real(c) ∗ real(c) + imag(c) ∗ imag(c)); } and call this with a double, e.g.: cout ≪ ”|7| = ” ≪ complex abs(7.0) ≪ ’\n’; The constant ‘7.0’ is considered as a double but there is no function ‘complex abs’ for double. There is a function for complex and complex has a constructor that accepts a double. So, the complex value is implicitly built from the double. This can be forbidden by declaring the constructor as ‘explicit’: class complex { public: explicit complex(double nr= 0.0, double i= 0.0) : r(nr), i(i) {} }; Then complex abs would not be called with a double or any other type complex. To call this function with a double we can write an overload for double or construct a complex explicitly in the call: cout ≪ ”|7| = ” ≪ complex abs(complex(7.0)) ≪ ’\n’; The explicit attribute is really important for the vector class. There will be a constructor taken the size of the vector as argument: class vector { public: vector(int n) : my size(n), data(new double[my size]) {} }; A function computing a scalar product will expect two vectors as arguments: double dot(const vector& v, const vector& w) { ... } Calling this function with integer arguments double d= dot(8, 8); will compile. What happened? Two temporary vectors of size 8 are created with the implicit constructor and passed to the function dot. This nonsense can be easily avoided by declaring the constructor explicit. Discussion 3.1 Which constructor shall be explicit is in the end the class designer’s decision. It is pretty obvious in the vector example: no right-minded programmer wants the compiler converting integers automatically into vectors. Whether the constructor of the complex class should be explicit depends on the expected utilization. Since a complex number with a zero imaginary part is mathematically identical with 5 The definitions of real and imag will be given soon.

72 CHAPTER 3. CLASSES<br />

• Compilers might find more optimizations.<br />

There are cases where the default copy constructor does not work, especially when the class<br />

contains pointers. Say we have a simple vector class with a copy constructor:<br />

class vector<br />

{<br />

public:<br />

vector(const vector& v)<br />

: size(v.size), data(new double[size])<br />

{<br />

<strong>for</strong> (unsigned i= 0; i < size; i++)<br />

data[i]= v.data[i];<br />

}<br />

// ...<br />

private:<br />

unsigned size;<br />

double ∗data;<br />

};<br />

If we omit this copy constructor the compiler would not complain and voluntarily built one<br />

<strong>for</strong> us. We are glad that our program is shorter and sexier but sooner or later we find that it<br />

behaves bizarrely. Changing one vector, modifies another one as well and when we observe this<br />

strange behavior we have to find the error in our program. This is particularly difficult because<br />

there is no error in what we have written but in what we have omitted.<br />

Another problem we can observe is that the run-time library will complain that we freed the<br />

same memory twice. 4 The reason <strong>for</strong> this is the way pointers are copied. Only the address is<br />

copied and the result is that both pointers point to the same memory. This might be useful in<br />

some cases but most of the time it is not, at least in our domain. Some pointer-addicted geeks<br />

might see this differently.<br />

3.3.1 Explicit and implicit constructors<br />

In C ++ we distinguish implicit and explicit constructors. Implicit constructors enable in addition<br />

to object initialization implicit conversions and assignment-like notation <strong>for</strong> construction.<br />

Instead of:<br />

complex c1(3.0);<br />

we can also write:<br />

or<br />

complex c1= 3.0;<br />

complex c1= pi∗pi/6.0;<br />

This notation is <strong>for</strong> many scientifically educated people more readable. Older compilers might<br />

generate more code in initializations using ‘=’ (the object is first created with the default<br />

constructor and the value is copied afterwards) while current compiler generate the same code<br />

<strong>for</strong> both notations.<br />

4 This is an error message every programmer experiences at least once in his/her life (or he/she is not doing<br />

serious business).

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!