C++ for Scientists - Technische Universität Dresden
C++ for Scientists - Technische Universität Dresden C++ for Scientists - Technische Universität Dresden
70 CHAPTER 3. CLASSES class solver { public: solver(int nrows, int ncols) : A(nrows, ncols) {} // ... private: matrix type A; }; Often the matrix (or whatever other object) is already constructed and we do not like to waste the memory for a copy. In this case we will use a reference to the object. A reference must be set in the constructor because this is the only place to declare what it is referring to. The solver shall not modify the matrix, so we write: class solver { public: solver(const matrix type& A) : A(A) {} // ... private: const matrix type& A; }; The code also shows that we can give the constructor arguments the same names as the member variables. After the colon, which A is which? The rule is that names outside the parenthesis refer to members and inside the parenthesis the constructor arguments are hiding the member variables. Some people are confused by this rule and use different names. To what refers A inside {}? To the constructor argument. Only names that does not exist as argument names are interpreted as member variables. In fact, this is a pure scope resolution: the scope of the function — in this case the constructor — is inside the scope of the class and thus the argument names hide the class member names. Let us return to our complex example. So far, we have a constructor allowing us to set the real and the imaginary part. Often only the real part is set and the imaginary is defaulted to 0. class complex { public: complex(double r, double i) : r(r), i(i) {} complex(double r) : r(rnew), i(0) {} // ... }; We can also say that the number is 0 + 0i if no value is given, i.e. if the complex number is default-constructed: complex() : r(0), i(0) {}
3.3. CONSTRUCTORS 71 Advise Define a default constructor for where it is possible although it might not seem necessary when you implement the class. For the complex class, we might think that we do not need a default constructor because we can delay its declaration until we know its value. The absence of a default constructor creates (at least) two problems: • We might need the variable outside the scope in which the values are computed. For instance, if the value depends on some condition and we would declare the (complex) variable in the two branches of if, the variable would not exist after the if. • We build containers of the type, e.g. a matrix of complex values. Then the constructor of the matrix must call constructors of complex for each entry and the default constructor is the most convenient fashion to handle this. For some classes, it might be very difficult to define a default constructor, e.g. when some of the members are references. In those cases, it can be easier to accept the before-mentioned drawbacks instead of building badly designed default constructors. We can combine all three of them with default arguments: class complex { public: complex(double r= 0, double i= 0) : r(r), i(i) {} // ... }; In the previous main function we defined two objects, one a copy of the other. We can write a constructor for this — called copy constructor: class complex { public: complex(const complex& c) : i(c.i), r(c.r) {} // ... }; But we do not have to. C ++ is doing this itself. If we do not define a copy constructor, i.e. a construstor that has one argument and which is a const reference to its type, than the compiler creates this construstor implicitly. This automatically built copies each member variable by calling the variables’ copy constructors and this is exactly what we did. In cases like this where copying all members is precisely what you want for your copy constructor you should use the default for the following reasons: • It is less verbose; • It is less error-prone; • Other people know directly what your copy constructor does without reading your code; and
- Page 20 and 21: 20 CHAPTER 2. C++ BASICS • std::c
- 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 72 and 73: 72 CHAPTER 3. CLASSES • Compilers
- 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
3.3. CONSTRUCTORS 71<br />
Advise<br />
Define a default constructor <strong>for</strong> where it is possible although it might not<br />
seem necessary when you implement the class.<br />
For the complex class, we might think that we do not need a default constructor because we<br />
can delay its declaration until we know its value. The absence of a default constructor creates<br />
(at least) two problems:<br />
• We might need the variable outside the scope in which the values are computed. For<br />
instance, if the value depends on some condition and we would declare the (complex)<br />
variable in the two branches of if, the variable would not exist after the if.<br />
• We build containers of the type, e.g. a matrix of complex values. Then the constructor of<br />
the matrix must call constructors of complex <strong>for</strong> each entry and the default constructor<br />
is the most convenient fashion to handle this.<br />
For some classes, it might be very difficult to define a default constructor, e.g. when some of<br />
the members are references. In those cases, it can be easier to accept the be<strong>for</strong>e-mentioned<br />
drawbacks instead of building badly designed default constructors.<br />
We can combine all three of them with default arguments:<br />
class complex<br />
{<br />
public:<br />
complex(double r= 0, double i= 0) : r(r), i(i) {}<br />
// ...<br />
};<br />
In the previous main function we defined two objects, one a copy of the other. We can write a<br />
constructor <strong>for</strong> this — called copy constructor:<br />
class complex<br />
{<br />
public:<br />
complex(const complex& c) : i(c.i), r(c.r) {}<br />
// ...<br />
};<br />
But we do not have to. C ++ is doing this itself. If we do not define a copy constructor, i.e. a<br />
construstor that has one argument and which is a const reference to its type, than the compiler<br />
creates this construstor implicitly. This automatically built copies each member variable by<br />
calling the variables’ copy constructors and this is exactly what we did. In cases like this where<br />
copying all members is precisely what you want <strong>for</strong> your copy constructor you should use the<br />
default <strong>for</strong> the following reasons:<br />
• It is less verbose;<br />
• It is less error-prone;<br />
• Other people know directly what your copy constructor does without reading your code;<br />
and