Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chapter 15 - Class string and String Stream Processing

Similar presentations


Presentation on theme: "Chapter 15 - Class string and String Stream Processing"— Presentation transcript:

1 Chapter 15 - Class string and String Stream Processing
Outline Introduction string Assignment and Concatenation Comparing strings Substrings Swapping strings string Characteristics Finding Strings and Characters in a string Replacing Characters in a string Inserting Characters into a string Conversion to C-Style char * Strings Iterators String Stream Processing

2 Template class basic_string
Introduction Template class basic_string String manipulation (copying, searching, etc.) typedef basic_string< char > string; Also typedef for wchar_t Include <string> string initialization string s1( "Hello" ); string s2( 8, 'x' ); 8 'x' characters string month = "March" Implicitly calls constructor

3 No conversion from int or char
Introduction No conversion from int or char The following definitions are errors string error1 = 'c'; string error2( 'u' ); string error3 = 22; string error4( 8 ); However, can assign to one char if declared s = 'n';

4 15.1 Introduction string features Not necessarily null terminated
length member function: s1.length() Use [] to access individual characters: s1[0] 0 to length-1 string not a pointer Many member functions take start position and length If length argument too large, max chosen Stream extraction cin >> stringObject; getline( cin, s) Delimited by newline

5 15.2 string Assignment and Concatenation
Makes a separate copy s2.assign(s1); Same as s2 = s1; myString.assign(s, start, N); Copies N characters from s, beginning at index start Individual characters s2[0] = s3[2];

6 15.2 string Assignment and Concatenation
Range checking s3.at( index ); Returns character at index Can throw out_of_range exception [] has no range checking Concatenation s3.append( "pet" ); s3 += "pet"; Both add "pet" to end of s3 s3.append( s1, start, N ); Appends N characters from s1, beginning at index start

7 String initialization and assignment.
// Fig. 15.1: fig15_01.cpp // Demonstrating string assignment and concatenation. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "cat" ); string string2; string string3; 17 string2 = string1; // assign string1 to string2 string3.assign( string1 ); // assign string1 to string3 cout << "string1: " << string1 << "\nstring2: " << string2 << "\nstring3: " << string3 << "\n\n"; 22 fig15_01.cpp (1 of 3) String initialization and assignment. Output string1: cat string2: cat string3: cat

8 Note use of member function at instead of [].
// modify string2 and string3 string2[ 0 ] = string3[ 2 ] = 'r'; 25 cout << "After modification of string2 and string3:\n" << "string1: " << string1 << "\nstring2: " << string2 << "\nstring3: "; 29 // demonstrating member function at for ( int i = 0; i < string3.length(); i++ ) cout << string3.at( i ); 33 // declare string4 and string5 string string4( string1 + "apult" ); string string5; 37 // overloaded += string3 += "pet"; // create "carpet" string1.append( "acomb" ); // create "catacomb" 41 // append subscript locations 4 through end of string1 to // create string "comb" (string5 was initially empty) string5.append( string1, 4, string1.length() ); 45 cout << "\n\nAfter concatenation:\nstring1: " << string1 << "\nstring2: " << string2 << "\nstring3: " << string3 << "\nstring4: " << string4 << "\nstring5: " << string5 << endl; 50 fig15_01.cpp (2 of 3) After modification of string2 and string3: string1: cat string2: rat string3: car Note use of member function at instead of []. After concatenation: string1: catacomb string2: rat string3: carpet string4: catapult string5: comb

9 fig15_01.cpp (3 of 3) fig15_01.cpp output (1 of 1)
return 0; 52 53 } // end main string1: cat string2: cat string3: cat After modification of string2 and string3: string2: rat string3: car After concatenation: string1: catacomb string3: carpet string4: catapult string5: comb fig15_01.cpp (3 of 3) fig15_01.cpp output (1 of 1)

10 15.3 Comparing strings Overloaded operators s1.compare(s2)
==, !=, <, >, <= and >= Return bool s1.compare(s2) Returns positive if s1 lexicographically greater Compares letter by letter 'B' lexicographically greater than 'A' Returns negative if less, zero if equal s1.compare(start, length, s2, start, length) Compare portions of s1 and s2 s1.compare(start, length, s2) Compare portion of s1 with all of s2

11 fig15_02.cpp (1 of 4) 1 // Fig. 15.2: fig15_02.cpp
// Demonstrating string comparison capabilities. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "Testing the comparison functions." ); string string2( "Hello" ); string string3( "stinger" ); string string4( string2 ); 18 fig15_02.cpp (1 of 4)

12 Note use of overloaded == operator. fig15_02.cpp (2 of 4)
cout << "string1: " << string1 << "\nstring2: " << string2 << "\nstring3: " << string3 << "\nstring4: " << string4 << "\n\n"; 22 // comparing string1 and string4 if ( string1 == string4 ) cout << "string1 == string4\n"; else { // string1 != string4 if ( string1 > string4 ) cout << "string1 > string4\n"; else // string1 < string4 cout << "string1 < string4\n"; } 32 // comparing string1 and string2 int result = string1.compare( string2 ); 35 if ( result == 0 ) cout << "string1.compare( string2 ) == 0\n"; else // result != 0 if ( result > 0 ) cout << "string1.compare( string2 ) > 0\n"; else // result < 0 cout << "string1.compare( string2 ) < 0\n"; 43 Note use of overloaded == operator. fig15_02.cpp (2 of 4) string1: Testing the comparison functions. string2: Hello string3: stinger string4: Hello string1 > string4

13 Note use of compare. fig15_02.cpp (3 of 4)
// comparing string1 (elements 2-5) and string3 (elements 0-5) result = string1.compare( 2, 5, string3, 0, 5 ); 46 if ( result == 0 ) cout << "string1.compare( 2, 5, string3, 0, 5 ) == 0\n"; else // result != 0 if ( result > 0 ) cout << "string1.compare( 2, 5, string3, 0, 5 ) > 0\n"; else // result < 0 cout << "string1.compare( 2, 5, string3, 0, 5 ) < 0\n"; 54 // comparing string2 and string4 result = string4.compare( 0, string2.length(), string2 ); 57 if ( result == 0 ) cout << "string4.compare( 0, string2.length(), " << "string2 ) == 0" << endl; else // result != 0 if ( result > 0 ) cout << "string4.compare( 0, string2.length(), " << "string2 ) > 0" << endl; else // result < 0 cout << "string4.compare( 0, string2.length(), " << "string2 ) < 0" << endl; 68 Note use of compare. fig15_02.cpp (3 of 4)

14 fig15_02.cpp (4 of 4) fig15_02.cpp output (1 of 1)
// comparing string2 and string4 result = string2.compare( 0, 3, string4 ); 71 if ( result == 0 ) cout << "string2.compare( 0, 3, string4 ) == 0" << endl; else // result != 0 if ( result > 0 ) cout << "string2.compare( 0, 3, string4 ) > 0" << endl; else // result < 0 cout << "string2.compare( 0, 3, string4 ) < 0" << endl; 79 return 0; 81 82 } // end main fig15_02.cpp (4 of 4) fig15_02.cpp output (1 of 1) string1: Testing the comparison functions. string2: Hello string3: stinger string4: Hello string1 > string4 string1.compare( string2 ) > 0 string1.compare( 2, 5, string3, 0, 5 ) == 0 string4.compare( 0, string2.length(), string2 ) == 0 string2.compare( 0, 3, string4 ) < 0

15 Function substr gets substring
Substrings Function substr gets substring s1.substr( start, N ); Gets N characters, beginning with index start Returns substring

16 fig15_03.cpp (1 of 1) fig15_03.cpp output (1 of 1)
// Fig. 15.3: fig15_03.cpp // Demonstrating string member function substr. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "The airplane landed on time." ); 15 // retrieve substring "plane" which // begins at subscript 7 and consists of 5 elements cout << string1.substr( 7, 5 ) << endl; 19 return 0; 21 22 } // end main fig15_03.cpp (1 of 1) fig15_03.cpp output (1 of 1) Note usage of substr. plane

17 Swapping strings s1.swap(s2); Switch contents of two strings

18 fig15_04.cpp (1 of 1) Call swap. 1 // Fig. 15.4: fig15_04.cpp
// Using the swap function to swap two strings. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string first( "one" ); string second( "two" ); 16 // output strings cout << "Before swap:\n first: " << first << "\nsecond: " << second; 20 first.swap( second ); // swap strings 22 cout << "\n\nAfter swap:\n first: " << first << "\nsecond: " << second << endl; 25 return 0; 27 28 } // end main fig15_04.cpp (1 of 1) Call swap.

19 Before swap: first: one second: two After swap: first: two second: one
After swap: first: two second: one fig15_04.cpp output (1 of 1)

20 15.6 string Characteristics
Member functions s1.size() and s1.length() Number of characters in string s1.capacity() Number of elements that can be stored without reallocation s1.max_size() Maximum possible string size s1.empty() Returns true if empty s1.resize(newlength) Resizes string to newlength

21 fig15_05.cpp (1 of 3) 1 // Fig. 15.5: fig15_05.cpp
// Demonstrating member functions related to size and capacity. #include <iostream> 4 using std::cout; using std::endl; using std::cin; using std::boolalpha; 9 10 #include <string> 11 12 using std::string; 13 14 void printStatistics( const string & ); 15 16 int main() 17 { string string1; 19 cout << "Statistics before input:\n" << boolalpha; printStatistics( string1 ); 22 // read in "tomato" cout << "\n\nEnter a string: "; cin >> string1; // delimited by whitespace cout << "The string entered was: " << string1; fig15_05.cpp (1 of 3)

22 fig15_05.cpp (2 of 3) Resize string. 27
cout << "\nStatistics after input:\n"; printStatistics( string1 ); 30 // read in "soup" cin >> string1; // delimited by whitespace cout << "\n\nThe remaining string is: " << string1 << endl; printStatistics( string1 ); 35 // append 46 characters to string1 string1 += " abcdefghijklmnopqrstuvwxyz "; cout << "\n\nstring1 is now: " << string1 << endl; printStatistics( string1 ); 40 // add 10 elements to string1 string1.resize( string1.length() + 10 ); cout << "\n\nStats after resizing by (length + 10):\n"; printStatistics( string1 ); 45 cout << endl; return 0; 48 49 } // end main 50 fig15_05.cpp (2 of 3) Resize string.

23 fig15_05.cpp (3 of 3) fig15_05.cpp output (1 of 2)
51 // display string statistics 52 void printStatistics( const string &stringRef ) 53 { cout << "capacity: " << stringRef.capacity() << "\nmax size: " << stringRef.max_size() << "\nsize: " << stringRef.size() << "\nlength: " << stringRef.length() << "\nempty: " << stringRef.empty(); 59 60 } // end printStatistics Display various string characteristics. fig15_05.cpp (3 of 3) fig15_05.cpp output (1 of 2) Statistics before input: capacity: 0 max size: size: 0 length: 0 empty: true Enter a string: tomato soup The string entered was: tomato Statistics after input: capacity: 31 size: 6 length: 6 empty: false

24 fig15_05.cpp output (2 of 2) The remaining string is: soup
capacity: 31 max size: size: 4 length: 4 empty: false string1 is now: soup abcdefghijklmnopqrstuvwxyz capacity: 63 size: 50 length: 50 Stats after resizing by (length + 10): size: 60 length: 60 fig15_05.cpp output (2 of 2)

25 15.7 Finding Strings and Characters in a string
Find functions If found, index returned If not found, string::npos returned Public static constant in class string s1.find( s2 ) s1.rfind( s2 ) Searches right-to-left s1.find_first_of( s2 ) Returns first occurrence of any character in s2 s1.find_frist_of( "abcd" ) Returns index of first 'a', 'b', 'c' or 'd'

26 15.7 Finding Strings and Characters in a string
Find functions s1.find_last_of( s2 ) Finds last occurrence of any character in s2 s1.find_first_not_of( s2 ) Finds first character NOT in s2 s1.find_last_not_of( s2 ) Finds last character NOT in s2

27 Note call to function find.
// Fig. 15.6: fig15_06.cpp // Demonstrating the string find member functions #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "noon is 12 p.m." ); int location; 16 // find "is" at location 5 cout << "Original string:\n" << string1 << "\n\n(find) \"is\" was found at: " << string1.find( "is" ) << "\n(rfind) \"is\" was found at: " << string1.rfind( "is" ); 23 // find 'o' at location 1 location = string1.find_first_of( "misop" ); fig15_06.cpp (1 of 3) Note call to function find. Find first occurrence of m, i, s, o or p.

28 Calls to other find functions similar. fig15_06.cpp (2 of 3)
26 cout << "\n\n(find_first_of) found '" << string1[ location ] << "' from the group \"misop\" at: " << location; 30 // find 'm' at location 13 location = string1.find_last_of( "misop" ); cout << "\n\n(find_last_of) found '" << string1[ location ] << "' from the group \"misop\" at: " << location; 36 // find '1' at location 8 location = string1.find_first_not_of( "noi spm" ); cout << "\n\n(find_first_not_of) '" << string1[ location ] << "' is not contained in \"noi spm\" and was found at:" << location; 42 // find '.' at location 12 location = string1.find_first_not_of( "12noi spm" ); cout << "\n\n(find_first_not_of) '" << string1[ location ] << "' is not contained in \"12noi spm\" and was " << "found at:" << location << endl; Calls to other find functions similar. fig15_06.cpp (2 of 3)

29 fig15_06.cpp (3 of 3) fig15_06.cpp output (1 of 1)
48 // search for characters not in string1 location = string1.find_first_not_of( "noon is 12 p.m." ); cout << "\nfind_first_not_of(\"noon is 12 p.m.\")" << " returned: " << location << endl; 53 return 0; 55 56 } // end main fig15_06.cpp (3 of 3) fig15_06.cpp output (1 of 1) Original string: noon is 12 p.m. (find) "is" was found at: 5 (rfind) "is" was found at: 5 (find_first_of) found 'o' from the group "misop" at: 1 (find_last_of) found 'm' from the group "misop" at: 13 (find_first_not_of) '1' is not contained in "noi spm" and was found at:8 (find_first_not_of) '.' is not contained in "12noi spm" and was found at:12 find_first_not_of("noon is 12 p.m.") returned: -1

30 15.8 Replacing Characters in a string
s1.erase( start ) Erase from index start to end of string, including start Replace s1.replace( begin, N, s2) begin: index in s1 to start replacing N: number of characters to replace s2: replacement string s1.replace( begin, N, s2, index, num ) index: element in s2 where replacement begins num: number of elements to use when replacing Replacement can overwrite characters string::npos represents max string length

31 fig15_07.cpp (1 of 2) 1 // Fig. 15.7: fig15_07.cpp
// Demonstrating string member functions erase and replace. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { // compiler concatenates all parts into one string string string1( "The values in any left subtree" "\nare less than the value in the" "\nparent node and the values in" "\nany right subtree are greater" "\nthan the value in the parent node" ); 20 cout << "Original string:\n" << string1 << endl << endl; 22 // remove all characters from (and including) location 62 // through the end of string1 string1.erase( 62 ); 26 fig15_07.cpp (1 of 2)

32 string::npos represents max string length.
// output new string cout << "Original string after erase:\n" << string1 << "\n\nAfter first replacement:\n"; 30 // replace all spaces with period int position = string1.find( " " ); 33 while ( position != string::npos ) { string1.replace( position, 1, "." ); position = string1.find( " ", position + 1 ); } // end while 38 cout << string1 << "\n\nAfter second replacement:\n"; 40 // replace all periods with two semicolons // NOTE: this will overwrite characters position = string1.find( "." ); 44 while ( position != string::npos ) { string1.replace( position, 2, "xxxxx;;yyy", 5, 2 ); position = string1.find( ".", position + 1 ); } // end while 49 cout << string1 << endl; return 0; 52 53 } // end main string::npos represents max string length. fig15_07.cpp (2 of 2) Find each space and replace with a '.' Start each search at the next position. Replace all '.' with two semicolons (the two characters at index 5).

33 fig15_07.cpp output (1 of 1) Original string:
The values in any left subtree are less than the value in the parent node and the values in any right subtree are greater than the value in the parent node Original string after erase: After first replacement: The.values.in.any.left.subtree are.less.than.the.value.in.the After second replacement: The;;alues;;n;;ny;;eft;;ubtree are;;ess;;han;;he;;alue;;n;;he fig15_07.cpp output (1 of 1)

34 15.9 Inserting Characters into a string
s1.insert( index, s2 ) Inserts s2 before position index s1.insert( index, s2, index2, N ); Inserts substring of s2 before position index Substring is N characters, starting at index2

35 Insert all of string2 before element 10.
// Fig. 15.8: fig15_08.cpp // Demonstrating class string insert member functions. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "beginning end" ); string string2( "middle " ); string string3( " " ); string string4( "xx" ); 18 cout << "Initial strings:\nstring1: " << string1 << "\nstring2: " << string2 << "\nstring3: " << string3 << "\nstring4: " << string4 << "\n\n"; 22 // insert "middle" at location 10 in string1 string1.insert( 10, string2 ); 25 fig15_08.cpp (1 of 2) Insert all of string2 before element 10.

36 fig15_08.cpp (2 of 2) fig15_08.cpp output (1 of 1)
// insert "xx" at location 3 in string3 string3.insert( 3, string4, 0, string::npos ); 28 cout << "Strings after insert:\nstring1: " << string1 << "\nstring2: " << string2 << "\nstring3: " << string3 << "\nstring4: " << string4 << endl; 32 return 0; 34 35 } // end main Insert all of string4 before index 3. fig15_08.cpp (2 of 2) fig15_08.cpp output (1 of 1) Initial strings: string1: beginning end string2: middle string3: string4: xx Strings after insert: string1: beginning middle end string3: 123xx45678

37 15.10 Conversion to C-Style char * Strings
Conversion functions strings not necessarily null-terminated s1.copy( ptr, N, index ) Copies N characters into the array ptr Starts at location index Need to null terminate s1.c_str() Returns const char * Null terminated s1.data() NOT null-terminated

38 Note calls to copy and c_str.
// Fig. 15.9: fig15_09.cpp // Converting to C-style strings. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "STRINGS" ); const char *ptr1 = 0; int length = string1.length(); char *ptr2 = new char[ length + 1 ]; // including null 18 // copy characters from string1 into allocated memory string1.copy( ptr2, length, 0 ); ptr2[ length ] = '\0'; // add null terminator 22 // output cout << "string s is " << string1 << "\nstring1 converted to a C-Style string is " << string1.c_str() << "\nptr1 is "; fig15_09.cpp (1 of 2) Note calls to copy and c_str.

39 fig15_09.cpp (2 of 2) fig15_09.cpp output (1 of 1)
27 // Assign to pointer ptr1 the const char * returned by // function data(). NOTE: this is a potentially dangerous // assignment. If string1 is modified, pointer ptr1 can // become invalid. ptr1 = string1.data(); 33 // output each character using pointer for ( int i = 0; i < length; i++ ) cout << *( ptr1 + i ); // use pointer arithmetic 37 cout << "\nptr2 is " << ptr2 << endl; delete [] ptr2; return 0; 41 42 } // end main fig15_09.cpp (2 of 2) fig15_09.cpp output (1 of 1) string s is STRINGS string1 converted to a C-Style string is STRINGS ptr1 is STRINGS ptr2 is STRINGS

40 15.11 Iterators Iterators Forwards and backwards traversal of strings
Access to individual characters Similar to pointer operations More coverage in Chapter 20

41 15.11 Iterators Basic usage Creation Referencing
string::const_iterator i = s.begin(); const, cannot modify string (more Chapter 20) Referencing *i; // reference character ++i; // traverse one character forward Test for end of string i != s.end() end returns iterator after last element of s

42 Print each character using the iterator.
// Fig. 15.10: fig15_10.cpp // Using an iterator to output a string. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 int main() 13 { string string1( "Testing iterators" ); string::const_iterator iterator1 = string1.begin(); 16 cout << "string1 = " << string1 << "\n(Using iterator iterator1) string1 is: "; 19 // iterate through string while ( iterator1 != string1.end() ) { cout << *iterator1; // dereference iterator to get char iterator1; // advance iterator to next char } // end while 25 cout << endl; return 0; 28 29 } // end main fig15_10.cpp (1 of 1) Print each character using the iterator.

43 fig15_10.cpp output (1 of 1) string1 = Testing iterators
(Using iterator iterator1) string1 is: Testing iterators fig15_10.cpp output (1 of 1)

44 15.12 String Stream Processing
I/O of strings to and from memory Called in-memory I/O or string stream processing Classes istringstream (input from string) ostringstream (output to a string) <sstream> and <iostream> headers Use string formatting to save data to memory

45 15.12 String Stream Processing
String output Ostringstream outputString; outputString << s1 << s2; Member function str Returns string that was output to memory outputString.str()

46 Create ostringstream object.
// Fig. 15.11: fig15_11.cpp // Using a dynamically allocated ostringstream object. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 #include <sstream> 13 14 using std::ostringstream; 15 16 int main() 17 { ostringstream outputString; // create ostringstream instance 19 string string1( "Output of several data types " ); string string2( "to an ostringstream object:" ); string string3( "\n double: " ); string string4( "\n int: " ); string string5( "\naddress of int: " ); fig15_11.cpp (1 of 2) Create ostringstream object.

47 Output format just like to writing to cout.
25 double double1 = ; int integer = 22; 28 // output strings, double and int to outputString outputString << string1 << string2 << string3 << double1 << string4 << integer << string5 << &integer; 32 // call str to output contents cout << "outputString contains:\n" << outputString.str(); 35 // add additional characters and call str to output string outputString << "\nmore characters added"; cout << "\n\nafter additional stream insertions,\n" << "outputString contains:\n" << outputString.str() << endl; 41 return 0; 43 44 } // end main fig15_11.cpp (2 of 2) Output format just like to writing to cout.

48 fig15_11.cpp output (1 of 1) outputString contains:
Output of several data types to an ostringstream object: double: int: 22 address of int: 0012FE94 after additional stream insertions, more characters added fig15_11.cpp output (1 of 1)

49 15.12 String Stream Processing
String input istringstream inputString ( myString ); inputString >> string1 >> string2 Like reading from cin

50 Create and initialize istringstream object.
// Fig. 15.12: fig15_12.cpp // Demonstrating input from an istringstream object. #include <iostream> 4 using std::cout; using std::endl; 7 #include <string> 9 10 using std::string; 11 12 #include <sstream> 13 14 using std::istringstream; 15 16 int main() 17 { string input( "Input test A" ); istringstream inputString( input ); string string1; string string2; int integer; double double1; char character; 25 fig15_12.cpp (1 of 2) Create and initialize istringstream object.

51 Read data into variables. fig15_12.cpp (2 of 2)
inputString >> string1 >> string2 >> integer >> double1 >> character; 28 cout << "The following items were extracted\n" << "from the istringstream object:" << "\nstring: " << string1 << "\nstring: " << string2 << "\n int: " << integer << "\ndouble: " << double1 << "\n char: " << character; 36 // attempt to read from empty stream long value; 39 inputString >> value; 41 // test stream results if ( inputString.good() ) cout << "\n\nlong value is: " << value << endl; else cout << "\n\ninputString is empty" << endl; 47 return 0; 49 50 } // end main Read data into variables. fig15_12.cpp (2 of 2) good returns 1 if can still read data (no EOF, bad bits, etc). In this case, there is no data, so the test fails.

52 fig15_12.cpp output (1 of 1) The following items were extracted
from the istringstream object: string: Input string: test int: 123 double: 4.7 char: A inputString is empty fig15_12.cpp output (1 of 1)


Download ppt "Chapter 15 - Class string and String Stream Processing"

Similar presentations


Ads by Google