330 likes | 451 Views
Review of C/C++ Concepts Arrays, Vectors, Iterators, & Lambdas. Operating Systems. Arrays of Primitive Types. Arrays of primitive data types are similar in philosophy to Java arrays Y ou don ’ t have to new static arrays. Just define and start using them.
E N D
Review of C/C++ ConceptsArrays, Vectors, Iterators, & Lambdas Operating Systems
Arrays of Primitive Types • Arrays of primitive data types are similar in philosophy to Java arrays • You don’t have to newstatic arrays. • Just define and start using them. • They are zero-index based just like in Java int value[10]; /* Array of 10 integers */ int counter = 0; for (counter = 0; (counter < 10); counter++) { value[counter] = counter * counter; }
Initializing Arrays • Arrays can be initialized by specifying an initializer list: int value[] = {1, 2, 3, 4, 5}; /* Array of 5 integers */ • A C-string is nothing but an array of characters! • However, an implicit ‘\0’(ASCII code zero) character is inserted at the end of the string. Therefore the following string actually occupies 6 bytes of memory! char name[] = “raodm”;
Strings • C language does not natively support strings • Strings are implemented as an ‘\0’ (ASCII 0) terminated, partially filled array of characters! • Example: char myString[10] = “Hello!\n”; • The trailing ‘\0’ is added when “” (double quotes) are used. • At al other times you have to explicitly add ‘\0’ to terminate a string • This is a common aspect that most novice C programmers miss out. • It is important to note as some of the OS API methods returns C strings Index positions in myString
Simple C-String Manipulation /* Program to print a given name in reverse. */ #include<stdio.h> char name[] = "Some Name"; int main() { // First count number of characters by searching for // the trailing '\0' in the array of characters int length; for(length = 0; (name[length] != '\0'); length++); // Now using length print characters in reverse while(length >= 0) { printf("%c", name[length]); length--; } printf("\n"); return 0; } Output from this program: emaN emoS
C to C++ Strings • Working with C strings can be cumbersome • It is convenient to convert C strings to C++ std::string • Some OS API may need C strings to operate • Convert C++ std::string to C strings when needed using c_str() method. #include<iostream> #include<string> int main() { charcString[] = "Testing"; std::string s1(cString); std::string s2 = cString; return 0; }
Static 2-D arrays • C provides a built-in syntax for 2-D arrays of various data types • Including user defined data types • 2-D arrays are defined and manipulated in a similar fashion as Java int matrix[2][3] = {{20, 30, 40}, {11, 12, 13}}; Conceptual Layout of matrix matrix[0][0] matrix[1][0] matrix[1][2]
The std::array class • The std::array class is preferred with C++ • With C++ try and avoid using vanilla arrays • The std::array class provides more safety than native arrays • It can be used as if it is a native array • The std::array matches the speed of native arrays in most cases #include <iostream> #include <array> intmain() { inti; conststd::array<std::string, 5> words = {{"zero", "one", "two", "three", "four"}}; std::cout << "Enter a number between 0 and 4: "; std::cin >> i; std::cout << words[i] << std::endl; return 0; } First generic parameter is data type of elements and second parameter is number of elements in the array.
std::vector • The C++ Standard Template Library (STL) provides a dynamic array class called std::vector • This is similar to java.util.ArrayList in Java • Elements can be added/removed dynamically • Provides several methods to operate on list of elements • Refer to online documentation for details on the methods: http://www.cplusplus.com/reference/stl/vector/ • Review Chapter 7 of E-textbook C++ How to Program • Typically this is the list-like data structure of choice • Provides forward and backward iterators
std::vector Example #include<iostream> #include<vector> int main() { std::vector<std::string> wordList; std::string word; // Read words from standard input until EOF while ((std::cin >> word)) { wordList.push_back(word); } // Write words in reverse order to standard output for(int i = wordList.size() - 1; (i >= 0); i--) { std::cout << wordList[i] << std::endl; } return 0; } Program Output (user inputs in green): one two three three two one After typing in inputs press Control+D to generate End-Of-File (EOF)
New for-loop with arrays & vectors #include<iostream> #include<array> int main() { // Define local array of 5 strings. std::array<std::string, 5> fiveWords; // Read 5 strings for(auto & word: fiveWords) { std::cin >> word; } for(auto word: fiveWords) { std::cout << word << std::endl; } return0; } The autokeyword lets the compiler automatically determines the data type. It is always good to explicitly specify data type when known.
Hash Map: unordered_map • The std::unordered_mapprovides a default Hash Map for storing <key, value> pairs • Key, Value are templatized and must be specified. • They are stored and returned as std::pair objects • The std::pair::first is the key • The std::pair::second is the value • Provides methods to add or remove <key, value> pairs. • It can be used as an associative array to add/remove <key, value> pairs • Permits custom hash methods to be used • Look up method details via online API at: http://en.cppreference.com/w/cpp/container/unordered_map
std::unordered_map Example #include<unordered_map> #include<iostream> int main() { std::unordered_map<std::string, int> MonthNum = {{"january", 1}, {"february", 2}, {"march", 3}, {"april", 4}, {"december", 12}, {"november", 11}, {"october", 10}, {"may", 5}, {"june", 6}, {"july", 7}, {"september", 9}}; // Instead of auto, the explicit pair is used to illustrate example for(std::pair<conststd::string, int> k: MonthNum) { std::cout << k.first << " => " << k.second << "\n"; } // Avoid using [] to look-up entries because it will // add an empty entry if element does not exist if (MonthNum["august"] == 0) { std::cout << "ERROR: Month number for august == 0!\n"; } // But [] is convenient to update an entry to the hash_map. MonthNum["august"] = 8; // Look up entry in hash_map std::stringmon; std::cout << "Enter a month: "; std::cin >> mon; if (MonthNum.find(mon) == MonthNum.end()) { std::cout << "The month " << mon << " is not valid.\n"; } return 0; } Program Output: july=> 7 june => 6 may => 5 september => 9 october => 10 november => 11 december => 12 april => 4 march => 3 february => 2 january => 1 ERROR: Month number for august == 0! Enter a month: blah The month blah is not valid.
Iterators • In C++, an iterator is an object that points to an element in a range of elements (such as an array or std::vector) and has ability to iterate through the elements • Use ++ and -- operator to iterate to the next or previous value respectively • Use * operator to dereference the iterator and obtain value it is referring to. • Iterators are powerful concepts in C++ and are extensively used in standard libraries • They are not core language constructs • Provide a convenient abstraction • Agnostic to underlying data structure • C++ has a variety of iterators • Random Access iterator: Access any element in the range • Bidirectionaliterator: Can move forward or backward • Forwarditerator: Unidirectional iterator • Inputiterator: Used to read input streams • Outputiterator: Used to write to output streams • Chapter 22 in E-textbook C++ How to Program covers iterators
Iterators with std::vector #include<iostream> #include<vector> #include<iterator> intmain() { std::vector<std::string> wordList; std::string word; // Read words from standard input (until EOF) while ((std::cin >> word)) { wordList.push_back(word); } // Write words in reverse order to standard output for(std::vector<std::string>::reverse_iteratorcurr = wordList.rbegin(); (curr != wordList.rend()); curr++) { std::cout << *curr << std::endl; } return 0; } Program Output (user inputs in green): one two three three two one After typing in inputs press Control+D to generate End-Of-File (EOF) Access value referred by the iterator
Iterators with std::unorded_map #include<unordered_map> #include<iostream> int main() { typedef std::unordered_map<std::string, int> StrIntMap; constStrIntMapMonthNum = {{"january", 1}, {"february", 2}, {"march", 3}, {"april", 4}, {"december", 12}, {"november", 11}, {"october", 10}, {"may", 5}, {"june", 6}, {"july", 7}, {"september", 9}}; // Use iterators and print all elements in map for(StrIntMap::const_iterator it = MonthNum.cbegin(); (it != MonthNum.cend()); it++) { std::cout << it->first << " => " << it->second << "\n"; } return 0; } Program Output : one two three july => 7 june => 6 may => 5 september => 9 october => 10 november => 11 december => 12 april => 4 march => 3 february => 2 january => 1
Algorithms & Iterators • C++ STL includes several standard algorithms that work with iterators • Iterators hide the actual data structure being used from the algorithms • So the same algorithm can be used on various data structures that support appropriate iterators • Different algorithms require different types of iterators • Refer to online documentation on algorithms package for various methods: http://www.cplusplus.com/reference/algorithm/ • Chapter 22 in E-textbook C++ How to Program introduces some methods in the algorithms package
Common methods in algorithms:for_each • The for_each method iterates over a range of values and calls a method with each value. • Algorithm prototype: template<classInputIterator, classFunction> Function for_each(InputIterator first, InputIterator last, Function fn) • Approximate behavior: template<classInputIterator, classFunction> Functionfor_each(InputIterator first, InputIterator last, Function fn) { while (first != last) { fn (*first); // Call function ++first; } return std::move(fn); }
Example:for_each • The for_each method iterates over a range of values and calls a method with each value. #include<iostream> #include<vector> #include<iterator> #include<algorithm> voidprintStr(conststd::string& str) { std::cout << str << std::endl; } int main() { std::vector<std::string> wordList; std::string word; // Read words from standard input while ((std::cin >> word)) { wordList.push_back(word); } // Write words in reverse order to standard output std::for_each(wordList.rbegin(), wordList.rend(), printStr); return 0; } Program Output (user inputs in green): one two three three two one
Common methods in algorithms:copy, unique_copy©_n • The copy,unique_copy, copy_if, and copy_n methods can be used to copy a range of values using iterators • The copy algorithm copies a range of values using iterators template <classInputIterator, classOutputIterator> OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); • The copy_n algorithm copies first nvalues template <classInputIterator, classSize, classOutputIterator> OutputIteratorcopy_n (InputIterator first, Size n, OutputIterator result); • The copy_if algorithm copies values that satisfy a given predicate (function that returns a Boolean value) template <classInputIterator, classOutputIterator, classUnaryPredicate> OutputIteratorcopy_if (InputIterator first, InputIteratorlast, OutputIteratorresult, UnaryPredicatepred); • The unique_copy algorithm requires entries to be sorted and copies unique entries (there is also a version that accepts a predicate for comparisons) template <classInputIterator, classOutputIterator> OutputIteratorunique_copy (InputIterator first, InputIterator last, OutputIterator result);
Example of:copy, unique_copy©_n • The copy,unique_copy and copy_n methods can be used to copy a range of values using iterators • Unique copy requires entries to be sorted and copies unique entries • copy_ncopies first n values #include<iostream> #include<vector> #include<iterator> #include<algorithm> int main() { std::vector<std::string> wordList; std::string word; while((std::cin >> word)) { wordList.push_back(word); } std::vector<std::string> fiveWords(5); // Create 5 empty strings as place holders. std::copy_n(wordList.begin(), 5, fiveWords.begin()); // Now five words has first 5 words from wordList. std::vector<std::string> backup(wordList.size()); // Create empty strings as place holders // Assume worldList is sorted. std::unique_copy(wordList.begin(), wordList.end(), backup.begin()); // NOTE: backup has unique strings & some empty strings return 0; }
Input & Output Iterators with I/O streams • Special input and output iterators are available for use with I/O streams • Eases reading and writing values in data structures • istream_iterator: A unidirectional iterator for reading from an input stream • Data is read using stream extraction operator (operator>>) • ostream_iterator: A unidirectional iterator for writing to an output stream • Data is written using stream insertion operator (operator<<) #include<iostream> #include<vector> #include<iterator> #include<algorithm> int main() { std::istream_iterator<std::string> wordReader(std::cin); std::istream_iterator<std::string> eof; // Dummy iterator for end-of-file (EOF). std::vector<std::string> wordList(wordReader, eof); std::sort(wordList.begin(), wordList.end()); std::copy(wordList.rbegin(), wordList.rend(), std::ostream_iterator<std::string>(std::cout, "\n")); return 0; }
Iterators & Algorithms with Arrays #include<iostream> #include<array> #include<iterator> #include<algorithm> int main() { // Define local array of 5 strings. std::array<std::string, 5> fiveWords; // Read 5 wordsfromstd::cin std::istream_iterator<std::string> wordReader(std::cin); std::copy_n(wordReader, 5, fiveWords.begin()); std::copy(fiveWords.begin(), fiveWords.end(), std::ostream_iterator<std::string>(std::cout, "\n")); return 0; } Refer to the C++ documentation at http://www.cplusplus.com/ for more details on the algorithm package.
Lambdas • A lambda is an anonymous function that you can write inline within another method • Typically used only for small functions • Convenient for use with STL algorithms that accept methods • Syntax (required characters in red): [Capture] (Parameters)->return-type{body} • Capture: The list of variables to be available to the body of the lambda. See next slide for details. • Parameters are list of parameters like any other method • The return-type of the lambda • The body can contain routine C/C++ statements
Examples of Lambdas • A few short examples of lambdas • [](int x, int y) -> int { int z = x + y; return z; } • Simple method that returns sum of parameters • [](int x, int y) { return x + y; } • Implicit return type from 'return' statement • [](int& x) { ++x; } • No return statement. So lambda functions' return type is 'void' • []() { ++global_x; } • no parameters, just accessing a global variable • []{ ++global_x; } • the same as previous example, so () can be omitted
Lambdas with algorithms #include<vector> #include<algorithm> #include<iterator> #include<iostream> int main() { // Read all numbers from a text file std::vector<int> numList(std::istream_iterator <int> (std::cin), std::istream_iterator <int> ()); // sortnumbers in the vector std::sort(numList.begin(), numList.end(), [](int x, int y){ return y < x; }); // Copyonlyevennumbers std::vector<int> evenNumList; std::copy_if(numList.begin(), numList.end(), std::back_inserter(evenNumList), [](int x){return x % 2 == 0; }); // Print "*"s for even numbers std::for_each(evenNumList.begin(), evenNumList.end(), [](int i){std::cout << std::string(i, '*') << std::endl; }); return 0; } Program output (user inputs in green): 5 4 6 1 3 2 ****** **** **
Capture syntax for lambdas • The capture clause in lambdas are used to refer to variables in scope of the lambda • [] : No variables defined in capture. Attempting to use any external variables in the lambda is an error. • [x, &y, …]: x is captured by value, y is captured by reference • [&] : any external variable is implicitly captured by reference if used • [=] : any external variable is implicitly captured by value if used • [&, x, …] : x is explicitly captured by value. Other variables will be captured by reference • [=, &z, …] : z is explicitly captured by reference. Other variables will be captured by value
Stream & File I/O • C++ provides streams for reading and writing data to text files • Most of the file I/O we will do in this course will be text I/O • Streams can also be used for binary I/O • File streams are similar in philosophy and usage as std::cin and std::cout • All the operations on standard streams and file streams have comparable results • Different file streams are provided for different operations • Use std::ifstream for input files • Use std::ostream for output files • Use std::fstream for I/O files
Class hierarchy for I/O streams ios_base ios istream iostream ostream istringstream ifstream ofstream ostringstream cin fstream cout cerr clog
File Stream example:Copy a text file #include<fstream> #include<string> #include<iostream> int main() { std::ifstreaminFile ("input.txt"); if (!inFile.good()) { std::cerr << "Unable to read input.txt\n"; return 1; } std::ofstreamoutFile("output.txt"); std::string line; while (getline(inFile, line)) { outFile << line << std::endl; } inFile.close(); outFile.close(); return 0; }
Iterators with files #include<fstream> #include<vector> #include<algorithm> #include<iterator> #include<iostream> int main() { // Read all numbers from a text file std::ifstreaminFile ("numbers.txt"); std::istream_iterator<int> intReader(inFile); std::vector<int> numList(intReader, std::istream_iterator <int> ()); // sortnumbers in the vector std::sort(numList.begin(), numList.end()); // Write sortednumbers to standard output std::copy(numList.begin(), numList.end(), std::ostream_iterator<int>(std::cout, "\n")); return 0; }
String Streams • C++ provides stream wrappers around strings • The stream wrappers ease reading and writing data to and from strings • std::ostringstream • Write data to a string through a stream • Convenient to convert objects to strings • std::istringstream • Read data from a string • Similar to reading data from std::cin
String Stream Example #include<sstream> #include<iostream> int main() { conststd::stringinputStr = "1 2 3"; std::istringstream reader(inputStr); int i, j; reader >> i >> j; std::ostringstream writer; writer << "i = " << i << " and j = " << j; std::stringoutputStr = writer.str(); std::cout << outputStr << std::endl; return 0; } Program output: i = 1 and j = 2