|
C++ programming language
|
C++11summary of more useful features of C++11
|
C++ tricks
|
STL code snippets// Vector: vector<string> vec; vec.push_back( name ); // push_back() means append vec.clear(); // Iterating thru a container: vector<string> files; vector<string>::iterator itr; for ( itr = files.begin(); itr != files.end(); ++itr ) std::cout << *itr << endl; // Iterating by subscript using an index (N/A to associative containers): vector<MyClass> vec; for ( int i = 0; i < vec.size(); ++i ) // Building and iterating thru a map and using pairs: map<string,int> mp; mp.insert( pair<string,int>( "a", 1 ) ); mp.insert( pair<string,int>( "b", 2 ) ); mp.insert( pair<string,int>( "c", 3 ) ); map<string,int>::iterator itr; for ( itr = mp.begin(); itr != mp.end(); ++itr ) std::cout << itr->first << " " << itr->second << endl; // To test if a key exists in a map/set: if ( mMap.find(key) != mMap.end() ) // Adding then removing from a container. list<int> l; for ( int i = 0; i < 10; ++i ) l.push_back( i ); // append while ( ! l.empty() ) { // Prints/pops oldest (head) element first, std::cout << l.front() << endl; l.pop_front(); } // If no match. if ( map.find("key") == map.end() ) std::cout << "not found" << endl; // Print a container. list<string> con; copy( con.begin(), con.end(), ostream_iterator<string>(std::cout," ") ); // Copy a container: copy( con1.begin(), con1.end(), con2 ); // WRONG/PITFALL if con2 smaller than con1 con2.clear(); copy( con1.begin(), con1.end(), back_inserter(con2) ); // OK if con2 smaller than con1 // Sorting using a binary predicate: // An alternative (using container container pointers instead // of values) that doesn't need a binary predicate is to define // user's own operator<() which sort() uses by default. bool BinPred( const Class& o1, const Class& o2 ) { return o1.val < o2.val; } vector<Class> vec; sort( vec.begin(), vec.end(), BinPred ); // A function that returns a pair. // Pair is returned by value (like a struct would be, so it isn't dangling). std::pair<float,float> AddMul( float x, float y ) { std::pair<float, float> res; res.first = x + y; res.second = x * y; return res; } // Print a binary number in base 2 using bitset. #include <bitset> bitset<8> val = 0xff; std::cout << val; // Turn string to all upper-case. string s; transform( s.begin(), s.end(), s.end(), toupper ); // Distance between two iterators. distance( itr1, itr2 ) // Getting iterator of an item that was inserted into a map: // (a multimap/multiset differs, adapted from gfx_share.hh) std::pair<typename Map::iterator,bool> insertion; insertion = mMap.insert( std::make_pair( *obj, std::make_pair(copy,1) ) ); itr = insertion.first; // insertion is assumed to succeed (bool not checked) // Replacing/substituting chars of a string. // string::replace() is really an overwrite, not a substitution. char ProperDirChar( char c ) { return c == '\' ? '/' : c; } string ProperDir( const string& dirname ) { string s = dirname; std::transform( s.begin(), s.end(), s.begin(), ProperDirChar ); return s; } |
stream code snippets// Save/restore stream flags. std::ios::fmtflags savedFlags = std::cout.flags(); ... std::cout.flags(savedFlags); // showbase (eg automatically print "0x" prefix for hex, etc). std::cout.setf( std::ios::showbase ); // Set float precision. mStream.setf( ios::fixed, ios::floatfield ); mStream.precision( 20 ); // Writing to an integer to a stream using width/precision. // Note that width/precision is discarded after every call to stream object! std::cout << std::setw(5) << std::setfill('0') << x << ' '; std::cout << std::setw(5) << std::setfill('0') << y << ' '; std::cout << std::setw(5) << std::setfill('0') << z << ' ' << std::endl; // Convert an int to a string stream for purpose // of passing int as a C++ string. int i; ostringstream ss; ss << i; Print( ss.str() ); // Open for reading to test if file is empty. strm.open( fname, ios::in|ios::binary ); if ( strm.good() && strm.is_open() ) { // File exists. Is it empty? strm.seekg(1); char c; strm >> c; // a read is required to trigger EOF, seekg(1) alone won't if ( strm.eof() ) std::cout << "File exists and is empty." << endl; else std::cout << "File exists and contains data." << endl; } else { std::cout << "File doesn't exist." << endl; } // Reopen in R/W mode. strm.close(); strm.clear(); strm.open( fname, ios::in|ios::out|ios::trunc|ios::binary ); strm.seekp(0); |
redirecting streamsTo write a class that redirects a stream to something else, define user's own streambuf class, then construct an ostream with pointer to streambuf object. An example is in Nicolai Josuttis's STL book. A faux-pas is trying to derive from std::ostream. One problem is that passing endl will result in a bad cast exception (g++ 3/4).
http://shekel.jct.ac.il/cc-res/online-doc/libgPP/iostream_28.html //////////////////////////////////////////////////////////////////////////////// /// @brief NopStream is a indirect way to use a disabled C++ stream. /// /// This is a "streambuf" class to serve as basis of an ostream. /// Derived from Nicolai Josuttis's STL book. /// /// @verbatim /// Example: /// NopStreambuf nopStreamBuf; /// std::ostream gLog( &gNopStreambuf ); /// @endverbatim /// class NopStreambuf : public streambuf { PREVENT_COPYING(NopStreambuf) typedef streambuf Parent; public: NopStreambuf( void ) { } ~NopStreambuf() { } protected: virtual int_type overflow( int_type c ) { return ~EOF; } std::streamsize xsputn( const char* buf, std::streamsize n ) { return n; } }; |
defining operator<<()Idea is to overload operator<<() with user-defined type.class Point { public: int x, y; }; ostream& operator<<( ostream& strm, const Point& obj ) { strm << "(" << obj.x << "," << obj.y << ")"; return strm; } |
overloaded operatorsOverloaded operators, except assignment, are inherited. But subtle compiler errors can occur. Let's say there are two related classes that are logically different types, but are structurally equivalent (identical members). class Vertex { public: Vertex& operator+( const Vertex& src ); float x, y, z; }; class WorldVertex : public Vertex { public: }; Now try adding two derived objects: void Draw( const WorldVertex& v ); WorldVertex v1, v2, v3; v3 = v1 + v2; // compile error Draw( v1 + v2 ); // ok Some compilers will give obscure errors, leading one to think that one needs to duplicate all overloaded operator code into every derived class. That wouldn't be ideal.
What's happening in class WorldVertex : public Vertex { public: WorldVertex& operator=( const Vertex& src ) { x = src.x; y = src.y; z = src.z; return *this; } }; Because these classes are structurally equivalent, Derived& operator=( const Base& ) does make sense, as it can simply copy .x, .y, .z. |
C++ pitfalls, traps
|
STL pitfalls, traps
|
iterating generically across containers of directly-stored objects or pointersA problem is when there are two different types of contains, one directly stores objects, other stores pointers. Then a common template function is needed to process both types, but a problem appears when accessing object via iterator: typedef std::vector<Object> Objects; typedef std::vector<Object>* ObjectPtrs; Object* obj = &(*iter); // container that stores objects directly Object* obj = *iter; // container that stores pointers to objects A solution is a wrapper class which gets object as a pointer, allowing template function to be just written in terms of pointers. //////////////////////////////////////////////////////////////////////////////// /// @brief Wrapper class to get an object from an iterator as a pointer. /// template<class OBJECT_DIRECT, class OBJECT_CONTAINER> class IteratorToPtr { public: typedef OBJECT_DIRECT* Pointer; public: static OBJECT_DIRECT* Get( typename OBJECT_CONTAINER::iterator iter ) { return *iter; } }; //////////////////////////////////////////////////////////////////////////////// /// @brief Wrapper class to get an object from an iterator as a pointer. /// template<class OBJECT_DIRECT, class OBJECT_CONTAINER> class IteratorToPtrByAddress { public: typedef OBJECT_DIRECT* Pointer; public: static OBJECT_DIRECT* Get( typename OBJECT_CONTAINER::iterator iter ) { return &(*iter); } }; /******************************************************************************* * *******************************************************************************/ template<class OBJECT, class OBJECT_CONTAINER, class ITERATOR_TO_PTR> OBJECT* FindObject( int val, OBJECT_CONTAINER& objects ) { for ( typename OBJECT_CONTAINER::iterator iter = objects.begin(); iter != objects.end(); ++iter ) { typename ITERATOR_TO_PTR::Pointer object = ITERATOR_TO_PTR::Get( iter ); if ( object->mVal == val ) return object; } return Object::Dummy(); } |
recommended booksRecommend reading books by Nicolai Josuttis and Scott Meyers. |
commentary/opinion[2023/05] summaryOverall, this author still has a positive opinion of C++ and shall continue writing C++. positive opinion
C++ is the best of the practical/pragmatic programming languages.
C++ became really good with the radical improvements of C++11. negative opinionC++ has become way, way, too big. C++23 standard is beyond comprehension. The syntax of C++23 is so complicated, convoluted, incoherent (and broken down, some say) that no programmer can fully trust any compiler to compile every possible syntax combination 100% correctly, hence the stay on the proven subset of C++ rule. (This author does know similar doubts against ALGOL-68 later proved false.) (FORTH compilers were 100% reliable, had ZERO bugs, because of being so tiny.)
"Look at today's situation, people are programming in C++ -- THE WORST DISEASE EVER CREATED! (laughter)
...[C++, Java, C#] They all suffer from their mightiness.
I'm always expecting them to collapse under their own weight."
every mutant of C++ has failedSeveral mutants of C++ were hyped as the programming language that would replace C++. Fools tried to "correct" C++ by creating imitations of C++ but each was much worse than C++. Even Dr. Frankenstein could not have made a worse abomination than the one Sun Microsystems hacked together. Popularity of each C++ mutant among novices spread, until the novices became experienced and frustrated with their limitations, then each C++ mutant was dumped as trash and forgotten. D (dlang) merits consideration, nevertheless. D was integrated into GNU gcc-9 in 2019. Code of its reference compiler is readable and written in itself -- D is written in D. But too much expressiveness of C++ was lost by trying to "improve" syntax of C++. Forex, smart-pointers are impossible in D, because D restricts operator-overloading to arithmetic [2023/06]. stay on the proven subset of C++Because of the complicated syntax of C++23 with all its permutations, no programmer can fully trust any compiler to compile C++23 code 100% correctly. Programmers should avoid the arcane pretzel-logic areas of syntax of C++23, should limit themselves to writing in the proven subset of C++ syntax, never venturing beyond essential improvements of C++11. |