- Avoid 'int' and associates. - 10 Updates
- Zeroing in the constructor - 5 Updates
- Strange message from codeblocks IDE about constness of iterator - 2 Updates
- Could you explain this struct BaseConstructor to me? - 5 Updates
- How to understand that there are both "virtual ~Number();" and "Number::~Number()"? - 3 Updates
Paavo Helde <myfirstname@osa.pri.ee>: Jun 14 08:24PM -0600 Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> wrote in > In modern C++ we should avoid using 'int' and its associates such as > 'short' and 'long' as they are all non-portable and unsafe (their size > and value range can differ from one implementation to the next); instead > one should use the typedefs from <cstdint> instead. Sorry, int is extremely portable, every C and C++ implementation has it and it is guaranteed to work in a pretty large range (the actual range can be checked quite easily via std::numeric_limits, BTW). Note that the actually needed range often depends on the application domain requirements and the hardware capabilities, both of which are out of the control of the programmer and can change in time. So, at the time of writing the programmer often does not have sufficient knowledge to select the "correct" fixed-size type. The only exception is when it is known that the numbers are relatively small and the limits do not matter, and this is exactly what the generic int type is meant for. For example, it used to be that the number of pixels in a single medical image (2D or 3D) fits in 32 bits, but this assumption is slowly starting to break. So all those apps who have used e.g. std::uint32_t for the pixel index type will probably need a potentially painful upgrade cycle in the future. Instead of relying on a fixed type, the program should define a typedef for each different usage case of int, which can be upgraded more cleanly and easily when the need arises. The typedef itself can be based on a fixed-size type or then not, this is not so crucial any more. This is not something new, there is a reason why STL contains several such typedefs as e.g. std::string::size_type. Using fixed-size types would not be possible here. Fixed-size types should be directly used only in places where the type sizes are indeed fixed, e.g. when composing a TIFF file IFD in memory or something like that. Cheers Paavo -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
Daniel <danielaparker@gmail.com>: Jun 14 07:01PM -0700 On Sunday, June 14, 2015 at 7:21:28 AM UTC-4, David Brown wrote: > >> a little pedantic, to say the least of it? > > How would you put it less pedantically? > By writing "range" instead of "rage" - Daniel was making a pun on your typo. Ben's follow up was actually pretty good, I was 90 percent certain that he had got my joke. Daniel |
Juha Nieminen <nospam@thanks.invalid>: Jun 15 09:00AM > 'short' and 'long' as they are all non-portable and unsafe (their size > and value range can differ from one implementation to the next); instead > one should use the typedefs from <cstdint> instead. "int" is traditionally the "natural" most efficient integer type of the platform. If you are going to use std::int32_t instead, then in some obscure platforms that might not be the most efficient integral type. (And "obscure" may not even mean "ancient". It could well be that some (current or future) 64-bit system has efficient 64-bit ints and inefficient 32-bit integrals.) It's probably safer to use std::int_least32_t if you want to ensure safety, and the fact that the integral might have more than 32 bits is a non-issue. std::int32_t ought to be used only if you need *exactly* 32 bits (eg. because you are handling some binary format, or require this for memory usage reasons.) In practice 'int' is just fine. If your library/program requires values that are outside the range of a 16-bit ingeter, and you want to make 100% sure that your library won't break, add a static_assert that ensures it has enough bits. --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
gwowen <gwowen@gmail.com>: Jun 15 03:10AM -0700 On Saturday, June 13, 2015 at 7:27:28 PM UTC+1, Mr Flibble wrote: > Using 'int' everywhere is you NOT choosing a type at all; it is just you > not thinking properly about your problem domain so by your own words you > are "idiotic". Nice strawman. Who recommended using 'int' everywhere? > The code you write must be really unsafe; Nope. |
gwowen <gwowen@gmail.com>: Jun 15 03:11AM -0700 On Saturday, June 13, 2015 at 9:24:01 PM UTC+1, Mr Flibble wrote: > > Claimed but not shown. > "their size and value range can differ from one implementation to the next" > /Flibble Wait, weren't you recommending size_t a minute ago? But "their size and value range can differ from one implementation to the next" so size_t must, by this logic (and way I say logic, I am using the word quite wrongly) be unsafe. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 15 11:42AM +0100 On Sat, 13 Jun 2015 17:43:07 +0100 Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk> wrote: [snip] > auto i = 42s32; // signed, 32-bits > or > auto i = 42u32; // unsigned, 32-bits The correct form for this kind of initialization in current C++11/14 if you want auto/template type deduction is: auto i = std::int32_t{42}; auto i = std::uint32_t{42}; which also has the benefit of avoiding implicit narrowing conversions. It seems good enough to me and I cannot see the point of yet more unnecessary C++ syntax. In addition, if you want minimum sizes you would be better off in most cases using std::int_least32_t and std::uint_least32_t, as others have said. Chris |
David Brown <david.brown@hesbynett.no>: Jun 15 07:13AM -0600 On 15/06/15 04:24, Paavo Helde wrote: > Sorry, int is extremely portable, every C and C++ implementation has it > and it is guaranteed to work in a pretty large range (the actual range > can be checked quite easily via std::numeric_limits, BTW). This all comes down to what is meant by "portable". An integer type has several characteristics, such as availability, minimum range, size, etc. For some of these, "int" is highly portable - it is found on any C++ system, and is guaranteed a minimum range of -32767 to +32767. But other characteristics, such as its actual size, lack portability. "int32_t" (and friends) on the other hand do not exist on all systems - but when they /do/ exist, they give you cross-target portability guarantees on size and range that you cannot get with plain "int". If you need some or all of the additional characteristics of int32_t, then int32_t is the most portable way to get that. If you just want a counter or index, with a range up to +/- 32767, then "int" is the most portable choice. We should use the most appropriate type for the task in hand (which could well be "auto") - sometimes it is "int", sometimes it is "int32_t". Personally, I have very little use of "short" or "long", however. Bar consistency with existing code, I can't see any use of "(unsigned) short" that would not be better suited with "(u)int16_t", nor any use of "long" that would not be better with "int32_t" or "int64_t", according to what you actually /need/. And I think "unsigned char" and "signed char" are meaningless monstrosities - plain "char" is good for character data, while "uint8_t" and "int8_t" are appropriate for data bytes and small unsigned or signed numbers. (On platforms without 8-bit chars, (u)int_least8_t is better than (un)signed char.) -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
legalize+jeeves@mail.xmission.com (Richard): Jun 15 02:53PM [Please do not mail me a copy of your followup] gwowen <gwowen@gmail.com> spake the secret code >On Saturday, June 13, 2015 at 7:27:28 PM UTC+1, Mr Flibble wrote: >> Using 'int' everywhere is you NOT choosing a type at all; Nonsense. It is choosing a type: signed integer. >> not thinking properly about your problem domain so by your own words you >> are "idiotic". >Nice strawman. Who recommended using 'int' everywhere? Stroustrup, actually. I'm pretty sure in "The C++ Programming Language" he recommends only using the specifically sized integer types when you are certain that it really *needs* to be that size. An example would be a 2-byte signed integer quantity read from a binary byte stream using std::istream::read. When I've looked at code that is littered with int32_t all over the place, I find it not only ugly but it violates the open/closed principle for design. 99.9999% of the time the 32-bitness of the integer was not essential or important for the code, they simply adopted that simple minded notion that everything should explicitly declare integers of specific sizes. Yes, C/C++ has a language model that more closely maps to the underlying machine, giving it a nice efficient mapping to instructions. But that doesn't mean we should turn all our C/C++ source code into thinly veiled assembly language with everything forced into WORD, DWORD, and QWORD quantities. (The Windows API makes this mistake -- WORD is 16 bits regardless of the actual size of the machine word!) -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
scott@slp53.sl.home (Scott Lurndal): Jun 15 02:58PM >> int32_t i = 42; >Why must one reserve 32bits for 42, if it can be stored in an int which >is, say, 16bit long? Because it doesn't have the const attribute? |
legalize+jeeves@mail.xmission.com (Richard): Jun 15 03:06PM [Please do not mail me a copy of your followup] slp53@pacbell.net spake the secret code >>Why must one reserve 32bits for 42, if it can be stored in an int which >>is, say, 16bit long? >Because it doesn't have the const attribute? Clearly it needs to be stored in uint6_t. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
legalize+jeeves@mail.xmission.com (Richard): Jun 15 03:36AM [Please do not mail me a copy of your followup] Noob <root@127.0.0.1> spake the secret code >> Instead crank up the warning level on your compiler to have it tell you >> which members are uninitialized. >You don't say why the latter is better? At the risk of repeating myself once again: please stop writing C style code in a C++ program. C++ is not C, it is a different language that provides a large amount of interoperability with C code, but it is not C. std::memset is the way that C zeroes a chunk of bytes. If those chunk of bytes happen to be a structure layout for which "initialized" is the same thing as setting all the bytes in the structure to zero, then it can be thought of as "initializing" the structure. (This is often not the case, even in C; members of the structure may need to be set to something other than zero in order to be consistent with the preconditions of the data structure.) C++ uses constructors to guarantee that an object, once constructed, is ready for use and that all the invariants of the object have been established. Constructors don't return error codes. If the invariants can't be established by the constructor, then it is common for the constructor to throw an exception, guaranteeing that you either get a valid object, or nothing. >The current constructor is ~60 lines of code. So? >In my (predominantly C) experience, This isn't C. This is C++. C++ != C. >clearing the entire struct with >a single line communicates the intent more clearly, which results in >less maintenance over time. The reason this is the accepted idiom in C is because C doesn't have constructors and can't establish invariants for an object represented by a struct. Constructors establish that intent explicitly in the language, instead of idiomatically by some sort of agreed upon convention. Constructors reveal the intent of initialization more clearly than C because it is a proper construct in the language. >If we are talking about a plain struct, there are no problems, right? As already mentioned on this thread, you have a class with a non-trivial constructor. It is already not a "plain old data" struct like those in C. See section 8.2.6 Plain Old Data in "The C++ Programming Language", Bjarne Stroustrup, 4th ed., pg. 210: "For example, copying a 100-element array using 100 calls of a copy constructor is unlikely to be as fast as calling std::memcpy(), which typically simply uses a block-move machine instruction. Even if the constructor is inlined, it could be hard for an optimizer to discover this optimization. Such "tricks" are not uncommon, and are important, in implementations of containers, such as vector, and in low-level I/O routines. **They are unnecessary and should be avoided in higher-level code.**" (emphasis added.) >(Although I seem to recall that C++'s "struct" is just a synonym for >"class" with everything public.) However, C++'s struct is not C's struct. >If the class consists only of int fields + accessors, are there >unforeseen drawbacks to zeroing with memset? Yes, C++ provides explicit mechanisms for initialization and you should use those and not C-style mechanisms. Initialization syntax follows the "open/closed principle" of object-oriented design, but std::memset does not. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
Christian Gollwitzer <auriocus@gmx.de>: Jun 15 07:10AM +0200 Am 13.06.15 um 19:33 schrieb Noob: >> value (true for IEEE floats, but...). > I'm not sure what you meant by "discards the type information". > In C too, it is not guaranteed that all-bits-0 pattern is 0.0 or NULL. Yes, exactly. Wouldn't it be much nicer to initialize to a knwon value, e.g. 0.0 or 3.14159... instead of just zero bits, which could be anything, though in practice it's a sensible value for most data types? > 40 methods > }; > Would it still look simple then? Well the syntax would then be something like foo() : aa(0), bb(0), ... { code } simple or not, your choice. But surely that "class" is not idiomatic C++. These odd integer arrays, are Na, Nb big numbers? If so, you'd be better off using a std:vector for memory management. Raw pointers to other structures also look suspicious. Your constructor should probably establish a link to a real object, maybe construct one, and dispose it in the destructor (or decrese the refcount or similar) >> int value = 5; >> }; > Do g++ and Visual Studio support this syntax? They should, depends on the version, since this is a C++11 feature. Should be no problem if you use a current version. > Thanks for the suggestion. > (However, I'm afraid of the code churn, as it would mean > touching all the class definitions.) Your choice, depends on whether it's cheaper to fix the engine using chewing gum and shoelace, or doing it right and spending lots of time to have it running for years. Christian |
Noob <root@127.0.0.1>: Jun 15 08:08AM +0200 On 13/06/2015 11:32, Christian Gollwitzer wrote: > }; > You could do that for all of your fields at the point where they are > declared, so would be easy to spot a missing one. What is the equivalent syntax for 0 initializing an int array? int array[20] = { 0 }; like in C? Also, can I omit the SomeClass(int) constructor if I don't use it? And can I omit the SomeClass() constructor, or does it have to be defined to a NOP? Regards. |
Juha Nieminen <nospam@thanks.invalid>: Jun 15 08:41AM > In my (predominantly C) experience, clearing the entire struct with > a single line communicates the intent more clearly, which results in > less maintenance over time. It may also break the class in the future, if somebody adds a new member to it that requires its own construction (and which would just break if you zero all of its bits). // Current version of your class: class MyClass { int a, b, c, d; double e, f, g; public: MyClass() { std::memset(...); } }; // Future version of your class: class MyClass { int a, b, c, d; double e, f, g; std::string str; // might break, depending on implementation public: MyClass() { std::memset(...); } }; If you can use C++11, it's better to initialize all the members where they are declared (rather than in the constructor). This makes it a lot more obvious if a member is not being initialized. --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
legalize+jeeves@mail.xmission.com (Richard): Jun 15 02:35PM [Please do not mail me a copy of your followup] Noob <root@127.0.0.1> spake the secret code >> declared, so would be easy to spot a missing one. >What is the equivalent syntax for 0 initializing an int array? > int array[20] = { 0 }; Value initialization: <http://en.cppreference.com/w/cpp/language/value_initialization> -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
Paul <pepstein5@gmail.com>: Jun 15 06:28AM -0700 I wrote std::vector<int> vect = {-1, 0, 1, 2, 3, -4, 4}; std::sort(vect.begin(), vect.end()); Using codeblocks, I put my cursor in the word begin and it reads const_iterator std::vector::begin() Surely, this is wrong? vect.begin() is a non-const iterator, isn't it? Or am I missing something? Or is codeblocks just generally unreliable? Thank you very much for your feedback. Paul |
Victor Bazarov <v.bazarov@comcast.invalid>: Jun 15 09:34AM -0400 On 6/15/2015 9:28 AM, Paul wrote: > Surely, this is wrong? vect.begin() is a non-const iterator, isn't > it? Or am I missing something? Or is codeblocks just generally > unreliable? There are two versions of 'vector::begin' member function. Perhaps codeblocks doesn't know which one to show and picks the apparently wrong one... V -- I do not respond to top-posted replies, please don't ask |
fl <rxjwg98@gmail.com>: Jun 14 05:09PM -0700 On Friday, June 12, 2015 at 8:53:53 AM UTC-7, fl wrote: > Number *rep; > short referenceCount; > }; After reading a description about the above code snippet, I find that it is possible helpful on the role of BaseConstructor. The code author try to mimic a pure object-oriented language with C++. But I still cannot understand the intention of BaseConstructor. I feel the code is interesting to learn the technique involved. Could you help me on the role of BaseConstructor? Thanks, ................. Motivation In pure object-oriented languages like Smalltalk, variables are run-time bindings to objects that act like labels. Binding a variable to an object is like sticking a label on it. Assignment in these languages is analogous to peeling a label off one object and putting it on another. On the other hand, in C and C++, variables are synonyms for addresses or offsets instead of being labels for objects. Assignment does not mean re-labelling, it means overwriting old contents with new one. Algebraic Hierarchy idiom uses delegated polymorphism to simulate weak variable to object binding in C++. Algebraic Hierarchy uses Envelope Letter idiom in its implementation. The motivation behind this idiom is to be able write code like the one below. Number n1 = Complex (1, 2); // Label n1 for a complex number Number n2 = Real (10); // Label n2 for a real number Number n3 = n1 + n2; // Result of addition is labelled n3 Number n2 = n3; // Re-labelling |
fl <rxjwg98@gmail.com>: Jun 14 06:48PM -0700 On Friday, June 12, 2015 at 8:53:53 AM UTC-7, fl wrote: > Number *rep; > short referenceCount; > }; My another question is about '(int=0)' inside the struct. I cannot understand it because 'int' is a reserved word. I never see such usage before. Can you explain it to me? Thanks, struct BaseConstructor { BaseConstructor(int=0) {} }; |
Richard Damon <Richard@Damon-Family.org>: Jun 14 10:23PM -0400 On 6/14/15 9:48 PM, fl wrote: > BaseConstructor(int=0) > {} > }; When declaring a function (or a constructor), as opposed to defining it, the declaration is not required to include the variable names of the parameters (as they aren't important). You are also allowed to provide default values for the parameter if it isn't provided. A fuller version of the declaration would be BaseConstructor(int i=0); Because this constructor can be called with no parameters, it can be used as the default constructor, which will act exactly like calling the constuctor with a parameter of 0 (the default value defined) |
Paavo Helde <myfirstname@osa.pri.ee>: Jun 15 02:06AM -0500 fl <rxjwg98@gmail.com> wrote in >> BaseConstructor(int=0) >> {} >> }; [...] >> { >> protected: >> Number (BaseConstructor); [...] > understand the intention of BaseConstructor. I feel the code is > interesting to learn the technique involved. Could you help me on the > role of BaseConstructor? It appears BaseConstructor is only used in a protected constructor, so it probably is meant as some kind of communication detail between derived and base classes. It is not clear from these code examples how it is used. > Number n2 = Real (10); // Label n2 for a real number > Number n3 = n1 + n2; // Result of addition is labelled n3 > Number n2 = n3; // Re-labelling I would guess this passage is more about the rep and referenceCount members. It is interesting to not that here both the "envelope" and "letter" parts are both based on the same class (Number), which seems to make the design much more complicated than needed. It is probably meant for "automagically" allowing code like that: Real x(10); Complex y(1, 2); Number z = x + y; However, then also this is allowed: x = y; This seems quite confusing - is x now Real or Complex? hth Paavo |
Victor Bazarov <v.bazarov@comcast.invalid>: Jun 15 08:29AM -0400 On 6/14/2015 10:23 PM, Richard Damon wrote: > Because this constructor can be called with no parameters, it can be > used as the default constructor, which will act exactly like calling the > constuctor with a parameter of 0 (the default value defined) I think the OP's question is, why have 'BaseConstructor' at all. I am perhaps too dense to try to think for the designer of the code, and so I am not sure I divined the reason for having 'BaseConstructor' and to use it to create a Number instance. I have a sneaky suspicion that it has something to do with possibly initializing Number from an int, and to prohibit such construction, which would be assisted in some way by having a BaseConstructor convertible from an int. But to confirm that one would need to take out BaseConstructor and see the effects, and I am simply too lazy to experiment. V -- I do not respond to top-posted replies, please don't ask |
fl <rxjwg98@gmail.com>: Jun 14 08:31PM -0700 Hi, When I read the example class, I feel puzzled about "Number::~Number()" after there is virtual ~Number(); I remember that the derived class of Number should have a real ~Number(); Now, class Number has both virtual and member function ~Number(). Can you help me on this question? Thanks for the expert keeping answer my questions. //////////////////////////// struct BaseConstructor { BaseConstructor(int=0) {} }; class RealNumber; class Complex; class Number; class Number { friend class RealNumber; friend class Complex; public: Number (); Number & operator = (const Number &n); Number (const Number &n); virtual ~Number(); virtual Number operator + (Number const &n) const; void swap (Number &n) throw (); static Number makeReal (double r); static Number makeComplex (double rpart, double ipart); protected: Number (BaseConstructor); private: void redefine (Number *n); virtual Number complexAdd (Number const &n) const; virtual Number realAdd (Number const &n) const; Number *rep; short referenceCount; }; Number::~Number() { if (rep && --rep->referenceCount == 0) delete rep; } |
legalize+jeeves@mail.xmission.com (Richard): Jun 15 03:52AM [Please do not mail me a copy of your followup] What book is this you're reading so I can make sure *not* to recommend it to anyone? LOL. I find the snippets you're posting to leave a reader new to the language more confused than enlightened and they often seem to exhibit poor practices and conventions. Introducing someone to the language with poor practices and conventions is IMO a really bad way to teach C++. fl <rxjwg98@gmail.com> spake the secret code >I remember that the derived class of Number should have a real > ~Number(); If a class is designed to be used as a base class with virtual methods that provide 'extension points' from the base class, such a base class should have a virtual destructor. The reason for this is that virtual methods are the mechanism by which we obtain run-time polymorphism for a class hierarchy. In such a sitaution, the user of such class is interacting with the class through a pointer to the base class. When an instance of the class is destroyed by operator delete, the virtual destructor ensures that the destructor associated with the actual derived instance of the base is invoked. If the destructor were not virtual, only the base class would be destroyed and the object would not be properly destroyed. >Now, class Number has both virtual and member function >~Number(). It declares the destructor virtual and then defines the destructor. > virtual ~Number(); This is a declaration. > if (rep && --rep->referenceCount == 0) > delete rep; >} This is a definition. When a virtual method is defined outside the class declaration, the 'virtual' keyword is not allowed because only declarations are marked virtual, not definitions. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
Paavo Helde <myfirstname@osa.pri.ee>: Jun 15 01:23AM -0500 fl <rxjwg98@gmail.com> wrote in news:265510a8-466d-4f31-b63a-fb8c1e210cc0 > virtual ~Number(); > I remember that the derived class of Number should have a real > ~Number(); It looks like you are thinking that a virtual function may not have a definition. This is not what 'virtual' means in C++, even pure virtual functions can have definitions. Find a decent C++ tutorial and reread the introduction to virtual member functions. |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment