1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
The class tt(Person)'s constructor so far has not received any
parameters. bf(C++) allows constructors to be defined with or without
parameter lists. The arguments are supplied when an object is defined.
For the class tt(Person) a constructor expecting three strings and a
tt(size_t) might be useful. Representing, respectively, the person's name,
address, phone number and mass. This constructor can be implemented like this
(but see also section ref(MemberInitializers)):
verb( Person::Person(string const &name, string const &address,
string const &phone, size_t mass)
{
d_name = name;
d_address = address;
setPhone(phone);
d_mass = mass;
})
It must of course also be declared in the class interface:
verb( class Person
{
// data members (not altered)
public:
Person(std::string const &name, std::string const &address,
std::string const &phone, size_t mass);
// rest of the class interface (not altered)
};)
Now that this constructor has been declared, the default constructor must
explicitly be declared as well if we still want to be able to construct a
plain tt(Person) object without any specific initial values for its data
members. The class tt(Person) would thus support two constructors, and the
part declaring the constructors now becomes:
verb( class Person
{
// data members
public:
Person();
Person(std::string const &name, std::string const &address,
std::string const &phone, size_t mass);
// additional members
};)
In this case, the default constructor doesn't have to do very much, as it
doesn't have to initialize the tt(string) data members of the tt(Person)
object. As these data members are objects themselves, they are initialized to
empty strings by their own default constructor. However, there is also a
tt(size_t) data member. That member is a variable of a built-in type and such
variabes do not have constructors and so are not initialized automatically.
Therefore, unless the value of the tt(d_mass) data member is explicitly
initialized its value is:
itemization(
it() a em(random) value for local tt(Person) objects;
it() 0 for global and static tt(Person) objects.
)
The 0-value might not be too bad, but normally we don't want a em(random)
value for our data members. So, even the default constructor has a job to do:
initializing the data members which are not initialized to sensible values
automatically. Its implementation can be:
verb( Person::Person()
{
d_mass = 0;
})
Using constructors with and without arguments is illustrated next. The
object tt(karel) is initialized by the constructor defining a non-empty
parameter list while the default constructor is used for the tt(anon)
object. When constructing objects using constructors requiring arguments you
are advised to surround the arguments by curly braces. Parentheses can often
also be used, and sometimes even em(have) to be used (cf. section
ref(VECTOR)), but mindlessly using parentheses instead of curly braces may
easily result in unexpected problems (cf. section ref(AMBIGUITY)). Hence the
advice to prefer curly braces rather than parentheses. Here's the
example showing two constructor-calls:
verb( int main()
{
Person karel{ "Karel", "Rietveldlaan 37", "542 6044", 70 };
Person anon;
})
The two tt(Person) objects are defined when tt(main) starts as they are
em(local) objects, living only for as long as tt(main) is active.
If tt(Person) objects must be definable using other arguments,
corresponding constructors must be added to tt(Person)'s interface. Apart from
overloading class constructors it is also possible to provide constructors
with default argument values. These default arguments must be specified with
the constructor declarations in the class interface, like so:
verb( class Person
{
public:
Person(std::string const &name,
std::string const &address = "--unknown--",
std::string const &phone = "--unknown--",
size_t mass = 0);
};)
Often, constructors use highly similar implementions. This results from
the fact that the constructor's parameters are often defined for convenience:
a constructor not requiring a tt(phone) number but requiring a tt(mass) cannot
be defined using default arguments, since tt(phone) is not the constructor's
last parameter. Consequently a special constructor is required not having
tt(phone) in its parameter list. However, this doesn't necessarily mean that
constructors must duplicate their code, as constructors may call each other
(called em(constructor delegation)). Constructor delegation is illustrated in
section ref(CONSCALL) below.
|