mirror of
https://gitlab.com/fbb-git/cppannotations
synced 2024-09-28 03:20:44 +02:00
wip preparing 12.5.0
This commit is contained in:
parent
5138741bad
commit
f204568cff
15 changed files with 129 additions and 28 deletions
|
@ -1,2 +1,2 @@
|
|||
#define VERSION "12.4.0"
|
||||
#define VERSION "12.5.0"
|
||||
#define YEARS "1994-2023"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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.
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
52
annotations/yo/polymorphism/construct.yo
Normal file
52
annotations/yo/polymorphism/construct.yo
Normal 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).
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue