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

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

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

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

Saved successfully!

Ooh no, something went wrong!