- void displayMessage() const { ... } a question about const being used - 2 Updates
- shared pointer question - 3 Updates
- Switching to C# - 11 Updates
- How to deal with running out of stack space? - 7 Updates
- Now we can write good programs! - 2 Updates
G G <gdotone@gmail.com>: Jun 16 01:04PM -0700 from deitel and deitel C++ how to prrogram 9th edition // Fig. 3.1: fig03_01.cpp // Define class GradeBook with a member function displayMessage() // create a GradeBook object, and call its displayMessage() #include <iostream> using namespace std; // GradeBook class definition class GradeBook I public: // function that displays a welcome message to the GradeBook user void displayMessage() const { cout << "Welcome to the Grade Book!" << ends; } ]; // function main begins program execution int main() { GradeBook myGradeBook; myGradeBook.displayMessage(); } My question is about the const use after displayMessage(). The author notes it should be used to indicate the function will not change the object GradeBook. Could you please explain. i'm trying to understand why it would be necessary, along with when it would be used again in a program. |
Szyk Cech <szykcech@spoko.pl>: Jun 16 10:15PM +0200 > GradeBook. Could you please explain. i'm trying to > understand why it would be necessary, along with > when it would be used again in a program. This const will protect you from accident modify GradeBook object when you implement function GradeBook::displayMessage() It also tell to class users that it will not modify class. It can be usefull for testers or programmers from your team. |
alexo <alelvb@inwind.it>: Jun 16 05:03PM +0200 Hello, trying to play a bit with shared pointers I noticed something I don't understand. In my toy code, here integrally reported, everything seems to work correctly. My intent is to create a first shared pointer, initialize it, make a second shared pointer initialize it to make it point to the first pointer and print the content of the memory location pointed by both to check if they effectively point to the same block of memory. cut here: // --------------------------------------------------------------------- #include <iostream> #include <memory> #include <iomanip> using namespace std; void print(const shared_ptr<double[]> & d, size_t n); int main() { size_t n {5}; shared_ptr<double[]> pdata1 {make_unique<double[]>(n)}; auto pdata2 {pdata1}; for(size_t i = 0; i < n; i++) { pdata1[i] = static_cast<double> (i) / 2; } print(pdata1, n); print(pdata2, n); return 0; } void print(const shared_ptr<double[]> & d, size_t n) { for(size_t i = 0; i < n; i++) { cout << showpoint << fixed << setprecision(1) << d[i] << " "; } cout << endl; } // --------------------------------------------------------------------- I compiled this code with gcc 7.4.0 using the following options: -std=c++17 -pedantic -Wall -Wextra no errors nor warning. Execution shows that the two pointers point to the same block of memory. But there is a but... To initialize the first pointer I used the following instruction: shared_ptr<double[]> pdata1 {make_unique<double[]>(n)}; If I use the function make_shared<> instead, which should be the correct one, I obtain many error messages, as you can see trying to substitute the former instruction with the following: shared_ptr<double[]> pdata1 {make_shared<double[]>(n)}; If instead of an array of doubles I use a single double value... cut here //----------------------------------------------------------------------- #include <iostream> #include <memory> #include <iomanip> using namespace std; int main() { shared_ptr<double> pdata1 {make_shared<double>(2.0)}; auto pdata2 {pdata1}; cout << *pdata1 << endl; cout << *pdata2 << endl; return 0; } ...everything is ok! What could it be the problem with the double[] type? Thank you for your patience and kind attention, alessandro |
Melzzzzz <Melzzzzz@zzzzz.com>: Jun 16 03:25PM > } > ...everything is ok! > What could it be the problem with the double[] type? Probably shared_pointer can't point to array. -- press any key to continue or any other to quit... U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi bili naoruzani. -- Mladen Gogala |
alexo <alelvb@inwind.it>: Jun 16 09:07PM +0200 Il 16/06/19 17:25, Melzzzzz ha scritto: >> ...everything is ok! >> What could it be the problem with the double[] type? > Probably shared_pointer can't point to array. It seems to me a little strange, but this is my opinion. Is there a way to check if the memory is released in the array version? |
nobody@example.org (Scott): Jun 16 01:58AM On Sat, 15 Jun 2019 18:49:52 +0200, Bonita Montero >-declarations. Just right-click at the symbol and click the >option to guide to the definition. >I like C++ but I don't think forward-declarations are an adantage. There are ways. Long ago I wrote an assembler that avoided forward declarations by making two passes over the source. C doesn't do that, for reasons. C does allow implicit declarations. You can call a previously undeclared function, and C will trust that the types you're passing are the types the function's expecting. I think it's a bad idea, and it usually yields warnings suggesting that it's a bad idea. I think any form of implicit typing is a bad idea. |
Keith Thompson <kst-u@mib.org>: Jun 15 07:31PM -0700 nobody@example.org (Scott) writes: [...] > are the types the function's expecting. I think it's a bad idea, and > it usually yields warnings suggesting that it's a bad idea. I think > any form of implicit typing is a bad idea. No, it doesn't. Calling an undeclared function has been a constraint violation since C99 dropped the "implicit int" rule. -- Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst> Will write code for food. void Void(void) { Void(); } /* The recursive call of the void */ |
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 07:28AM +0200 > fn(a,b,c); > that either a definition or declaration of fn has been encountered > prior to this point. There aren't any declarations with .net / Java so there isn't such a problem. |
nobody@example.org (Scott): Jun 16 08:20AM On Sat, 15 Jun 2019 19:31:13 -0700, Keith Thompson <kst-u@mib.org> wrote: >> any form of implicit typing is a bad idea. >No, it doesn't. Calling an undeclared function has been a constraint >violation since C99 dropped the "implicit int" rule. Apparently not a fatal one. gcc 7.4 with -std=c99 warns, but builds it anyway. |
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 10:54AM +0200 > are the types the function's expecting. I think it's a bad idea, and > it usually yields warnings suggesting that it's a bad idea. I think > any form of implicit typing is a bad idea. Missing forward-declarations aren't a kind of implicit typing. |
Keith Thompson <kst-u@mib.org>: Jun 16 02:24AM -0700 >>violation since C99 dropped the "implicit int" rule. > Apparently not a fatal one. gcc 7.4 with -std=c99 warns, but builds it > anyway. As I said, it's a constraint violation. That means that a conforming compiler must issue a diagnostic message. It's not required to be fatal. In fact the only construct that required a fatal error is the "#error" directive. (I personally wish gcc were more strict, but that's a whole 'nother kettle of fish.) The lesson is that warnings from C compilers should be taken *very* seriously. "gcc -std=c99 -pedantic" results in a fatal error. -- Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst> Will write code for food. void Void(void) { Void(); } /* The recursive call of the void */ |
Keith Thompson <kst-u@mib.org>: Jun 16 02:26AM -0700 >> it usually yields warnings suggesting that it's a bad idea. I think >> any form of implicit typing is a bad idea. > Missing forward-declarations aren't a kind of implicit typing. I suppose it depends on what you mean by "implicit typing". In C90, this: int main(void) { some_function(); } was valid, and would implicitly assume that some_function has been defined with no parameters and a return type of int. (And if that assumption were violated, you'd have undefined behavior.) -- Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst> Will write code for food. void Void(void) { Void(); } /* The recursive call of the void */ |
David Brown <david.brown@hesbynett.no>: Jun 16 12:23PM +0200 On 16/06/2019 03:58, Scott wrote: > are the types the function's expecting. I think it's a bad idea, and > it usually yields warnings suggesting that it's a bad idea. I think > any form of implicit typing is a bad idea. It is worth being clear here that implicit declarations don't let you use functions properly before declaring them. When a function is used with an implicit declaration, it is treated as taking an unknown number of unknown type parameters, using default argument promotions, and returning an int. If there is a definition (or prototype declaration) of the function found later in the code, the compiler does not go back and correct the earlier usage. So as you say, relying on implicit declarations is a bad idea. It is also important to note that C++ is a bit more flexible here. When declaring a class, functions declared within the class can refer to members and functions that are declared later in the class. And template code can refer to identifiers declared later on (as long as they are declared before the template is invoked). |
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 12:43PM +0200 > int main(void) { > some_function(); > } In Java / .net that's different because the definition is checked. |
nobody@example.org (Scott): Jun 16 05:03PM On Sun, 16 Jun 2019 02:24:25 -0700, Keith Thompson <kst-u@mib.org> wrote: >The lesson is that warnings from C compilers should be taken *very* >seriously. >"gcc -std=c99 -pedantic" results in a fatal error. Hmm. Which gcc are you using? Mine (7.4.0) still gives a warning with those flags. -pedantic-errors does give an error as I expected. I'm not saying you're wrong, I'm just curious why we're seeing different results with the same experiment. The source snippet I used is this: int main(void) { return 45 != foo("Hello", 2.5, 45); } int foo(char *x, double y, int z) { return z; } |
nobody@example.org (Scott): Jun 16 05:05PM On Sun, 16 Jun 2019 12:43:45 +0200, Bonita Montero >> some_function(); >> } >In Java / .net that's different because the definition is checked. [checks Newsgroups header] [notes presence of C / C++ newsgroups] [notes lack of Java / .net newsgroups] |
David Brown <david.brown@hesbynett.no>: Jun 16 11:55AM +0200 On 15/06/2019 20:34, Alf P. Steinbach wrote: >> True (for C++14 onwards). > `std::greater` was there, providing a total ordering, in the first C++ > standard, C++98. It's necessary for ordered containers of pointers. Yes. (I misread a detail in a reference page - it wasn't added in C++14, merely slightly modified.) > version, the 286, would be employed for segment handling. There were no > segment tables, just a simple arithmetic relationship (namely 16x) > between selector and physical segment start address. The architecture was called "segmented", whether you like the name or not (and I tend to agree with you here). As a more general point, systems can have multiple views of memory and multiple ways to order it, and may not have a complete order for addresses. Logical and physical addresses can have very different mappings. You can have aliasing with different logical addresses mapping to the same physical address. You can have the same logical address mapping to multiple physical addresses in different circumstances. You can have all sorts of combinations. It can certainly be useful to have an ordering on addresses for things like containers - all you need is an ordering, without any requirement for it to be "real" in any sense. But it does not let you get information about sizes of stacks or memories - each object is, in effect, floating "somewhere" unconnected to any other object. |
David Brown <david.brown@hesbynett.no>: Jun 16 12:13PM +0200 On 15/06/2019 20:41, Alf P. Steinbach wrote: >> different from any other code. > In the context of tail recursion a destructor is different, in that it's > invoked implicitly, not explicitly visible in the source code. In the source code, it is invoked implicitly, yes (that's the whole idea of them). But as far as the compiler is concerned, when it translates the source code into internal formats it fills in the destructor call just like any other function invocation. /Then/ it starts to analyse and manipulate the code, looking for optimisations such as tail recursion. Destructors, being hidden in the source, might make it harder for programmers to guess if a particular function is likely to be suitable for tail recursion optimisation - but it makes no difference to the compiler. > Visual C++ stubbornly refused to optimize tail recursion floating point > type results. So if one relies on tail recursion in C++ one should > better know what one is doing, and know one's compilers. Exactly, yes. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 11:35AM +0100 On Sat, 15 Jun 2019 17:22:00 +0200 > may be inlined. If the compiler can arrange the recursive calls with > tail call optimisations, it will - destructors are not any different > from any other code. Yes, but generally it can't do this, because destructors may only be called at the end of the local scope in which the object concerned has its lifetime, and in the reverse order of construction of other objects in that scope. Any observable effect of object destruction, as required by the C++ standard, must occur after the recursive function call. The result of that is that a recursive function call at the end of local scope, which might appear to be in tail position, in fact isn't - the destructors still remain to execute. I cannot see how an optimizer can get around this where the objects concerned are other than trivial (in the C++-standard sense of that word). The proof of the pudding is in the eating: can you actually produce some code where tail call elimination is applied with non-trivial objects in local scope? If you can, a few lines of code should suffice to demonstrate this. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 12:10PM +0100 On Sun, 16 Jun 2019 11:35:40 +0100 Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote: [snip] > code where tail call elimination is applied with non-trivial objects in > local scope? If you can, a few lines of code should suffice to > demonstrate this. The kind of case to which I am referring is this: void recfunc() { std::string s; ... do something ... recfunc(); } You can bring back tail recursion by massaging the code: void recfunc() { { std::string s; ... do something ... } recfunc(); } But in a case where recfunc() takes an argument, you cannot easily use the string to provide an argument for the recursive function, because it is no longer in scope at the time of the recursive call. |
David Brown <david.brown@hesbynett.no>: Jun 16 01:55PM +0200 On 16/06/2019 12:35, Chris Vine wrote: > destructors still remain to execute. I cannot see how an optimizer can > get around this where the objects concerned are other than trivial (in > the C++-standard sense of that word). Of course a compiler can't do magic - if the operation of the destructor is too complicated to re-arrange to get tail recursion optimisation, then you don't get the optimisation. My point - and I hope this is the last time I have to write the same thing here - is that destructors are not special. They are just like any other function invocation. If they are simple enough, or if the compiler can figure out how to re-arrange things - then they don't hinder the optimisation. (And many destructors /are/ simple.) Destructors don't have to be called at the end of the scope or in reverse order of construction - but their observable effects must occur exactly as if they had been called in that place and order. Just like any other function call. Destructors that do complex things, like deallocate memory and resources, make it unlikely that a compiler can do tail recursive optimisations. But it is nothing special about destructors - it is the same as any other code. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Jun 16 05:00PM +0200 On 16.06.2019 13:10, Chris Vine wrote: > But in a case where recfunc() takes an argument, you cannot easily use > the string to provide an argument for the recursive function, because > it is no longer in scope at the time of the recursive call. The following shows what the compiler would have to do. I am not sure if it's /allowed/ to do such a rewrite. (To make this compile without the referred library, if that's desired, just #include the necessary standard library headers and replace `$use_std` with `using`-declarations.) ---------------------------------------------------------------------- #include <cppx-core/all.hpp> // <url: https://github.com/alf-p-steinbach/cppx-core> namespace my{ $use_std( clog, endl, ostringstream, setw, stack, string ); class Logger { static inline int level = 0; string m_indent; string m_funcname; string m_arglist; public: ~Logger() { --level; clog << m_indent << "< " << m_funcname << m_arglist << endl; } template< class... Args > Logger( const string& funcname, const Args&... args ): m_indent( 4*level, ' ' ), m_funcname( funcname ) { if constexpr( sizeof...( args ) > 0 ) { ostringstream stream; stream << "("; auto output = [&stream, n = 0]( auto v ) mutable -> int { if( n > 0 ) { stream << ", "; } stream << v; ++n; return 0; }; const int a[] = { output( args )... }; (void)a; stream << ")"; m_arglist = stream.str(); } clog << m_indent << "> " << m_funcname << m_arglist << endl; ++level; } }; auto recursive_gcd( const int a, const int b ) -> int { const Logger logging( __func__, a, b ); const int r = a % b; if( r == 0 ) { return b; } return recursive_gcd( b, r ); } auto tail_optimized_gcd( int a, int b ) -> int { stack<Logger> logging_stack; for( ;; ) { logging_stack.emplace( __func__, a, b ); const int r = a % b; // const int r = a % b; if( r == 0 ) // if( r == 0 ) { return b; } { while( not logging_stack.empty() ) { logging_stack.pop(); } return b; } a = b; b = r; // return recursive_gcd( b, r ); } } } // namespace my namespace app{ $use_std( cout, endl ); void run() { cout << "Recursive:" << endl; const int r_gcd = my::recursive_gcd( 60, 16 ); cout << "recursive_gcd(60, 16) = " << r_gcd << endl; cout << endl; cout << "Tail-optimized:" << endl; const int to_gcd = my::tail_optimized_gcd( 60, 16 ); cout << "tail_optimized_gcd(60, 16) = " << to_gcd << endl; } } // namespace app auto main() -> int { app::run(); } ---------------------------------------------------------------------- Output: Recursive: > recursive_gcd(60, 16) > recursive_gcd(16, 12) > recursive_gcd(12, 4) < recursive_gcd(12, 4) < recursive_gcd(16, 12) < recursive_gcd(60, 16) recursive_gcd(60, 16) = 4 Tail-optimized: > tail_optimized_gcd(60, 16) > tail_optimized_gcd(16, 12) > tail_optimized_gcd(12, 4) < tail_optimized_gcd(12, 4) < tail_optimized_gcd(16, 12) < tail_optimized_gcd(60, 16) tail_optimized_gcd(60, 16) = 4 Irrelevant to the question of tail optimization, but I was surprised that one had to manually clear the stack to get LIFO destruction. Hm! Cheers!, - Alf |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 04:08PM +0100 On Sun, 16 Jun 2019 13:55:59 +0200 > then you don't get the optimisation. My point - and I hope this is the > last time I have to write the same thing here - is that destructors are > not special. I never said they were special. I said that in C++ they generally remove the ability of the compiler to perform tail call elimination where non-trivial objects are in local scope when the recursive call is made, because that call is (contrary to appearances) not in tail position. I was right. Insofar as you are saying otherwise, I think you are wrong. I hope this is the last time I have to write the same thing here. But you now state that you are saying something else. Me: "Cooked apples are best served with custard." You: "No, pears are best served with cream." > > code where tail call elimination is applied with non-trivial objects in > > local scope? If you can, a few lines of code should suffice to > > demonstrate this. To continue the culinary analogy, I still await some code, if you say the case exists. (But I think you are now saying that you agree after all with the point I made three posts up-thread.) |
Szyk Cech <szykcech@spoko.pl>: Jun 16 11:38AM +0200 Hi! I think my post scare him/them enough: snews://newshosting.com:563/mTxKE.510937$2A5.222361@fx10.fr7 To avoid more posts like this they decite to wipe out him from this group. I think... > cout << endl; > } > <======================================================================> There is a litle bug: last word will be end with " " (space) instead "." dot. best regards Szyk Cech |
rick.c.hodgin@gmail.com: Jun 16 03:46AM -0700 On Sunday, June 16, 2019 at 5:38:28 AM UTC-4, Szyk Cech wrote: > Hi! > I think my post scare him/them enough: Your posts do not scare me. I do fear for you though. Your blindness and arrogance and pride harm you greatly. > To avoid more posts like this they decite to wipe out him from this > group. I think... God is getting ready to move in this world. You've all been taught the message. You've heard the truth. You've either received or rejected it. Look at the changes in our society. The whole world has turnef against Jesus Christ. Time is almost up fir this age of the gentiles. Next up is the rapture, and the seven year Tribulation begins. -- Rick C. Hodgin |
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