wip preparing 12.5.0

This commit is contained in:
Frank B. Brokken 2023-11-06 13:44:33 +01:00
parent 5138741bad
commit f204568cff
15 changed files with 129 additions and 28 deletions

View file

@ -1,2 +1,2 @@
#define VERSION "12.4.0"
#define VERSION "12.5.0"
#define YEARS "1994-2023"

View file

@ -1,3 +1,9 @@
C++-annotations (12.5.0)
* Added a subsection about constructors of polymorphic classes
* Various cosmetics and typo corrections.
C++-annotations (12.4.0)
* Added the multi-index index operator, available since c++-23.

View file

@ -102,11 +102,10 @@ ref(INHERITANCE)).
the users of objects to modify the internal data of the objects. By
convention, manipulators start with tt(set). E.g., tt(setName).
it() With em(accessors), a tt(get)-prefix is still frequently encountered,
e.g., tt(getName). However, following the conventions promoted by the bi(Qt)
(see ti(http://www.trolltech.com))
emi(Graphical User Interface Toolkit), the tt(get)-prefix is now
deprecated. So, rather than defining the member tt(getAddress), it should
simply be named tt(address).
e.g., tt(getName). However, following the conventions promoted by bi(Qt) (see
ti(https://doc.qt.io), e.g., its classes tt(QByteArray) and
tt(QString)), using the tt(get)-prefix is deprecated. So, rather than defining
a member tt(getAddress), it should simply be named tt(address).
it() Normally (exceptions exist) the public member functions of a class
are listed first, immediately following the class's data members. They are the
important elements of the interface as they define the features the class is

View file

@ -15,9 +15,7 @@ size from 32 bits to 64 bits; integers of type tt(int) remain at their size of
32 bits. This may cause data truncation when assigning pointer or tt(long)
types to tt(int) types. Also, problems with sign extension can occur when
assigning expressions using types shorter than the size of an tt(int) to an
tt(unsigned long) or to a pointer. More information about this issue can be
found
url(here)(http://developers.sun.com/solaris/articles/ILP32toLP64Issues.html).
tt(unsigned long) or to a pointer.
Except for these built-in types the class-type tt(string) is available
for handling character strings. The datatypes tt(bool), and tt(wchar_t) are

View file

@ -1,6 +1,6 @@
The standard tt(for) repetition statements start with an optional
initialization clause. The initialization clause allows us to localize
variables to the scope of the for statements. Initialization clauses van also
variables to the scope of the for statements. Initialization clauses can also
be used in selection statements.
Consider the situation where an action should be performed if the next line

View file

@ -2,8 +2,8 @@ The C2a standard added the em(three-way comparison) operator tt(<=>), also
known as the em(spaceship operator), to bf(C++). In bf(C++) operators can be
defined for class-types, among which equality and comparison operators (the
familiar set of tt(==, !=, <, <=, >) and tt(>=) operators). To provide
classes with all comparison operators merely the the equality and the
spaceship operator need to be defined.
classes with all comparison operators merely the equality and the spaceship
operator need to be defined.
Its priority hi(<=>: priority) is less than the priorities of the bit-shift
operators tt(<<) and tt(>>) and larger than the priorities of the ordering

View file

@ -33,5 +33,33 @@ of the stream.)
It is OK to hi(seek beyond file boundaries) seek beyond the last file
position. Seeking before tt(ios::beg) raises the hi(failbit)tt(ios::failbit)
flag.
Calling tt(seekg) clears the tt(istream's ios::failbit), but not its
tt(ios::badbit) or tt(ios::badbit). To ensure that the stream's state is reset
to `good' its member tt(clear) should be called.
To illustrate: in the following example tt(cin's ios::eofbit) is
set. Following tt(seekg) that bit is cleared, but its tt(ios::goodbit) stil
isn't set. Since its tt(goodbit) isn't set, extraction fails following
tt(seekg):
verb( int main() // in 'src.cc'
{
cin.setstate(ios::eofbit | ios::failbit);
cerr << cin.good() << ' ' << cin.eof() << '\n';
cin.seekg(0);
cerr << cin.good() << ' ' << cin.eof() << '\n' <<
(cin.get() == EOF ? "failed" : "OK") << '\n';
cin.clear();
cerr << cin.good() << ' ' << cin.eof() << '\n' <<
(cin.get() == EOF ? "failed" : "OK") << '\n';
}
outputs when called as 'a.out < src.cc':
0 1
0 0
failed
1 0
OK)
)
)

View file

@ -36,5 +36,20 @@ of the stream.)
beyond endOfFile() will pad the intermediate bytes with 0-valued bytes:
i(null-bytes).
Seeking before tt(ios::beg) raises the hi(fail)tt(ios::fail) flag.
Different from tt(seekg) (cf. section ref(ISTREAMPOS)) tt(seekp) does not
clear the stream's tt(ios::eofbit). To reset an tt(ostream's) state to `good',
its tt(clear) member should be called.
)
)

View file

@ -9,7 +9,8 @@ the default constructors and assignment operators cannot always be provided.
These are the rules the compiler applies when deciding what to provide or not
to provide:
itemization(
it() If the copy constructor or the copy assignment operator is declared,
it() If the copy constructor, the copy assignment operator,
or a destructor is declared (even if it's declared using tt(= default),
then the default move constructor and move assignment operator are suppressed;
their use is replaced by the corresponding copy operation (constructor or
assignment operator);

View file

@ -49,7 +49,7 @@ the tt(String's) members can all be set to 0. If there's a default constructor
doing exactly that then assigning zeroes is fine. If tt(d_capacity) is doubled
once tt(d_size == d_capacity) then setting tt(d_capactiy) to 1 and letting
tt(d_string) point to a newly allocated block of raw memory the size of a
tt(string) might be attractive. The source obbject's capacity might even
tt(string) might be attractive. The source object's capacity might even
remain as-is. E.g., when moving tt(std::string) objects the source object's
capacity isn't altered.

View file

@ -20,18 +20,19 @@ like this:
The move constructor (and other move operations!) must realize that the
destructor not only deletes tt(d_string), but also considers tt(d_size). When
tt(d_size) and tt(d_string) are set to 0, the destructor (correctly) won't
delete anything. When the class uses capacity-doubling once tt(d_size ==
d_capacity) then the move constructor may have reset the source object's
members accordingly, as shown in the following implementation of the move
constructor:
delete anything. In addition, when the class uses
capacity-doubling once tt(d_size ==
d_capacity) then the move constructor can still reset the source's
(d_capacity) to 0, since it's em(known) that the tt(tmp) object ceases to
exist following the move-assignment:
verb( Strings::Strings(Strings &&tmp)
:
d_string(tmp.d_string),
d_size(tmp.d_size),
d_capacity(tmp.d_capacity)
{
tmp.d_string = static_cast<string *>(operator new(sizeof(string)));
tmp.d_capacity = 1;
tmp.d_string = 0;
tmp.d_capacity = 0;
tmp.d_size = 0;
})

View file

@ -5,7 +5,7 @@ header file must be included.
Placement tt(new) is passed an existing block of memory into which tt(new)
initializes an object or value. The block of memory should be large enough to
contain the object, but apart from that there are no further requirements. It
is easy to determine how much memory is used by en entity (object or variable)
is easy to determine how much memory is used by an entity (object or variable)
of type tt(Type): the
ti(sizeof) operator returns the number of bytes used by an tt(Type) entity.

View file

@ -3,6 +3,9 @@ includefile(polymorphism/intro)
lsect(virfunc)(Virtual functions)
includefile(polymorphism/function)
subsect(Constructors of polymorhic classes)
includefile(polymorphism/construct)
lsect(VIRTDES)(Virtual destructors)
includefile(polymorphism/destructor)

View file

@ -0,0 +1,52 @@
Although constructors of polymorphic classes may (indirectlly) call virtual
members, that's probably not what you want as constructors of polymorphic
classes don't consider that those members may be overridden by derived
classes. As an opening example: if the class tt(Vehicle) would
define these members:
verb( public:
void Vehicle::prepare()
{
vPrepare();
}
private:
virtual void Vehicle::vPrepare()
{
cout << "Preparing the Vehicle\n";
})
and tt(Car) would override tt(vPrepare):
verb( virtual void Car::vPrepare()
{
cout << "Preparing the Car\n";
})
then tt(Preparing the Car) would be shown by the following code fragment:
verb(Car car{1200};
Vehicle &veh = car;
veh.prepare();)
Maybe a preparation is always required. So why not do it in the base
class's constructor? Thus, the tt(Vehicle's) constructor could be defined as:
verb( Vehicle::Vehicle()
{
prepare();
})
However, the following code fragment shows tt(Preparing the Vehicle),
and em(not) tt(Preparing the Car):
verb(Car car{1200};)
As base classes' constructors do not recognize overridden virtual members
tt(Vehicle's) constructor simply calls its own tt(vPrepare) member instead of
tt(Vehicle::vPrepare).
There is clear logic to base class constructors not recognizing overridden
member functions: polymorphism allows us to tailor the base class's interface
to derived classes. Virtual members exist to realize this tailoring process.
But that's completely different from not being able to call derived classes'
members from base classes' constructors: at that point the derived class
objects haven't yet properly been initialized. When derived class objects are
constructed their base class parts are constructed before the derived class
objects themselves are in a valid state. Therefore, em(if) a base class
constructor would be allowed to call an overridden virtual member then that
member would most likely use data of the derived class, which at that point
haven't properly been initialized yet (often resulting in undefined behavior
like segmentation faults).

View file

@ -109,10 +109,8 @@ expecting base class references or pointers. Using polymorphism existing code
may be reused by derived classes reimplementing the appropriate members of
their base classes. It's about time to uncover how this magic can be realized.
Polymorphism, which is not
the default in bf(C++), solves the problem and allows the author of the
classes to reach its goal. For the curious reader: prefix tt(void hello()) in
the tt(Base) class with the keyword tt(virtual) and recompile. Running the
modified program produces the intended and expected tt(derived hello). Why
this happens is explained next.
Polymorphism, which is not the default in bf(C++), solves the problem and
allows the author of the classes to reach its goal. For the curious reader:
prefix tt(void hello()) in the tt(Base) class with the keyword tt(virtual) and
recompile. Running the modified program produces the intended and expected
tt(derived hello). Why this happens is explained next.