C++ for Scientists - Technische Universität Dresden
C++ for Scientists - Technische Universität Dresden C++ for Scientists - Technische Universität Dresden
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.
- Page 22 and 23: 22 CHAPTER 2. C++ BASICS In the fir
- Page 24 and 25: 24 CHAPTER 2. C++ BASICS int main (
- Page 26 and 27: 26 CHAPTER 2. C++ BASICS Operator A
- Page 28 and 29: 28 CHAPTER 2. C++ BASICS The bitwis
- Page 30 and 31: 30 CHAPTER 2. C++ BASICS cast (type
- Page 32 and 33: 32 CHAPTER 2. C++ BASICS complicate
- Page 34 and 35: 34 CHAPTER 2. C++ BASICS } else if
- Page 36 and 37: 36 CHAPTER 2. C++ BASICS eps/= 2.0;
- Page 38 and 39: 38 CHAPTER 2. C++ BASICS for (...;
- Page 40 and 41: 40 CHAPTER 2. C++ BASICS 2.6.1 Inli
- Page 42 and 43: 42 CHAPTER 2. C++ BASICS To make su
- Page 44 and 45: 44 CHAPTER 2. C++ BASICS float divi
- Page 46 and 47: 46 CHAPTER 2. C++ BASICS The first
- Page 48 and 49: 48 CHAPTER 2. C++ BASICS #ifndef at
- Page 50 and 51: 50 CHAPTER 2. C++ BASICS float A[7]
- Page 52 and 53: 52 CHAPTER 2. C++ BASICS Encapsulat
- Page 54 and 55: 54 CHAPTER 2. C++ BASICS As a pract
- Page 56 and 57: 56 CHAPTER 2. C++ BASICS A function
- Page 58 and 59: 58 CHAPTER 2. C++ BASICS Now that t
- Page 60 and 61: 60 CHAPTER 2. C++ BASICS we need sm
- Page 62 and 63: 62 CHAPTER 2. C++ BASICS 2.12 Exerc
- Page 64 and 65: 64 CHAPTER 2. C++ BASICS 2.13 Opera
- Page 66 and 67: 66 CHAPTER 3. CLASSES apply symm bl
- Page 68 and 69: 68 CHAPTER 3. CLASSES int main() {
- Page 70 and 71: 70 CHAPTER 3. CLASSES class solver
- Page 74 and 75: 74 CHAPTER 3. CLASSES a real number
- Page 76 and 77: 76 CHAPTER 3. CLASSES } return ∗t
- Page 78 and 79: 78 CHAPTER 3. CLASSES This mechanis
- Page 80 and 81: 80 CHAPTER 3. CLASSES One could not
- Page 82 and 83: 82 CHAPTER 3. CLASSES class matrix
- Page 84 and 85: 84 CHAPTER 3. CLASSES Approach 3: R
- Page 86 and 87: 86 CHAPTER 3. CLASSES of the decrem
- Page 88 and 89: 88 CHAPTER 3. CLASSES There are two
- Page 90 and 91: 90 CHAPTER 4. GENERIC PROGRAMMING }
- Page 92 and 93: 92 CHAPTER 4. GENERIC PROGRAMMING c
- Page 94 and 95: 94 CHAPTER 4. GENERIC PROGRAMMING A
- Page 96 and 97: 96 CHAPTER 4. GENERIC PROGRAMMING v
- Page 98 and 99: 98 CHAPTER 4. GENERIC PROGRAMMING c
- Page 100 and 101: 100 CHAPTER 4. GENERIC PROGRAMMING
- Page 102 and 103: 102 CHAPTER 4. GENERIC PROGRAMMING
- Page 104 and 105: 104 CHAPTER 4. GENERIC PROGRAMMING
- Page 106 and 107: 106 CHAPTER 4. GENERIC PROGRAMMING
- Page 108 and 109: 108 CHAPTER 4. GENERIC PROGRAMMING
- Page 110 and 111: 110 CHAPTER 4. GENERIC PROGRAMMING
- Page 112 and 113: 112 CHAPTER 4. GENERIC PROGRAMMING
- Page 114 and 115: 114 CHAPTER 4. GENERIC PROGRAMMING
- Page 116 and 117: 116 CHAPTER 4. GENERIC PROGRAMMING
- Page 118 and 119: 118 CHAPTER 4. GENERIC PROGRAMMING
- Page 120 and 121: 120 CHAPTER 4. GENERIC PROGRAMMING
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).