From ba13a1bf0aa0f3fd4731cb6247ba56568d9c06bf Mon Sep 17 00:00:00 2001 From: George Pimpleton Date: Wed, 31 Jan 2024 10:00:24 -0800 Subject: [PATCH 1/3] Added modules source files for chapter 7 examples and exercises --- Examples/Modules/Chapter 07/Ex7_01.cpp | 23 ++++++ Examples/Modules/Chapter 07/Ex7_01A.cpp | 24 +++++++ Examples/Modules/Chapter 07/Ex7_02.cpp | 37 ++++++++++ Examples/Modules/Chapter 07/Ex7_02A.cpp | 40 +++++++++++ Examples/Modules/Chapter 07/Ex7_03.cpp | 58 +++++++++++++++ Examples/Modules/Chapter 07/Ex7_04.cpp | 15 ++++ Examples/Modules/Chapter 07/Ex7_05.cpp | 28 ++++++++ Examples/Modules/Chapter 07/Ex7_06.cpp | 40 +++++++++++ Examples/Modules/Chapter 07/Ex7_07.cpp | 36 ++++++++++ Exercises/Modules/Chapter 07/Soln7_01.cpp | 72 +++++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_02.cpp | 60 ++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_03.cpp | 55 +++++++++++++++ Exercises/Modules/Chapter 07/Soln7_04.cpp | 46 ++++++++++++ Exercises/Modules/Chapter 07/Soln7_05.cpp | 59 ++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_06.cpp | 58 +++++++++++++++ Exercises/Modules/Chapter 07/Soln7_07.cpp | 48 +++++++++++++ Exercises/Modules/Chapter 07/Soln7_08.cpp | 62 ++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_08A.cpp | 82 ++++++++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_09.cpp | 59 ++++++++++++++++ Exercises/Modules/Chapter 07/Soln7_09A.cpp | 65 +++++++++++++++++ 20 files changed, 967 insertions(+) create mode 100644 Examples/Modules/Chapter 07/Ex7_01.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_01A.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_02.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_02A.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_03.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_04.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_05.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_06.cpp create mode 100644 Examples/Modules/Chapter 07/Ex7_07.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_01.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_02.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_03.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_04.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_05.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_06.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_07.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_08.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_08A.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_09.cpp create mode 100644 Exercises/Modules/Chapter 07/Soln7_09A.cpp diff --git a/Examples/Modules/Chapter 07/Ex7_01.cpp b/Examples/Modules/Chapter 07/Ex7_01.cpp new file mode 100644 index 0000000..f4ce81d --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_01.cpp @@ -0,0 +1,23 @@ +// Concatenating strings + +import ; +import ; + +int main( ) +{ + std::string first; // Stores the first name + std::string second; // Stores the second name + + std::cout << "Enter your first name: "; + std::cin >> first; // Read first name + + std::cout << "Enter your second name: "; + std::cin >> second; // Read second name + + std::string sentence { "Your full name is " }; // Create basic sentence + sentence += first + " " + second + "."; // Augment with names + + std::cout << sentence << std::endl; // Output the sentence + std::cout << "The string contains " // Output its length + << sentence.length( ) << " characters." << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_01A.cpp b/Examples/Modules/Chapter 07/Ex7_01A.cpp new file mode 100644 index 0000000..15e94f0 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_01A.cpp @@ -0,0 +1,24 @@ +// Concatenating characters and strings +// (exactly the same as Ex07_01, except that we use ' ' and '.' instead of " " and ".") + +import ; +import ; + +int main( ) +{ + std::string first; // Stores the first name + std::string second; // Stores the second name + + std::cout << "Enter your first name: "; + std::cin >> first; // Read first name + + std::cout << "Enter your second name: "; + std::cin >> second; // Read second name + + std::string sentence { "Your full name is " }; // Create basic sentence + sentence += first + ' ' + second + '.'; // Augment with names + + std::cout << sentence << std::endl; // Output the sentence + std::cout << "The string contains " // Output its length + << sentence.length( ) << " characters." << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_02.cpp b/Examples/Modules/Chapter 07/Ex7_02.cpp new file mode 100644 index 0000000..5712d2d --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_02.cpp @@ -0,0 +1,37 @@ +// Accessing characters in a string + +import ; +import ; + +#include + +int main( ) +{ + std::string text; // Stores the input + std::cout << "Enter a line of text:\n"; + + std::getline(std::cin, text); // Read a line including spaces + + unsigned vowels { }; // Count of vowels + unsigned consonants { }; // Count of consonants + + for ( size_t i { }; i < text.length( ); ++i ) + { + if ( std::isalpha(text[i]) ) // Check for a letter + { + switch ( std::tolower(text[i]) ) // Convert to lowercase + { + case 'a': case 'e': case 'i': case 'o': case 'u': + ++vowels; + break; + + default: + ++consonants; + break; + } + } + } + + std::cout << "Your input contained " << vowels << " vowels and " + << consonants << " consonants." << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_02A.cpp b/Examples/Modules/Chapter 07/Ex7_02A.cpp new file mode 100644 index 0000000..2954265 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_02A.cpp @@ -0,0 +1,40 @@ +// Accessing characters in a string +// (same as Ex7_02, except that this version uses the more convenient range-based for loop) + +import ; +import ; + +#include // for std::isalpha() and tolower() + +int main( ) +{ + std::string text; // Stores the input + + std::cout << "Enter a line of text:\n"; + std::getline(std::cin, text); // Read a line including spaces + + unsigned vowels { }; // Count of vowels + unsigned consonants { }; // Count of consonants + + for ( const char ch : text ) + { + if ( std::isalpha(ch) ) // Check for a letter + { + switch ( std::tolower(ch) ) // Convert to lowercase + { + case 'a': case 'e': case 'i': case 'o': case 'u': + ++vowels; + break; + + default: + ++consonants; + break; + } + } + } + + std::cout << "Your input contained " + << vowels << " vowels and " + << consonants << " consonants." + << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_03.cpp b/Examples/Modules/Chapter 07/Ex7_03.cpp new file mode 100644 index 0000000..2e7f4d9 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_03.cpp @@ -0,0 +1,58 @@ +// Comparing strings + +import ; // For stream I/O +import ; // For string formatting +import ; // For the string type +import ; // For the vector container + +int main( ) +{ + std::vector names; // Vector of names + std::string input_name; // Stores a name + + for ( ; ; ) // Indefinite loop (stopped using break) + { + std::cout << "Enter a name followed by Enter (leave blank to stop): "; + std::getline(std::cin, input_name); // Read a name and... + + if ( input_name.empty( ) ) break; // ...if it's not empty... + + names.push_back(input_name); // ...add it to the vector + } + + // Sort the names in ascending sequence + bool sorted { }; + do + { + sorted = true; // remains true when names are sorted + for ( size_t i { 1 }; i < names.size( ); ++i ) + { + if ( names[i - 1] > names[i] ) + { // Out of order - so swap names + names[i].swap(names[i - 1]); + sorted = false; + } + } + } + while ( !sorted ); + + // Find the length of the longest name + size_t max_length { }; + + for ( const auto& name : names ) + if ( max_length < name.length( ) ) + max_length = name.length( ); + + // Output the sorted names 5 to a line + const size_t field_width { max_length + 2 }; + size_t count { }; + + std::cout << "In ascending sequence the names you entered are:\n"; + for ( const auto& name : names ) + { + std::cout << std::format("{:>{}}", name, field_width); // Right-align + dynamic width + if ( !(++count % 5) ) std::cout << std::endl; + } + + std::cout << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_04.cpp b/Examples/Modules/Chapter 07/Ex7_04.cpp new file mode 100644 index 0000000..370bb42 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_04.cpp @@ -0,0 +1,15 @@ +// Searching within strings + +import ; +import ; + +int main( ) +{ + std::string sentence { "Manners maketh man" }; + std::string word { "man" }; + + std::cout << sentence.find(word) << std::endl; // Outputs 15 + std::cout << sentence.find("Ma") << std::endl; // Outputs 0 + std::cout << sentence.find('k') << std::endl; // Outputs 10 + std::cout << sentence.find('x') << std::endl; // Outputs std::string::npos +} diff --git a/Examples/Modules/Chapter 07/Ex7_05.cpp b/Examples/Modules/Chapter 07/Ex7_05.cpp new file mode 100644 index 0000000..e10b6fb --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_05.cpp @@ -0,0 +1,28 @@ +// Searching within substrings + +import ; +import ; + +int main( ) +{ + std::string text; // The string to be searched + std::string word; // Substring to be found + + std::cout << "Enter the string to be searched and press Enter:\n"; + std::getline(std::cin, text); + + std::cout << "Enter the string to be found and press Enter:\n"; + std::getline(std::cin, word); + + size_t count { }; // Count of substring occurrences + size_t index { }; // String index + + while ( (index = text.find(word, index)) != std::string::npos ) + { + ++count; + index += word.length( ); // Advance by full word (discards overlapping occurrences) + } + + std::cout << "Your text contained " << count << " occurrences of \"" + << word << "\"." << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_06.cpp b/Examples/Modules/Chapter 07/Ex7_06.cpp new file mode 100644 index 0000000..f5fa5a4 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_06.cpp @@ -0,0 +1,40 @@ +import ; +import ; +import ; +import ; + +int main( ) +{ + std::string text; // The string to be searched + + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + const std::string separators { " ,;:.\"!?'\n" }; // Word delimiters + std::vector words; // Words found + size_t start { text.find_first_not_of(separators) }; // First word start index + + while ( start != std::string::npos ) // Find the words + { + size_t end { text.find_first_of(separators, start + 1) }; // Find end of word + + if ( end == std::string::npos ) // Found a separator? + end = text.length( ); // No, so set to end of text + + words.push_back(text.substr(start, end - start)); // Store the word + start = text.find_first_not_of(separators, end + 1); // Find first character of next word + } + + std::cout << "Your string contains the following " << words.size( ) << " words:\n"; + + size_t count { }; // Number output + + for ( const auto& word : words ) + { + std::cout << std::format("{:15}", word); + + if ( !(++count % 5) ) + std::cout << std::endl; + } + std::cout << std::endl; +} diff --git a/Examples/Modules/Chapter 07/Ex7_07.cpp b/Examples/Modules/Chapter 07/Ex7_07.cpp new file mode 100644 index 0000000..b3292a2 --- /dev/null +++ b/Examples/Modules/Chapter 07/Ex7_07.cpp @@ -0,0 +1,36 @@ +// Replacing words in a string + +import ; +import ; + +int main( ) +{ + std::string text; // The string to be modified + std::cout << "Enter a string terminated by *:\n"; + std::getline(std::cin, text, '*'); + + std::string word; // The word to be replaced + std::cout << "Enter the word to be replaced: "; + std::cin >> word; + + std::string replacement; // The word to be substituted + std::cout << "Enter the string to be substituted for " << word << ": "; + std::cin >> replacement; + + if ( word == replacement ) // Verify there's something to do + { + std::cout << "The word and its replacement are the same.\n" + << "Operation aborted." << std::endl; + return 1; + } + + size_t start { text.find(word) }; // Index of 1st occurrence of word + + while ( start != std::string::npos ) // Find and replace all occurrences + { + text.replace(start, word.length( ), replacement); // Replace word + start = text.find(word, start + replacement.length( )); + } + + std::cout << "\nThe string you entered is now:\n" << text << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_01.cpp b/Exercises/Modules/Chapter 07/Soln7_01.cpp new file mode 100644 index 0000000..e4f760e --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_01.cpp @@ -0,0 +1,72 @@ +// Exercise 7-1 Storing student names and grades. +// This uses a vector of string objects to store the names. + +import ; +import ; +import ; +import ; + +#include + +int main( ) +{ + std::vector names; + std::vector grades; + + size_t max_length { }; // Longest name length + double average_grade { }; // First accumulates the sum of the grades, + + // to be divided by the number of grades later + + // Data entry loop. + // This loop reads the name and grade for each student. + while ( true ) + { + std::cout << "Enter a student's name: "; + std::string name; // Stores a student name + std::getline(std::cin, name); + + names.push_back(name); + + if ( max_length < name.length( ) ) + max_length = name.length( ); + + std::cout << "Enter " << name << "\'s grade: "; + double grade { }; // Stores a student grade + std::cin >> grade; + grades.push_back(grade); + + average_grade += grade; + + std::cout << "Do you wish to enter another student's details (y/n): "; + char answer { }; + std::cin >> answer; + + // Ignore the line break that is still on the input stream after reading the y/n character + // Otherwise the next std::getline() always returns an empty line... + // (Note: we'll try to remember to add this annoyance as a hint in the next edition...) + std::cin.ignore( ); + + if ( std::toupper(answer) == 'N' ) break; + } + + // Calculating the class average. + average_grade /= grades.size( ); + + // Displaying the class average. + std::cout << + std::format("\nThe class average for {} students is {:.2f}\n", names.size( ), average_grade); + + // Displaying the students' names and grades. + const size_t perline { 3 }; + + for ( size_t i {}; i < names.size( ); ++i ) + { + std::cout << std::format("{:<{}} {:>4}\t", names[i], max_length, grades[i]); + + if ( (i + 1) % perline ) continue; + + std::cout << std::endl; + } + std::cout << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_02.cpp b/Exercises/Modules/Chapter 07/Soln7_02.cpp new file mode 100644 index 0000000..24da4d6 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_02.cpp @@ -0,0 +1,60 @@ +// Exercise 7-2 Frequency of words in text. + +import ; +import ; +import ; +import ; + +int main( ) +{ + std::string text; // The text to be searched + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + const std::string separators { " ,;:.\"!?'\n" }; // Word delimiters + std::vector words; // Words found + std::vector counts; // Words counts (same order as words) + + size_t start { text.find_first_not_of(separators) }; // First word start index + while ( start != std::string::npos ) // Find the words + { + size_t end { text.find_first_of(separators, start + 1) }; // Find end of word + if ( end == std::string::npos ) // Found a separator? + end = text.length( ); // No, so set to last + 1 + std::string word { text.substr(start, end - start) }; // Record the word + + // Check for word already in vector + bool is_in { false }; // true when word has been found before + for ( int i {}; i < words.size( ); ++i ) + { + if ( words[i] == word ) + { + ++counts[i]; + is_in = true; + break; + } + } + if ( !is_in ) // If it's a new word... + { + words.push_back(word); // ...store the word... + counts.push_back(1); // ...and record the count + } + start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word + } + + // Find maximum word length + size_t max_length { }; + for ( auto& word : words ) + if ( max_length < word.length( ) ) max_length = word.length( ); + + std::cout << "Your string contains the following " << words.size( ) << " words and counts:\n"; + size_t count { }; // Numbers of words output + const size_t perline { 3 }; // Number per line + for ( size_t i {}; i < words.size( ); ++i ) + { + std::cout << std::format("{:<{}}{:>4} ", words[i], max_length, counts[i]); + if ( !(++count % perline) ) + std::cout << std::endl; + } + std::cout << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_03.cpp b/Exercises/Modules/Chapter 07/Soln7_03.cpp new file mode 100644 index 0000000..4954af1 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_03.cpp @@ -0,0 +1,55 @@ +// Exercise 7-3 Replacing a word in text by asterisks. +// Because we are looking for the word regardless of case, +// the best way is to scan the text character by character. + +import ; +import ; + +#include + +int main( ) +{ + std::string text; // The text to be searched + std::string word; // Stores the word to be replaced + + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + std::cout << "\nEnter the word to be replaced: "; + std::cin >> word; + + std::string lower_word; // Convert word to lower case + for ( char ch : word ) // (we do so once, rather than with every iteration of the loop) + lower_word += std::tolower(ch); + + const std::string separators { " ,;:.\"!?'\n" }; // Word delimiters + + size_t start { text.find_first_not_of(separators) }; // First word start index + while ( start != std::string::npos ) // Find the words + { + auto end { text.find_first_of(separators, start + 1) }; // Find end of word + if ( end == std::string::npos ) // Found a separator? + end = text.length( ); // No, so set to last + 1 + + // Compare the word found with lower_word + if ( end - start == word.length( ) ) + { + bool is_same_word { true }; // Assume it is the word we need to replace + for ( size_t i { start }; i < end; ++i ) + { + if ( lower_word[i - start] != std::tolower(text[i]) ) // If a character differs... + { + is_same_word = false; // ...it is not the word + break; + } + } + if ( is_same_word ) // If it is the word... + { + for ( size_t i { start }; i < end; ++i ) // ... replace by asterisks + text[i] = '*'; + } + } + start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word + } + + std::cout << std::endl << text << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_04.cpp b/Exercises/Modules/Chapter 07/Soln7_04.cpp new file mode 100644 index 0000000..c102daa --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_04.cpp @@ -0,0 +1,46 @@ +// Exercise 7-4 Check for anagrams. +// There's more than one way to do this. +// We chose to delete characters in one word that are common to both. +// If the length of the word that has characters deleted is zero, they are anagrams. + +import ; +import ; + +#include + +int main( ) +{ + std::string word1, word2; + + std::cout << "Enter the first word: "; + std::cin >> word1; + std::cout << "Enter the second word: "; + std::cin >> word2; + + // Test the pathological case of the strings being different lengths + if ( word1.length( ) != word2.length( ) ) + { + std::cout << word1 << " and " << word2 << " are different lengths so they can't be anagrams!" << std::endl; + return 0; + } + + std::string word2_copy { word2 }; // Copy word2 - because we will delete characters + // Loop over all the characters in word1 + for ( char c : word1 ) + { + // Loop over all the characters in word2 (we need the index now, so a range-based for loop won't do) + for ( size_t i { }; i < word2_copy.length( ); ++i ) + { + if ( std::tolower(word2_copy[i]) == std::tolower(c) ) + { + word2_copy.erase(i, 1); // Character found so erase from word2 + break; // Caution: do not use erase(i), because that erases the entire substring starting at index i! + } + } + } + + std::cout << word1 << " and " << word2 << " are " + << (word2_copy.empty( ) ? "" : "not ") + << "anagrams of one another." + << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_05.cpp b/Exercises/Modules/Chapter 07/Soln7_05.cpp new file mode 100644 index 0000000..232f357 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_05.cpp @@ -0,0 +1,59 @@ +// Exercise 7-5 Check for anagrams, ignoring spaces. +// We chose to ignore all non-alphanumeric characters, not just spaces. + +import ; +import ; + +#include + +int main( ) +{ + std::string word1, word2; + + std::cout << "Enter the first word or phrase: "; + std::getline(std::cin, word1); + std::cout << "Enter the second word or phrase: "; + std::getline(std::cin, word2); + + std::string word2_copy { word2 }; // Copy word2 - because we will delete characters + + bool anagrams { true }; // Assume anagrams: becomes false if found otherwise + + // Loop over all the characters in word1 + for ( char c : word1 ) + { + if ( !std::isalnum(c) ) continue; // Ignore characters that aren't alphanumeric. + + bool found { false }; // Becomes true if c is found + // Loop over all the characters in word2 (we need the index now, so a range-based for loop won't do) + for ( size_t i { }; i < word2_copy.length( ); ++i ) + { + if ( std::tolower(word2_copy[i]) == std::tolower(c) ) + { + word2_copy.erase(i, 1); // Character found so erase from word2 + found = true; + break; // Caution: do not use erase(i), because that erases the entire substring starting at index i! + } + } + if ( !found ) + { + anagrams = false; + break; + } + } + + // No alphanumeric characters are allowed to be left in word2_copy anymore at this point + for ( char c : word2_copy ) + { + if ( std::isalnum(c) ) + { + anagrams = false; + break; + } + } + + std::cout << word1 << " and " << word2 << " are " + << (anagrams ? "" : "not ") + << "anagrams of one another." + << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_06.cpp b/Exercises/Modules/Chapter 07/Soln7_06.cpp new file mode 100644 index 0000000..2eade02 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_06.cpp @@ -0,0 +1,58 @@ +// Exercise 7-6 Finding words that begin with a given letter. +// If you wanted the words ordered by the first letter, you could sort the contents of letter first. +// You could also retain all the sets of words for each letter in a separate vector for each set; +// for this you could in other words use a std::vector>. +// Here, we opted to gather the words for one letter and print out the results +// during one iteration in the same loop. + +import ; +import ; +import ; +import ; + +#include + +int main( ) +{ + std::string text; // The text to be searched + std::string letters; + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + std::cout << "\nEnter the starting letters for the words you want to find: "; + std::cin >> letters; + + const std::string separators { " ,;:.\"!?'\n" }; // Word delimiters + std::vector words; // Words found + const size_t perline { 5 }; // Words output per line + size_t count { }; // Number of words found + for ( auto ch : letters ) + { + size_t start { text.find_first_not_of(separators) }; // First word start index + size_t max_length { }; // Maximum word length + while ( start != std::string::npos ) // Find the words + { + auto end { text.find_first_of(separators, start + 1) }; // Find end of word + if ( end == std::string::npos ) // Found a separator? + end = text.length( ); // No, so set to last + 1 + auto word { text.substr(start, end - start) }; // Record the word + if ( std::toupper(word[0]) == std::toupper(ch) ) // If it begins with the current letter... + { + words.push_back(word); // ...save the word + if ( max_length < word.length( ) ) max_length = word.length( ); + } + + start = text.find_first_not_of(separators, end + 1); // Find 1st character of next word + } + // List words for current letter + std::cout << "\nWords beginning with '" << ch << "' are:\n"; + for ( auto& word : words ) + { + std::cout << std::format("{:<{}}", word, max_length + 2); + if ( ++count % perline ) continue; + std::cout << std::endl; + } + std::cout << std::endl; + words.clear( ); + count = 0; + } +} diff --git a/Exercises/Modules/Chapter 07/Soln7_07.cpp b/Exercises/Modules/Chapter 07/Soln7_07.cpp new file mode 100644 index 0000000..aea32fd --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_07.cpp @@ -0,0 +1,48 @@ +// Exercise 7-7 +// Practice string concatenation, looping over strings, and stoi()-like functions + +import ; +import ; + +int main( ) +{ + std::cout << "Enter a sequence of numbers terminated by #:\n"; + + // Read a long string containing any number of integers + std::string numbers; + std::getline(std::cin, numbers, '#'); + + long sum { }; + + size_t index { }; + while ( true ) // An indefinite loop, stopped using a break statement. + { + // Note: this solution does not verify that in between the numbers there's only whitespace. + // In real programs, such input validation may be appropriate... + + // Search for the start of the next (signed!) number + const size_t start { numbers.find_first_of("-0123456789", index) }; + if ( start == std::string::npos ) + break; // Stop once there's no numbers left + + // Search for the end of the number + size_t end { numbers.find_first_not_of("0123456789", start + 1) }; + if ( end == std::string::npos ) + end = numbers.length( ); + + const size_t length { end - start }; // To access substrings, we need the length of the string + const auto substring { numbers.substr(start, length) }; + + // Just in case: skip minus signs not followed by a number (would otherwise crash...) + if ( substring != "-" ) + { + const int number { std::stoi(substring) }; + + sum += number; + } + + index = end; // Advance to the next input (if any) + } + + std::cout << "The sum of the numbers you entered is: " << sum << std::endl; +} diff --git a/Exercises/Modules/Chapter 07/Soln7_08.cpp b/Exercises/Modules/Chapter 07/Soln7_08.cpp new file mode 100644 index 0000000..defe9ec --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_08.cpp @@ -0,0 +1,62 @@ +// Exercise 7-8. Testing whether something is a tautogram. +// +// The loop based on find_first_of/find_first_not_of we use here is only one of many possible solutions. +// See Soln7_08A for an alternate solution that first splits the text into a vector of words. +// +// Our solution in this file more or less assumes you enter only characters +// that are either a letter or whitespace, +// although it does cope with commas or dots that come directly after a word. +// The alternate solution is more robust against non-letter characters. + +import ; +import ; + +#include + +int main( ) +{ + std::string text; // The text to be checked + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + const auto whitespace { " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... + + const size_t first_letter_index { text.find_first_not_of(whitespace) }; + if ( first_letter_index == std::string::npos ) + { + // Is an empty string a tautogram? Let's not go there. + return 0; + } + + const char start_letter { static_cast(std::toupper(text[first_letter_index])) }; + bool tautogram { true }; + + for ( size_t start_current_word { first_letter_index };;) // Use an indefinite loop (see the break; statements) + { + const size_t next_space_index { text.find_first_of(whitespace, start_current_word) }; + if ( next_space_index == std::string::npos ) + { + break; + } + + const size_t next_letter_index { text.find_first_not_of(whitespace, next_space_index) }; + if ( next_letter_index == std::string::npos ) + { + break; + } + + if ( std::toupper(text[next_letter_index]) != start_letter ) + { + tautogram = false; + break; + } + + start_current_word = next_letter_index; + } + + std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; + if ( tautogram ) + { + std::cout << "All words start with the letter " << start_letter << '.'; + } +} diff --git a/Exercises/Modules/Chapter 07/Soln7_08A.cpp b/Exercises/Modules/Chapter 07/Soln7_08A.cpp new file mode 100644 index 0000000..362c419 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_08A.cpp @@ -0,0 +1,82 @@ +// Exercise 7-8. Testing whether something is a tautogram. +// This version uses a totally different technique (first splits the text into words), +// and as a bonus ignores small words, and only requires 70% of the words to start with the same letter. +// With this looser definition of tautogram "The only verdict is vengeance; a vendetta, held as a votive not in vain.", +// not entirely coincidentally, passes as a "near tautogram". + +import ; +import ; +import ; + +#include + +int main( ) +{ + // Ignore small words + const size_t small_word_bound { 3 }; // Set to 0 to not ignore small words at all + const double near_tautology_ratio_bound { 0.7 }; // Set to 1 to always require 100% of the words to begin with the same letter + + std::string text; // The text to be checked + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + std::vector words; + for ( size_t i { }; i < text.length( ); ) + { + // Skip spaces until we find the start of a word... + while ( i < text.length( ) && !std::isalpha(text[i]) ) ++i; + + const size_t start { i }; + + // Search for the end of the word... + while ( i < text.length( ) && std::isalpha(text[i]) ) ++i; + + if ( i > start ) + { + words.push_back(text.substr(start, i - start)); + } + } + + // Only search for tautograms where words start with Latin letters A - Z + size_t counts[26] { }; // All counts are initialized to 0 + size_t long_word_count { }; + + for ( auto& word : words ) + { + if ( word.length( ) > small_word_bound ) + { + ++long_word_count; + const auto start_letter { std::toupper(word[0]) }; + if ( start_letter >= 'A' && start_letter <= 'Z' ) + { + ++counts[start_letter - 'A']; + } + } + } + + // Look for the most common start letter + size_t most_common_index { 0 }; + for ( size_t i { 1 }; i < std::size(counts); ++i ) + { + if ( counts[i] > counts[most_common_index] ) + { + most_common_index = i; + } + } + + const auto most_common_start_letter { static_cast('A' + most_common_index) }; + + if ( counts[most_common_index] == words.size( ) ) + { + std::cout << "The text that you entered is a true tautogram.\n"; + std::cout << "All words start with the letter " << most_common_start_letter << '.'; + } + else if ( static_cast(counts[most_common_index]) / long_word_count >= near_tautology_ratio_bound ) + { + std::cout << "It is a \"near tautogram\", though, as most longer words do start with the letter " << most_common_start_letter << '.'; + } + else + { + std::cout << "The text that you entered is no tautogram. Not even close.\n"; + } +} diff --git a/Exercises/Modules/Chapter 07/Soln7_09.cpp b/Exercises/Modules/Chapter 07/Soln7_09.cpp new file mode 100644 index 0000000..7f7fc8e --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_09.cpp @@ -0,0 +1,59 @@ +// Exercise 7-9. Remove the begin letter of the tautogram (the easy way) + +import ; +import ; + +#include + +int main( ) +{ + std::string text; // The text to be checked + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + const auto whitespace { " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... + + const size_t first_letter_index { text.find_first_not_of(whitespace) }; + if ( first_letter_index == std::string::npos ) + { + // Is an empty string a tautogram? Let's not go there. + return 0; + } + + const char start_letter { static_cast(std::toupper(text[first_letter_index])) }; + bool tautogram { true }; + + for ( size_t start_current_word { first_letter_index };;) // Use an indefinite loop (see the break; statements) + { + const size_t next_space_index { text.find_first_of(whitespace, start_current_word) }; + if ( next_space_index == std::string::npos ) + { + break; + } + + const size_t next_letter_index { text.find_first_not_of(whitespace, next_space_index) }; + if ( next_letter_index == std::string::npos ) + { + break; + } + + if ( std::toupper(text[next_letter_index]) != start_letter ) + { + tautogram = false; + break; + } + + start_current_word = next_letter_index; + } + + std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; + if ( tautogram ) + { + std::cout << "All words start with the letter " << start_letter << ".\n"; + + std::erase(text, start_letter); + std::erase(text, std::tolower(start_letter)); + + std::cout << "After removing this letter, the text becomes as follows:\n" << text << std::endl; + } +} diff --git a/Exercises/Modules/Chapter 07/Soln7_09A.cpp b/Exercises/Modules/Chapter 07/Soln7_09A.cpp new file mode 100644 index 0000000..e0e5b28 --- /dev/null +++ b/Exercises/Modules/Chapter 07/Soln7_09A.cpp @@ -0,0 +1,65 @@ +// Exercise 7-9. Remove the begin letter of the tautogram (the hard way) + +import ; +import ; + +#include + +int main( ) +{ + std::string text; // The text to be checked + std::cout << "Enter some text terminated by *:\n"; + std::getline(std::cin, text, '*'); + + const auto whitespace { " \t\n\r\f\v" }; // Can be solved using std::isspace() as well... + + const size_t first_letter_index { text.find_first_not_of(whitespace) }; + if ( first_letter_index == std::string::npos ) + { + // Is an empty string a tautogram? Let's not go there. + return 0; + } + + const char start_letter { static_cast(std::toupper(text[first_letter_index])) }; + bool tautogram { true }; + + for ( size_t start_current_word { first_letter_index };;) // Use an indefinite loop (see the break; statements) + { + const size_t next_space_index { text.find_first_of(whitespace, start_current_word) }; + if ( next_space_index == std::string::npos ) + { + break; + } + + const size_t next_letter_index { text.find_first_not_of(whitespace, next_space_index) }; + if ( next_letter_index == std::string::npos ) + { + break; + } + + if ( std::toupper(text[next_letter_index]) != start_letter ) + { + tautogram = false; + break; + } + + start_current_word = next_letter_index; + } + + std::cout << "The text that you entered is " << (tautogram ? "" : "not ") << "a tautogram.\n"; + if ( tautogram ) + { + std::cout << "All words start with the letter " << start_letter << ".\n"; + + std::string text_without_start_letter; + for ( char c : text ) + { + if ( std::toupper(c) != start_letter ) + { + text_without_start_letter.push_back(c); + } + } + + std::cout << "After removing this letter, the text becomes as follows:\n" << text_without_start_letter << std::endl; + } +} From 90bf1a260f1ad17bfbd83fa1e81b352e5314d772 Mon Sep 17 00:00:00 2001 From: George Pimpleton Date: Sat, 17 Feb 2024 20:18:40 -0800 Subject: [PATCH 2/3] Code updates --- .../Modules/Chapter 13/Ex13_12/Message.cppm | 24 ++- .../Modules/Chapter 13/Ex13_12A/Message.cppm | 24 +-- .../Modules/Chapter 13/Ex13_12B/Message.cppm | 28 +-- .../Chapter 16/Soln16_06/Soln16_06.cpp | 162 +++++++++--------- 4 files changed, 127 insertions(+), 111 deletions(-) diff --git a/Examples/Modules/Chapter 13/Ex13_12/Message.cppm b/Examples/Modules/Chapter 13/Ex13_12/Message.cppm index d3f88a8..53c3ed0 100644 --- a/Examples/Modules/Chapter 13/Ex13_12/Message.cppm +++ b/Examples/Modules/Chapter 13/Ex13_12/Message.cppm @@ -1,21 +1,27 @@ module; + #include // For std::strlen() and std::strcpy() + +// stop Visual Studio from whinging about strcpy being unsafe +// https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996 +#pragma warning(disable : 4996) + export module message; export class Message { public: - explicit Message(const char* text = "") - : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! - { - std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! - } - ~Message() { delete[] m_text; } + explicit Message(const char* text = "") + : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! + { + std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! + } + ~Message( ) { delete[ ] m_text; } - Message& operator=(const Message& message); // Assignment operator + Message& operator=(const Message& message); // Assignment operator - const char* getText() const { return m_text; } + const char* getText( ) const { return m_text; } private: - char* m_text; + char* m_text; }; diff --git a/Examples/Modules/Chapter 13/Ex13_12A/Message.cppm b/Examples/Modules/Chapter 13/Ex13_12A/Message.cppm index 5bf7427..d81eaea 100644 --- a/Examples/Modules/Chapter 13/Ex13_12A/Message.cppm +++ b/Examples/Modules/Chapter 13/Ex13_12A/Message.cppm @@ -1,22 +1,26 @@ module; + #include // For std::strlen() and std::strcpy() + +#pragma warning(disable : 4996) + export module message; export class Message { public: - explicit Message(const char* text = "") - : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! - { - std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! - } - ~Message() { delete[] m_text; } + explicit Message(const char* text = "") + : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! + { + std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! + } + ~Message( ) { delete[ ] m_text; } - Message(const Message& message); // Copy constructor - Message& operator=(const Message& message); // Copy assignment operator + Message(const Message& message); // Copy constructor + Message& operator=(const Message& message); // Copy assignment operator - const char* getText() const { return m_text; } + const char* getText( ) const { return m_text; } private: - char* m_text; + char* m_text; }; diff --git a/Examples/Modules/Chapter 13/Ex13_12B/Message.cppm b/Examples/Modules/Chapter 13/Ex13_12B/Message.cppm index cbb3e1a..98875ed 100644 --- a/Examples/Modules/Chapter 13/Ex13_12B/Message.cppm +++ b/Examples/Modules/Chapter 13/Ex13_12B/Message.cppm @@ -1,29 +1,33 @@ module; + #include // For std::strlen() and std::strcpy() + export module message; +#pragma warning(disable : 4996) + export class Message { public: - explicit Message(const char* text = "") - : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! - { - std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! - } - ~Message() { delete[] m_text; } + explicit Message(const char* text = "") + : m_text(new char[std::strlen(text) + 1]) // Caution: include the null character! + { + std::strcpy(m_text, text); // Mind the order: strcpy(destination, source)! + } + ~Message( ) { delete[ ] m_text; } - Message(const Message& message); // Copy constructor - Message& operator=(const Message& message); // Copy assignment operator + Message(const Message& message); // Copy constructor + Message& operator=(const Message& message); // Copy assignment operator - void swap(Message& other) noexcept; + void swap(Message& other) noexcept; - const char* getText() const { return m_text; } + const char* getText( ) const { return m_text; } private: - char* m_text; + char* m_text; }; export void swap(Message& one, Message& other) noexcept { - return one.swap(other); + return one.swap(other); } diff --git a/Exercises/Modules/Chapter 16/Soln16_06/Soln16_06.cpp b/Exercises/Modules/Chapter 16/Soln16_06/Soln16_06.cpp index 14ce769..c164939 100644 --- a/Exercises/Modules/Chapter 16/Soln16_06/Soln16_06.cpp +++ b/Exercises/Modules/Chapter 16/Soln16_06/Soln16_06.cpp @@ -1,121 +1,123 @@ // Exercise 16-6 /* Creating RAII classes to manage resource handles returned by a C interface Remember: RAII is not just for dynamic memory: every resource should be managed by an object! - + Leaks in the original code: - - The database connection was not disconnected if an exception occurred indirectly + - The database connection was not disconnected if an exception occurred indirectly in the main try-catch block. For the DatabaseException thrown in the block of db_query() the connection was correctly disconnected. Possible other exceptions, though, include: - a) verifyCustomerFields() discovers a problem. This verification step - may've been added later by someone trying to make the program more robuust, + a) verifyCustomerFields() discovers a problem. This verification step + may've been added later by someone trying to make the program more robuust, but not familiar enough with the surrounding program... - b) std::stoi() throws std::invalid_argument because an empty string was passed, + b) std::stoi() throws std::invalid_argument because an empty string was passed, or a string that does not start with a number c) Customer::toString() throws std::bad_alloc because memory allocation failed for the new string - d) If the output stream used fails for whatever reason, a std::runtime_exception occurs. + d) If the output stream used fails for whatever reason, a std::runtime_exception occurs. While for std::cout this is less likely, perhaps the program is changed later to output to some other stream that writes to a GUI element or a file. These might fail... - The memory leaks for the query result are analogous - - If no customers are found, someone decided to add an extra return statement. + - If no customers are found, someone decided to add an extra return statement. This again leaks all resources... - + Bottom line: the larger the program becomes, the more resources there are to keep track of, and at the same time the more exceptions and return statements there appear. Moreover, in real life often many different people collaborate on the same code, often less familiar with the original program and the resources it uses. - It is just too easy to forget one case, and introduce a leak. + It is just too easy to forget one case, and introduce a leak. Even the most disciplined developer will make mistakes this way---believe us! Hence: always use some form of RAII to manage a resource! */ import ; import ; +import ; // needed for std::to_string() #include "DB.h" + import db.exception; import db.raii; import customer; -void verifyCustomerFields(DB_QUERY_RESULT* result); // Sanity check on the number of fields returned by our query -std::vector readCustomers(DB_QUERY_RESULT* result); // Convert the DB result to a series of C++ objects -void print(std::ostream& stream, const Customer& customer); // Print a given customer to a given output stream +void verifyCustomerFields( DB_QUERY_RESULT* result ); // Sanity check on the number of fields returned by our query +std::vector readCustomers( DB_QUERY_RESULT* result ); // Convert the DB result to a series of C++ objects +void print( std::ostream& stream, const Customer& customer ); // Print a given customer to a given output stream -int main() +int main( ) { - DBConnectionRAII connection{ db_connect() }; - try - { - DBQueryResultRAII result{ db_query(connection, "SELECT * FROM CUSTOMER_TABEL") }; - if (!result) - { - throw DatabaseException{"Query failed"}; - } - - std::vector customers{ readCustomers(result) }; - - if (customers.empty()) - { - std::cerr << "No customers found?" << std::endl; - return 2; - } - - for (auto& customer : customers) - { - print(std::cout, customer); - } - } - catch (const std::exception& caught) - { - std::cerr << caught.what() << std::endl; - return 1; - } + DBConnectionRAII connection { db_connect( ) }; + try + { + DBQueryResultRAII result { db_query( connection, "SELECT * FROM CUSTOMER_TABEL" ) }; + if ( !result ) + { + throw DatabaseException { "Query failed" }; + } + + std::vector customers { readCustomers( result ) }; + + if ( customers.empty( ) ) + { + std::cerr << "No customers found?" << std::endl; + return 2; + } + + for ( auto& customer : customers ) + { + print( std::cout, customer ); + } + } + catch ( const std::exception& caught ) + { + std::cerr << caught.what( ) << std::endl; + return 1; + } } -std::vector readCustomers(DB_QUERY_RESULT* result) +std::vector readCustomers( DB_QUERY_RESULT* result ) { - // Sanity check - // (if the number of fields does not match 5, - // the code below would crash!) - verifyCustomerFields(result); - - std::vector customers; - - auto row{ db_fetch_row(result) }; - while (row) - { - customers.push_back(Customer{ - row[0], // Surname - row[1], // Name - row[2], // Street - std::stoi(row[3]), // Street number - row[4] // City - }); - - row = db_fetch_row(result); - } - - return customers; + // Sanity check + // (if the number of fields does not match 5, + // the code below would crash!) + verifyCustomerFields( result ); + + std::vector customers; + + auto row { db_fetch_row( result ) }; + while ( row ) + { + customers.push_back( Customer { + row[0], // Surname + row[1], // Name + row[2], // Street + std::stoi( row[3] ), // Street number + row[4] // City + } ); + + row = db_fetch_row( result ); + } + + return customers; } -void verifyCustomerFields(DB_QUERY_RESULT* result) +void verifyCustomerFields( DB_QUERY_RESULT* result ) { - const int numFields{ db_num_fields(result) }; - if (numFields < 0) - { - throw DatabaseException{"db_num_fields() failed"}; - } - if (numFields != 5) - { - throw DatabaseException{"Unexpected number of fields: " + std::to_string(numFields)}; - } + const int numFields { db_num_fields( result ) }; + if ( numFields < 0 ) + { + throw DatabaseException { "db_num_fields() failed" }; + } + if ( numFields != 5 ) + { + throw DatabaseException { "Unexpected number of fields: " + std::to_string( numFields ) }; + } } -void print(std::ostream& stream, const Customer& customer) +void print( std::ostream& stream, const Customer& customer ) { - stream << customer.toString() << std::endl; - if (std::cout.fail()) - { - std::cout.clear(); - throw std::runtime_error("Failed to output customer"); - } + stream << customer.toString( ) << std::endl; + if ( std::cout.fail( ) ) + { + std::cout.clear( ); + throw std::runtime_error( "Failed to output customer" ); + } } From 1f49b0136af1e88f4edf4142762259fc04c16a95 Mon Sep 17 00:00:00 2001 From: George Pimpleton Date: Sat, 17 Feb 2024 20:23:08 -0800 Subject: [PATCH 3/3] Code updates --- .../Chapter 16/Soln16_04/Soln16_04.cpp | 124 +++++++++--------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/Exercises/Modules/Chapter 16/Soln16_04/Soln16_04.cpp b/Exercises/Modules/Chapter 16/Soln16_04/Soln16_04.cpp index 7887a9d..4cf4b54 100644 --- a/Exercises/Modules/Chapter 16/Soln16_04/Soln16_04.cpp +++ b/Exercises/Modules/Chapter 16/Soln16_04/Soln16_04.cpp @@ -1,4 +1,5 @@ // Throwing and catching standard exceptions + import ; import ; import ; @@ -7,86 +8,87 @@ import ; import ; /* - This solution triggers all exceptions mentioned in the text + This solution triggers all exceptions mentioned in the text accompanying the Table with all Standard Library exceptions, in order. - To know how to trigger any other type, + To know how to trigger any other type, you will have to consult your Standard Library reference. - Note: while you'll typically catch exceptions by reference-to-const-std::exception + Note: while you'll typically catch exceptions by reference-to-const-std::exception (unless more specific handling for concrete types is required, of course), we specify the concrete exception type instead here for clarity. */ -// First, some dummy class types... + // First, some dummy class types... class BaseClass { public: - virtual ~BaseClass() = default; + virtual ~BaseClass( ) = default; }; class DerivedClass1 : public BaseClass {}; class DerivedClass2 : public BaseClass {}; -int main() +int main( ) { - try - { - std::vector v{ 1, 2, 3, 4, 5 }; - std::cout << v.at(10) << std::endl; - } - catch (const std::out_of_range& exception) - { - std::cout << "std::out_of_range: " << exception.what() << std::endl; - } + try + { + std::vector v { 1, 2, 3, 4, 5 }; + std::cout << v.at( 10 ) << std::endl; + } + catch ( const std::out_of_range& exception ) + { + std::cout << "std::out_of_range: " << exception.what( ) << std::endl; + } - try - { - std::cout << std::format("Hello {:g}!\n", "World"); - } - catch (const std::format_error& exception) - { - std::cout << "std::format_error: " << exception.what() << std::endl; - } + try + { + // std::cout << std::format( "Hello {:g}!\n", "World" ); + std::cout << std::format( "Hello {}!\n", "World" ); + } + catch ( const std::format_error& exception ) + { + std::cout << "std::format_error: " << exception.what( ) << std::endl; + } - try - { - // Remember: a polymorphic class is a class with at least one virtual function. - BaseClass* polymorphic{ nullptr }; - std::cout << typeid(*polymorphic).name(); - } - catch (const std::bad_typeid& exception) - { - std::cout << "std::bad_typeid: " << exception.what() << std::endl; - } + try + { + // Remember: a polymorphic class is a class with at least one virtual function. + BaseClass* polymorphic { nullptr }; + std::cout << typeid( *polymorphic ).name( ); + } + catch ( const std::bad_typeid& exception ) + { + std::cout << "std::bad_typeid: " << exception.what( ) << std::endl; + } - try - { - DerivedClass1 derived; - BaseClass& reference_to_base{ derived }; - dynamic_cast(reference_to_base); - } - catch (const std::bad_cast& exception) - { - std::cout << "std::bad_cast: " << exception.what() << std::endl; - } + try + { + DerivedClass1 derived; + BaseClass& reference_to_base { derived }; + dynamic_cast< DerivedClass2& >( reference_to_base ); + } + catch ( const std::bad_cast& exception ) + { + std::cout << "std::bad_cast: " << exception.what( ) << std::endl; + } - try - { - std::optional empty; - std::cout << empty.value() << std::endl; - } - catch (const std::bad_optional_access& exception) - { - std::cout << "std::bad_optional_access: " << exception.what() << std::endl; - } + try + { + std::optional empty; + std::cout << empty.value( ) << std::endl; + } + catch ( const std::bad_optional_access& exception ) + { + std::cout << "std::bad_optional_access: " << exception.what( ) << std::endl; + } - try - { - int size{ -1 }; - new int[size]; - } - catch (const std::bad_alloc& exception) - { - std::cout << "std::bad_alloc: " << exception.what() << std::endl; - } + try + { + int size { -1 }; + new int[size]; + } + catch ( const std::bad_alloc& exception ) + { + std::cout << "std::bad_alloc: " << exception.what( ) << std::endl; + } }