C++ defines a rich library of abstract data types. Among the most important library types are string and vector, which define variable-sized character strings and collections, respectively.
Associated with string and vector are companion types known as iterators, which are used to access the characters in a string or the elements in a vector. These library types are abstractions of more primitive types arrays and pointers that are part of the language.
Another library type, bitset, provides an abstract way to manipulate a collection of bits. This class provides a more convenient way of dealing with bits than is offered by the built-in bitwise operators on values of integral type.
This chapter introduces the library vector, string, and bitset types.
Section 3.1. Namespace using Declarations
- Such names use the :: operator, which is the scope operator (Section 1.2.2, p. 8). This operator says that we should look for the name of the right-hand operand in the scope of the left-hand operand.
- A using declaration allows us to access a name from a namespace without the prefix namespace_name::. A using declaration has the following form: using namespace::name;
- A Separate Using Declaration Is Required for Each Name
- A using declaration introduces only one namespace member at a time. It allows us to be very specific regarding which names are used in our programs. If we want to use several names from stdor any other namespacethen we must issue a using declaration for each name that we intend to use.
- Beware:Readers should be aware that they must add appropriate #include and using declarations to our examples before compiling them.
- Class Definitions that Use Standard Library Types
- There is one case in which we should always use the fully qualified library names: inside header files. The reason is that the contents of a header are copied into our program text by the preprocessor. When we #include a file, it is as if the exact text of the header is part of our file. If we place a using declaration within a header, it is equivalent to placing the same using declaration in every program that includes the header whether that program wants the using declaration or not.
- Best Practice : In general, it is good practice for headers to define only what is strictly necessary.
Section 3.2. Library string Type
- The string type supports variable-length character strings. The library takes care of managing the memory associated with storing the characters and provides various useful operations. The library string type is intended to be efficient enough for general use.
- As with any library type, programs that use strings must first include the associated header. Our programs will be shorter if we also provide an appropriate using declaration: #include <string> using std::string;
- 3.2.1. Defining and Initializing strings
- A constructor is a special member function that defines how objects of that type can be initialized.
- The default constructor (Section 2.3.4, p. 52) is used "by default" when no initializer is specified.
- Ways to Initialize a string
- Caution: Library string Type and String Literals: For historical reasons, and for compatibility with C, character string literals are not the same type as the standard library string type. This fact can cause confusion and is important to keep in mind when using a string literal or the string data type.
- 3.2.2. Reading and Writing strings
- Similarly, we can use the iostream and string libraries to allow us to read and write strings using the standard input and output operators
#include "stdafx.h"
#include <iostream>;
#include <string>;
using namespace std;
// Note: #include and using declarations must be added to compile this code
int main()
{
string s; // empty string
cin >> s; // read whitespace-separated string into s
cout << s << endl; // write s to the output
return 0;
} - reads the standard input storing what is read into s. The string input operator:
- Reads and discards any leading whitespace (e.g., spaces, newlines, tabs)
- It then reads characters until the next whitespace character is encountered
- Reads and discards any leading whitespace (e.g., spaces, newlines, tabs)
- Reading an Unknown Number of strings
- Like the input operators that read built-in types, the string input operator returns the stream from which it read. Therefore, we can use a string input operation as a condition, just as we did when reading ints in the program on page 18. The following program reads a set of strings from the standard input and writes what it has read, one string per line, to the standard output:
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
string word;
// read until end-of-file, writing each word to a new line
while (cin >> word)
cout << word << endl;
return 0;
}
- Like the input operators that read built-in types, the string input operator returns the stream from which it read. Therefore, we can use a string input operation as a condition, just as we did when reading ints in the program on page 18. The following program reads a set of strings from the standard input and writes what it has read, one string per line, to the standard output:
- Using getline to Read an Entire Line
- This is a function that takes both an input stream and a string. The getline function reads the next line of input from the stream and stores what it read, not including the newline, in its string argument. Unlike the input operator, getline does not ignore leading newlines. Whenever getline encounters a newline, even if it is the first character in the input, it stops reading the input and returns. The effect of encountering a newline as the first character in the input is that the string argument is set to the empty string.
- Because line does not contain a newline, we must write our own if we want the strings written one to a line. As usual, we use endl to write a newline and flush the output buffer.
- NOTE:The newline that causes getline to return is discarded; it does not get stored in the string.
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line;
// read line at time until end-of-file
while (getline(cin, line))
cout << line << endl;
return 0;
}
- This is a function that takes both an input stream and a string. The getline function reads the next line of input from the stream and stores what it read, not including the newline, in its string argument. Unlike the input operator, getline does not ignore leading newlines. Whenever getline encounters a newline, even if it is the first character in the input, it stops reading the input and returns. The effect of encountering a newline as the first character in the input is that the string argument is set to the empty string.
- Similarly, we can use the iostream and string libraries to allow us to read and write strings using the standard input and output operators
- 3.2.3. Operations on strings
- string::size_type
- It might be logical to expect that size returns an int, or, thinking back to the note on page 38, an unsigned. Instead, the size operation returns a value of type string::size_type. This type requires a bit of explanation.
- The string class and many other library types defines several companion types. These companion types make it possible to use the library types in a machine-independent manner. The type size_type is one of these companion types. It is defined as a synonym for an unsigned type either unsigned int or unsigned long that is guaranteed to be big enough to hold the size of any string. To use the size_type defined by string, we use the scope operator to say that the name size_type is defined in the string class.
- Best Practice:Any variable used to store the result from the string size operation ought to be of type string::size_type. It is particularly important not to assign the return from size to an int.
- Although we don't know the precise type of string::size_type, we do know that it is an unsigned type (Section 2.1.1, p. 34). We also know that for a given type, the unsigned version can hold a positive value twice as large as the corresponding signed type can hold. This fact implies that the largest string could be twice as large as the size an int can hold.
- Another problem with using an int is that on some machines the size of an int is too small to hold the size of even plausibly large strings. For example, if a machine has 16-bit ints, then the largest string an int could represent would have 32,767 characters. A string that held the contents of a file could easily exceed this size. The safest way to hold the size of a string is to use the type the library defines for this purpose, which is string::size_type.
- It might be logical to expect that size returns an int, or, thinking back to the note on page 38, an unsigned. Instead, the size operation returns a value of type string::size_type. This type requires a bit of explanation.
- The string Relational Operators
- The string class defines several operators that compare two string values. Each of these operators works by comparing the characters from each string.
- NOTE:string comparisons are case-sensitivethe upper- and lowercase versions of a letter are different characters. On most computers, the uppercase letters come first: Every uppercase letter is less than any lowercase letter.
- The relational operators compare strings using the same strategy as in a (case-sensitive) dictionary:
- If two strings have different lengths and if every character in the shorter string is equal to the corresponding character of the longer string, then the shorter string is less than the longer one.
- If the characters in two strings differ, then we compare them by comparing the first character at which the strings differ.
- If two strings have different lengths and if every character in the shorter string is equal to the corresponding character of the longer string, then the shorter string is less than the longer one.
- The string class defines several operators that compare two string values. Each of these operators works by comparing the characters from each string.
- Assignment for strings
- In general the library types strive to make it as easy to use a library type as it is to use a built-in type. To this end, most of the library types support assignment. In the case of strings, we can assign one string object to another:
- Most string library implementations go to some trouble to provide efficient implementations of operations such as assignment, but it is worth noting that conceptually, assignment requires a fair bit of work. It must delete the storage containing the characters associated with st1, allocate the storage needed to contain a copy of the characters associated with st2, and then copy those characters from st2 into this new storage.
- In general the library types strive to make it as easy to use a library type as it is to use a built-in type. To this end, most of the library types support assignment. In the case of strings, we can assign one string object to another:
- Adding Two strings
- Addition on strings is defined as concatenation. That is, it is possible to concatenate two or more strings through the use of either the plus operator (+) or the compound assignment operator (+=)
- Adding Character String Literals and strings
- When mixing strings and string literals, at least one operand to each + operator must be of string type:
- Fetching a Character from a string
- The string type uses the subscript ([ ]) operator to access the individual characters in the string. The subscript operator takes a size_type value that denotes the character position we wish to fetch. The value in the subscript is often called "the subscript" or "an index."
- NOTE:Subscripts for strings start at zero; if s is a string, then if s isn't empty, s[0] is the first character in the string, s[1] is the second if there is one, and the last character is in s[s.size() - 1].It is an error to use an index outside this range.
- The string type uses the subscript ([ ]) operator to access the individual characters in the string. The subscript operator takes a size_type value that denotes the character position we wish to fetch. The value in the subscript is often called "the subscript" or "an index."
- Subscripting Yields an Lvalue
- Recall that a variable is an lvalue (Section 2.3.1, p. 45), and that the left-hand side of an assignment must be an lvalue. Like a variable, the value returned by the subscript operator is an lvalue. Hence, a subscript can be used on either side of an assignment. The following loop sets each character in str to an asterisk:
- Computing Subscript Values
- Any expression that results in an integral value can be used as the index to the subscript operator.
- Although any integral type can be used as an index, the actual type of the index is string::size_type, which is an unsigned type.
- BestPractice:The same reasons to use string::size_type as the type for a variable that holds the return from size apply when defining a variable to serve as an index. A variable used to index a string should have type string::size_type.
- When we subscript a string, we are responsible for ensuring that the index is "in range." By in range, we mean that the index is a number that, when assigned to a size_type, is a value in the range from 0 through the size of the string minus one. By using a string::size_type or another unsigned type as the index, we ensure that the subscript cannot be less than zero. As long as our index is an unsigned type, we need only check that it is less than the size of the string.
- Beware:The library is not required to check the value of the index. Using an index that is out of range is undefined and usually results in a serious run-time error.
// EXE-3.2.3.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
int main()
{
string st("The expense of spirit\n");
cout << "The size of " << st << "is " << st.size()
<< " characters, including the newline" << endl;
if(!st.empty())
{
cout << "The string '" << st << "'is not empty!" << endl;
}
//Beware: here do not use the int type , better to use the string::sizetype;
string::size_type sLength = st.size();
cout << "The length of the string is " << sLength << endl;
string big = "big", small = "small";
string s0 = big; // s1 is a copy of big
if (big == small) // false
{
cout << big << " and " << small << " are equal!" << endl;
}
if (big <= s0) // true, they're equal, so big is less than or equal to s1
{
cout << "'" << big << "'" << " is smaller than or equal to " << "'" << s0 << "'" << endl;
}
string substr = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
if(substr < phrase)
{
cout << "'" << substr << "'" << " is less than " << "'" << phrase << "'" << endl;
}
else
{
cout << "'" << substr << "'" << " is greater than " << "'" << phrase << "'" << endl;
}
if((slang > substr) && (slang > phrase))
{
cout << "'" << slang << "'" << " is greater than " << "'" << phrase << "'" << endl;
cout << "'" << slang << "'" << " is greater than " << "'" << substr << "'" << endl;
}
// st1 is an empty string, st2 is a copy of the literal
string st1, st2 = "The expense of spirit";
st1 = st2; // replace st1 by a copy of st2
cout << "The st1 is '" << st1 << "' and st2 is '" << st2 << "'" << endl;
string s1("hello, ");
string s2("world!");
cout << "s1 is '" << s1 << "' and the s2 is '" << s2 << "'" << endl;
string s3 = s1 + s2; // s3 is hello, world\n
cout << "The s3 = s1 + s2 is '" << s3 << "'" << endl;
s1 += s2; // equivalent to s1 = s1 + s2
cout << "The s1 += s2 is '" << s1 << "'" << endl;
string s10("hello");
string s20("world");
string s30 = s10 + ", " + s20 + "\n";
string s11 = "hello"; // no punctuation
string s21 = "world";
string s31 = s11 + ", "; // ok: adding a string and a literal
//string s41 = "hello" + ", "; // error: no string operand "error C2110: '+' : cannot add two pointers"
string s51 = s11 + ", " + "world"; // ok: each + has string operand
//string s61 = "hello" + ", " + s21; // error: can't add string literals "error C2110: '+' : cannot add two pointers"
string tmp = s11 + ", "; // ok: + has a string operand
string s52 = tmp + "world"; // ok: + has a string operand
string str("some string");
for (string::size_type ix = 0; ix < str.size(); ++ix)
cout << str[ix] << endl;
for (string::size_type ix = 0; ix != str.size(); ++ix)
str[ix] = '*';
cout << "str is '" << str << "'" << endl;
string::size_type someotherval(3),someval(2);
str[someotherval * someval] = someval;
cout << "The str is updated as '" << str << "'" << endl;
return 0;
} - Any expression that results in an integral value can be used as the index to the subscript operator.
- string::size_type
- 3.2.4. Dealing with the Characters of a string
- Often we want to process the individual characters of a string. Table 3.3 on the facing page lists the functions that can be used on the characters in a string (or on any other char value). These functions are defined in the cctype header.
-
- These functions mostly test the given character and return an int, which acts as a truth value. Each function returns zero if the test fails; otherwise, they return a (meaningless) nonzero value indicating that the character is of the requested kind.
- For these functions, a printable character is a character with a visible representation; whitespace is one of space, tab, vertical tab, return, newline, and form feed; and punctuation is a printable character that is not a digit, a letter, or (printable) whitespace character such as space.
- Rather than returning a truth value, the tolower and toupper functions return a charactereither the argument unchanged or the lower- or uppercase version of the character.
- Often we want to process the individual characters of a string. Table 3.3 on the facing page lists the functions that can be used on the characters in a string (or on any other char value). These functions are defined in the cctype header.
- Advice: Use the C++ Versions of C Library Headers
- In addition to facilities defined specifically for C++, the C++ library incorporates the C library. The cctype header makes available the C library functions defined in the C header file named ctype.h.
- The standard C headers names use the form name.h. The C++ versions of these headers are named cnamethe C++ versions remove the .h suffix and precede the name by the letter c. Thec indicates that the header originally comes from the C library. Hence, cctype has the same contents as ctype.h, but in a form that is appropriate for C++ programs. In particular, the names defined in the cname headers are defined inside the std namespace, whereas those defined in the .h versions are not.
- Ordinarily, C++ programs should use the cname versions of headers and not the name.h versions. That way names from the standard library are consistently found in the std namespace. Using the .h headers puts the burden on the programmer to remember which library names are inherited from C and which are unique to C++.
// EXE-3.2.4.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string s("Hello World!!!");
string::size_type punct_cnt = 0;
// count number of punctuation characters in s
for (string::size_type index = 0; index != s.size(); ++index)
if (ispunct(s[index]))
++punct_cnt;
cout << punct_cnt
<< " punctuation characters in " << s << endl;
// convert s to lowercase
for (string::size_type index = 0; index != s.size(); ++index)
s[index] = tolower(s[index]);
cout << s << endl;
return 0;
} - In addition to facilities defined specifically for C++, the C++ library incorporates the C library. The cctype header makes available the C library functions defined in the C header file named ctype.h.
Section 3.3. Library vector Type
- A vector is a collection of objects of a single type, each of which has an associated integer index.
- We speak of a vector as a container because it contains other objects. All of the objects in a container must have the same type.
- To use a vector, we must include the appropriate header. In our examples, we also assume an appropriate using declaration is made: #include <vector> using std::vector;
- A vector is a class template. Templates let us write a single class or function definition that can be used on a variety of types.
- To declare objects of a type generated from a class template, we must supply additional information. The nature of this information depends on the template.
- NOTE: vector is not a type; it is a template that we can use to define any number of types. Each of vector type specifies an element type. Hence, vector<int> and vector<string> are types.
- 3.3.1. Defining and Initializing vectors
- The vector class defines several constructors (Section 2.3.3, p. 49), which we use to define and initialize vector objects. The constructors are listed in Table 3.4.
- Creating a Specified Number of Elements
- When we create a vector that is not empty, we must supply value(s) to use to initialize the elements. When we copy one vector to another, each element in the new vector is initialized as a copy of the corresponding element in the original vector. The two vectors must hold the same element type:
- Key Concept: vectorS Grow Dynamically
- A central property of vectors (and the other library containers) is that they are required to be implemented so that it is efficient to add elements to them at run time. Because vectors grow efficiently, it is usually best to let the vector grow by adding elements to it dynamically as the element values are known.
- As we'll see in Chapter 4, this behavior is distinctly different from that of built-in arrays in C and for that matter in most other languages. In particular, readers accustomed to using C or Java might expect that because vector elements are stored contiguously, it would be best to pre-allocate the vector at its expected size. In fact, the contrary is the case, for reasons we'll explore in Chapter 9.
- Beware:Although we can pre-allocate a given number of elements in a vector, it is usually more efficient to define an empty vector and add elements to it (as we'll learn how to do shortly).
- A central property of vectors (and the other library containers) is that they are required to be implemented so that it is efficient to add elements to them at run time. Because vectors grow efficiently, it is usually best to let the vector grow by adding elements to it dynamically as the element values are known.
- Value Initialization
- When we do not specify an element initializer, then the library creates a value initialized element initializer for us. This library-generated value is used to initialize each element in the container. The value of the element initializer depends on the type of the elements stored in the vector.
- If the vector holds elements of a built-in type, such as int, then the library creates an element initializer with a value of 0:
- If the vector holds elements of a class type, such as string, that defines its own constructors, then the library uses the value type's default constructor to create the element initializer:
- NOTE:As we'll see in Chapter 12, some classes that define their own constructors do not define a default constructor. We cannot initialize a vector of such a type by specifying only a size; we must also specify an initial element value.
- There is a third possibility: The element type might be of a class type that does not define any constructors. In this case, the library still creates a value-initialized object. It does so by value-initializing each member of that object.
// EXE-3.3.1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main()
{
vector<int> ivec1; // ivec1 holds objects of type int
vector<int> ivec2(ivec1); // ok: copy elements of ivec1 into ivec2
//vector<string> svec(ivec1); // error: svec holds strings, not ints
vector<int> ivec4(10, -1); // 10 elements, each initialized to -1
vector<string> svec(10, "hi!"); // 10 strings, each initialized to "hi!"
cout << "The ivec4's content is:";
for (vector<int>::size_type index = 0; index < ivec4.size() ; ++index)
{
cout << ivec4[index] ;
}
cout << endl;
cout << "The svec's conent is:";
for (vector<string>::size_type index = 0 ; index < svec.size(); ++index)
{
cout << svec[index];
}
cout << endl;
return 0;
} - When we do not specify an element initializer, then the library creates a value initialized element initializer for us. This library-generated value is used to initialize each element in the container. The value of the element initializer depends on the type of the elements stored in the vector.
- The vector class defines several constructors (Section 2.3.3, p. 49), which we use to define and initialize vector objects. The constructors are listed in Table 3.4.
- 3.3.2. Operations on vectors
- The vector library provides various operations, many of which are similar to operations on strings.
- The size of a vector
- The empty and size operations are similar to the corresponding string operations (Section 3.2.3, p. 83). The size member returns a value of the size_type defined by the corresponding vector type.
- NOTE:To use size_type, we must name the type in which it is defined. A vector type always includes the element type of the vector:
vector<int>::size_type // ok
vector::size_type // error
- The empty and size operations are similar to the corresponding string operations (Section 3.2.3, p. 83). The size member returns a value of the size_type defined by the corresponding vector type.
- Adding Elements to a vector
- The push_back operation takes an element value and adds that value as a new element at the back of a vector. In effect it "pushes" an element onto the "back" of the vector
- Subscripting a vector
- Objects in the vector are not named. Instead, they can be accessed by their position in the vector. We can fetch an element using the subscript operator. Subscripting a vector is similar to subscripting a string
- The vector subscript operator takes a value and returns the element at that position in the vector. Elements in a vector are numbered beginning with 0. The following example uses a for loop to reset each element in the vector to 0:
- Like the string subscript operator, the vector subscript yields an lvalue so that we may write to it, which we do in the body of the loop. Also, as we do for strings, we use the size_type of the vector as the type for the subscript.
- Objects in the vector are not named. Instead, they can be accessed by their position in the vector. We can fetch an element using the subscript operator. Subscripting a vector is similar to subscripting a string
- Subscripting Does Not Add Elements
- Programmers new to C++ sometimes think that subscripting a vector adds elements; it does not:
vector<int> ivec; // empty vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // disaster: ivec has no elements
- NOTE:An element must exist in order to subscript it; elements are not added when we assign through a subscript.
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new element with value ix
- Programmers new to C++ sometimes think that subscripting a vector adds elements; it does not:
- Key Concept: Safe, Generic Programming
- Programmers coming to C++ from C or Java might be surprised that our loop used != rather than < to test the index against the size of the vector. C programmers are probably also suprised that we call the size member in the for rather than calling it once before the loop and remembering its value.
- C++ programmers tend to write loops using != in preference to < as a matter of habit. In this case, there is no particular reason to choose one operator or the other. We'll understand the rationale for this habit once we cover generic programming in Part II.
- Calling size rather than remembering its value is similarly unnecessary in this case but again reflects a good habit. In C++, data structures such as vector can grow dynamically. Our loop only reads elements; it does not add them. However, a loop could easily add new elements. If the loop did add elements, then testing a saved value of size would failure loop would not account for the newly added elements. Because a loop might add elements, we tend to write our loops to test the current size on each pass rather than store a copy of what the size was when we entered the loop.
- As we'll see in Chapter 7, in C++ functions can be declared to be inline. When it can do so, the compiler will expand the code for an inline function directly rather than actually making a function call. Tiny library functions such as size are almost surely defined to be inline, so we expect that there is little run-time cost in making this call on each trip through the loop.
// EXE-3.3.2.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main()
{
vector<int>::size_type a; // ok
//vector::size_type b; // error;argument list for class template "std::vector" is missing
// read words from the standard input and store them as elements in a vector
cout << "Please input strings:" << endl;
string word;
vector<string> text; // empty vector
while (cin >> word) {
text.push_back(word); // append word to text
}
cout << "All your input is:" << endl;
for (vector<string>::size_type index = 0; index < text.size(); ++index)
{
cout << text[index];
}
cout << endl;
vector<string> svector(10,"Hello world!");
cout << "Vector initialized values:" << endl;
for (vector<string>::size_type index = 0; index < svector.size(); ++index)
{
cout << index << "th element's value is " << svector[index] << endl;
}
cout << "Updating vector ..." << endl;
for (vector<string>::size_type index = 0; index < svector.size(); ++index)
{
svector[index] = string(index,'*');
}
cout << "Updated values:" << endl;
for (vector<string>::size_type index = 0; index < svector.size(); ++index)
{
cout << index << "th element's value is " << svector[index] << endl ;
}
vector<int> ivec; // empty vector
//for (vector<int>::size_type ix = 0; ix != 10; ++ix)
// ivec[ix] = ix; // disaster: ivec has no elements
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds new element with value ix
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
{
cout<< ix << "th element is " << ivec[ix] << endl;
}
return 0;
} - Programmers coming to C++ from C or Java might be surprised that our loop used != rather than < to test the index against the size of the vector. C programmers are probably also suprised that we call the size member in the for rather than calling it once before the loop and remembering its value.
- The vector library provides various operations, many of which are similar to operations on strings.
Section 3.4. Introducing Iterators
- While we can use subscripts to access the elements in a vector, the library also gives us another way to examine elements: We can use an iterator. An iterator is a type that lets us examine the elements in a container and navigate from one element to another
- The library defines an iterator type for each of the standard containers, including vector. Iterators are more general than subscripts: All of the library containers define iterator types, but only a few of them support subscripting. Because iterators are common to all containers, modern C++ programs tend to use iterators rather than subscripts to access container elements, even on types such as vector that support subscripting
- Caution: Only Subscript Elements that Are Known to Exist!
- It is crucially important to understand that we may use the subscript operator, (the [] operator), to fetch only elements that actually exist. For example,
vector<int> ivec; // empty vector
cout << ivec[0]; // Error: ivec has no elements!
vector<int> ivec2(10); // vector with 10 elements
cout << ivec[10]; // Error: ivec has elements 0...9 - Attempting to fetch an element that doesn't exist is a run-time error. As with most such errors, there is no assurance that the implementation will detect it. The result of executing the program is uncertain. The effect of fetching a nonexisting element is undefinedwhat happens will vary by implementation, but the program will almost surely fail in some interesting way at run time.
- This caution applies any time we use a subscript, such as when subscripting a string and, as we'll see shortly, when subscripting a built-in array.
- Attempting to subscript elements that do not exist is, unfortunately, an extremely common and pernicious programming error. So-called "buffer overflow" errors are the result of subscripting elements that don't exist. Such bugs are the most common cause of security problems in PC and other applications.
- It is crucially important to understand that we may use the subscript operator, (the [] operator), to fetch only elements that actually exist. For example,
- Container iterator Type
- Each of the container types, such as vector, defines its own iterator type: vector<int>::iterator iter;
- This statement defines a variable named iter, whose type is the type named iterator defined by vector<int>. Each of the library container types defines a member named iterator that is a synonym for the actual type of its iterator
- Each of the container types, such as vector, defines its own iterator type: vector<int>::iterator iter;
- Terminology: Iterators and Iterator Types
- When first encountered, the nomenclature around iterators can be confusing. In part the confusion arises because the same term, iterator, is used to refer to two things. We speak generally of the concept of an iterator, and we speak specifically of a concrete iterator type defined by a container, such as vector<int>.
- What's important to understand is that there is a collection of types that serve as iterators. These types are related conceptually. We refer to a type as an iterator if it supports a certain set of actions. Those actions let us navigate among the elements of a container and let us access the value of those elements.
- Each container class defines its own iterator type that can be used to access the elements in the container. That is, each container defines a type named iterator, and that type supports the actions of an (conceptual) iterator.
- When first encountered, the nomenclature around iterators can be confusing. In part the confusion arises because the same term, iterator, is used to refer to two things. We speak generally of the concept of an iterator, and we speak specifically of a concrete iterator type defined by a container, such as vector<int>.
- The begin and end Operations
- Each container defines a pair of functions named begin and end that return iterators.
- vector<int>::iterator iter = ivec.begin();
- This statement initializes iter to the value returned by the vector operation named begin.
- Assuming the vector is not empty, after this initialization, iter refers to the same element as ivec[0].
- This statement initializes iter to the value returned by the vector operation named begin.
- The iterator returned by the end operation is an iterator positioned "one past the end" of the vector. It is often referred to as the off-the-end iterator indicating that it refers to a nonexistent element "off the end" of the vector. If the vector is empty, the iterator returned by begin is the same as the iterator returned by end.
- NOTE:The iterator returned by the end operation does not denote an actual element in the vector. Instead, it is used as a sentinel indicating when we have processed all the elements in the vector.
- Each container defines a pair of functions named begin and end that return iterators.
- Dereference and Increment on vector Iterators
- The operations on iterator types let us retrieve the element to which an iterator refers and let us move an iterator from one element to another.
- Iterator types use the dereference operator (the * operator) to access the element to which the iterator refers: *iter = 0;
- The dereference operator returns the element that the iterator currently denotes. Assuming iter refers to the first element of the vector, then *iter is the same element as ivec[0].
- Iterators use the increment operator (++) (Section 1.4.1, p. 13) to advance an iterator to the next element in the container. Incrementing an iterator is a logically similar operation to the increment operator when applied to int objects. In the case of ints, the effect is to "add one" to the int's value. In the case of iterators, the effect is to "advance the iterator by one position" in the container. So, if iter refers to the first element, then ++iter denotes the second element.
- NOTE:Because the iterator returned from end does not denote an element, it may not be incremented or dereferenced.
- The operations on iterator types let us retrieve the element to which an iterator refers and let us move an iterator from one element to another.
- Other Iterator Operations
- Another pair of useful operations that we can perform on iterators is comparison: Two iterators can be compared using either == or !=. Iterators are equal if they refer to the same element; they are unequal otherwise.
- Another pair of useful operations that we can perform on iterators is comparison: Two iterators can be compared using either == or !=. Iterators are equal if they refer to the same element; they are unequal otherwise.
- A Program that Uses Iterators
- NOTE:This program, like the one on page 94, is safe if the vector is empty. If ivec is empty, then the iterator returned from begin does not denote any element; it can't, because there are no elements. In this case, the iterator returned from begin is the same as the one returned from end, so the test in the for fails immediately.
- const_iterator
- The previous program used a vector::iterator to change the values in the vector. Each container type also defines a type named const_iterator, which should be used when reading, but not writing to, the container elements.
- When we dereference a plain iterator, we get a nonconst reference (Section 2.5, p. 59) to the element. When we dereference a const_iterator, the value returned is a reference to a const (Section 2.4, p. 56) object. Just as with any const variable, we may not write to the value of this element.
- When we use the const_iterator type, we get an iterator whose own value can be changed but that cannot be used to change the underlying element value. We can increment the iterator and use the dereference operator to read a value but not to assign to that value.
- A const_iterator should not be confused with an iterator that is const. When we declare an iterator as const we must initialize the iterator. Once it is initialized, we may not change its value:
- A const_iterator may be used with either a const or nonconst vector, because it cannot write an element. An iterator that is const is largely useless: Once it is initialized, we can use it to write the element it refers to, but cannot make it refer to any other element
- NOTE:
// an iterator that cannot write elements
vector<int>::const_iterator
// an iterator whose value cannot change
const vector<int>::iterator
- The previous program used a vector::iterator to change the values in the vector. Each container type also defines a type named const_iterator, which should be used when reading, but not writing to, the container elements.
- 3.4.1. Iterator Arithmetic
- In addition to the increment operator, which moves an iterator one element at a time, vector iterators (but few of the other library container iterators) also support other arithmetic operations. These operations are referred to as iterator arithmetic, and include:
- iter + n ; iter – n: We can add or subtract an integral value to an iterator. Doing so yields a new iterator positioned n elements ahead of (addition) or behind (subtraction) the element to which iter refers. The result of the addition or subtraction must refer to an element in the vector to which iter refers or to one past the end of that vector. The type of the value added or subtracted ought ordinarily to be the vector's size_type or difference_type (see below).
- iter1 – iter2 : Computes the difference between two iterators as a value of a signed integral type named difference_type, which, like size_type, is defined by vector. The type is signed because subtraction might have a negative result. This type is guaranteed to be large enough to hold the distance between any two iterators. Both iter1 and iter2 must refer to elements in the same vector or the element one past the end of that vector.
- We can use iterator arithmetic to move an iterator to an element directly. For example, we could locate the middle of a vector as follows:
- This code initializes mid to refer to the element nearest to the middle of ivec. It is more efficient to calculate this iterator directly than to write an equivalent program that increments the iterator one by one until it reaches the middle element.
vector<int>::iterator mid = vi.begin() + vi.size() / 2;
- Beware:Any operation that changes the size of a vector makes existing iterators invalid. For example, after calling push_back, you should not rely on the value of an iterator into the vector.
// 3.4.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec(10,42);
cout << "Print all vec elements:" << endl;
for (vector<int>::const_iterator iter = ivec.begin();iter != ivec.end(); ++iter)
{
cout << *iter << endl;
}
cout << "Updating vec ..." << endl;
/*iter = ivec.begin();
while(iter != ivec.end() )
{
*iter++ = 5;
}*/
// equivalent loop using iterators to reset all the elements in ivec to 0
for ( vector<int>::iterator iter = ivec.begin();iter != ivec.end(); ++iter)
{
*iter = 0; // set element to which iter refers to 0
}
cout << "Print out updating:" <<endl;
for (vector<int>::const_iterator iter = ivec.begin();iter != ivec.end(); ++iter)
{
//*iter = 123; //error C3892: 'iter' : you cannot assign to a variable that is const
cout << *iter <<endl;
}
vector<int> nums(10); // nums is nonconst
const vector<int>::iterator cit = nums.begin();
*cit = 1; // ok: cit can change its underlying element
//++cit; // error: can't change the value of cit
//Issue: how to create the iterator of the const vector?
//const vector<int> nines(10, 9); // cannot change elements in nines and can't work with iterators?
//const vector<int>::iterator cit2 = nines.begin();
vector<int> nines(10, 9);
// error: cit2 could change the element it refers to and nines is const
const vector<int>::iterator cit2 = nines.begin();
// ok: it can't change an element value, so it can be used with a const vector<int>
vector<int>::const_iterator it = nines.begin();
//*it = 10; // error: *it is const
++it; // ok: it isn't const so we can change its value
return 0;
} - In addition to the increment operator, which moves an iterator one element at a time, vector iterators (but few of the other library container iterators) also support other arithmetic operations. These operations are referred to as iterator arithmetic, and include:
Section 3.5. Library bitset Type
- Some programs deal with ordered collections of bits. Each bit can contain either a 0 (off) or a 1 (on) value. Using bits is a compact way to keep yes/no information (sometimes called flags) about a set of items or conditions. The standard library makes it easy to deal with bits through the bitset class.
- To use a bitset we must include its associated header file. #include <bitset>
- 3.5.1. Defining and Initializing bitsets
- Like vector, the bitset class is a class template. Unlike vector, objects of type bitset differ by size rather than by type. When we define a bitset, we say how many bits the bitset will contain, which we do by specifying the size between a pair of angle brackets
-
- bitset<32> bitvec; // 32 bits, all zero
- The size must be a constant expression (Section 2.7, p. 62). It might be defined, as we did here, using an integral literal constant or using a const object of integral type that is initialized from a constant.
- This statement defines bitvec as a bitset that holds 32 bits. Just as with the elements of a vector, the bits in a bitset are not named. Instead, we refer to them positionally. The bits are numbered starting at 0. Thus, bitvec has bits numbered 0 through 31. The bits starting at 0 are referred to as the low-order bits, and those ending at 31 are referred to as high-order bits.
- The size must be a constant expression (Section 2.7, p. 62). It might be defined, as we did here, using an integral literal constant or using a const object of integral type that is initialized from a constant.
- Initializing a bitset from an unsigned Value
- When we use an unsigned long value as an initializer for a bitset, that value is treated as a bit pattern. The bits in the bitset are a copy of that pattern. If the size of the bitset is greater than the number of bits in an unsigned long, then the remaining high-order bits are set to zero. If the size of the bitset is less than that number of bits, then only the low-order bits from the unsigned value are used; the high-order bits beyond the size of the bitset object are discarded.
- Initializing a bitset from a string
- When we initialize a bitset from a string, the string represents the bit pattern directly. The bits are read from the string from right to left:
string strval("1100");
bitset<32> bitvec4(strval); - The bit pattern in bitvec4 has bit positions 2 and 3 set to 1, while the remaining bit positions are 0. If the string contains fewer characters than the size of the bitset, the high-order bits are set to zero.
- NOTE:The numbering conventions of strings and bitsets are inversely related: The rightmost character in the stringthe one with the highest subscriptis used to initialize the low-order bit in the bitsetthe bit with subscript 0. When initializing a bitset from a string, it is essential to remember this difference.
- We need not use the entire string as the initial value for the bitset. Instead, we can use a substring as the initializer:
- When we initialize a bitset from a string, the string represents the bit pattern directly. The bits are read from the string from right to left:
- Like vector, the bitset class is a class template. Unlike vector, objects of type bitset differ by size rather than by type. When we define a bitset, we say how many bits the bitset will contain, which we do by specifying the size between a pair of angle brackets
- 3.5.2. Operations on bitsets
- The bitset operations (Table 3.7) define various operations to test or set one or more bits in the bitset.
- Testing the Entire bitset
- The any operation returns true if one or more bits of the bitset object are turned onthat is, are equal to 1. Conversely, the operation none returns true if all the bits of the object are set to zero.
- If we need to know how many bits are set, we can use the count operation, which returns the number of bits that are set:
- The return type of the count operation is a library type named size_t. The size_t type is defined in the cstddef header, which is the C++ version of the stddef.h header from the C library. It is a machine-specific unsigned type that is guaranteed to be large enough to hold the size of an object in memory.
- The size operation, like the one in vector and string, returns the total number of bits in the bitset. The value returned has type size_t:
- The any operation returns true if one or more bits of the bitset object are turned onthat is, are equal to 1. Conversely, the operation none returns true if all the bits of the object are set to zero.
- Accessing the Bits in a bitset
- The subscript operator lets us read or write the bit at the indexed position.
- The subscript operator lets us read or write the bit at the indexed position.
- Setting the Entire bitset
- The set and reset operations can also be used to turn on or turn off the entire bitset object, respectively:
- The flip operation reverses the value of an individual bit or the entire bitset:
- The set and reset operations can also be used to turn on or turn off the entire bitset object, respectively:
- Retrieving the Value of a bitset
- The to_ulong operation returns an unsigned long that holds the same bit pattern as the bitset object. We can use this operation only if the size of the bitset is less than or equal to the size of an unsigned long:
- The to_ulong operation is intended to be used when we need to pass a bitset to a C or pre-Standard C++ program. If the bitset contains more bits than the size of an unsigned long, a run-time exception is signaled. We'll introduce exceptions in Section 6.13 (p. 215) and look at them in more detail in Section 17.1 (p. 688).
- The to_ulong operation returns an unsigned long that holds the same bit pattern as the bitset object. We can use this operation only if the size of the bitset is less than or equal to the size of an unsigned long:
- Printing the Bits
- Using the Bitwise Operators
- The bitset class also supports the built-in bitwise operators. As defined by the language, these operators apply to integral operands. They perform operations similar to the bitset operations described in this section. Section 5.3 (p. 154) describes these operators.
// EXE-3.5.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <bitset>
#include <iostream>
#include <string>
using namespace std;
int main()
{
bitset<32> bitvec; // 32 bits, all zero
cout << "All zero bitvec: " << bitvec <<endl;
//// bitvec1 is smaller than the initializer
bitset<16> bitvec1(0xffff); // bits 0 ... 15 are set to 1
cout << "Expected bitvec1: bits 0 ... 15 are set to 1:"
<< bitvec1 <<endl;
//// bitvec2 same size as initializer
bitset<32> bitvec2(0xffff); // bits 0 ... 15 are set to 1; 16 ... 31 are 0
cout << "Expected bitvec2: bits 0 ... 15 are set to 1; 16 ... 31 are 0:"
<< bitvec2 <<endl;
//// on a 32-bit machine, bits 0 to 31 initialized from 0xffff
bitset<128> bitvec3(0xffff); // bits 32 through 127 initialized to zero
//Issue: how to init the bitwise to 32 bits as 1;
cout << "Expected bitvec3: bits 32 through 127 initialized to zero:"
<< bitvec3 << endl;
///*The bit pattern in bitvec4 has bit positions 2 and 3 set to 1, while the remaining bit positions are 0. If the string contains fewer characters than the size of the bitset, the high-order bits are set to zero.
//*/
string strval("1100");
bitset<32> bitvec4(strval);
cout << "Expected bitvec4:1100-" << bitvec4 << endl;
/*Here bitvec5 is initialized by a substring of str starting at str[5] and continuing for four positions. As usual, we start at the rightmost end of this substring when initializing the bitset, meaning that bitvec5 is initialized with bit positions 0 through 3 set to 1100 while its remaining bit positions are set to 0. Leaving off the third parameter says to use characters from the starting position to the end of the string. In this case, the characters starting four from the end of str are used to initialize the lower four bits of bitvec6. The remainder of the bits in bitvec6 are initialized to zero.
*/
string str("1111111000000011001101");
bitset<32> bitvec5(str, 5, 4); // 4 bits starting at str[5], 1100
bitset<32> bitvec6(str, str.size() - 4); // use last 4 characters
cout << "Expected bitvec5:1100-" << bitvec5 << endl;
cout << "Expected bitvec6:1101-" << bitvec6 << endl;
bitset<32> bitvec7; // 32 bits, all zero
bool is_set = bitvec7.any(); // false, all bits are zero
bool is_not_set = bitvec7.none(); // true, all bits are zero
cout << "bitvec7:" << bitvec7 <<endl;
cout << "bitvec7: bitvec7.any() =" << bitvec7.any() << endl;
cout << "bitvec7: bitvec7.none() =" << bitvec7.none() << endl;
size_t bits_set = bitvec.count(); // returns number of bits that are on
size_t sz = bitvec7.size(); // returns 32
cout << "bitvec7: bitvec7.count() =" << bitvec7.count() << endl;
cout << "bitvec7: bitvec7.size() =" << bitvec7.size() << endl;
// assign 1 to even numbered bits
for (int index = 0; index != 32; index += 2)
bitvec7[index] = 1;
cout << "bitvec7:assign 1 to even numbered bits:" << bitvec7 << endl;
// equivalent loop using set operation
for (int index = 0; index != 32; index += 2)
bitvec.set(index);
cout << "bitvec7:equivalent loop using set operation:" << bitvec7 << endl;
for ( int index = 0; index != bitvec7.size(); ++index)
{
if (bitvec7.test(index))
{
cout << "bitvec7[" << index << "] is on" << endl;
}
}
// equivalent test using subscript
for ( int index = 0; index != bitvec7.size(); ++index)
{
if (!bitvec7[index])
{
cout << "bitvec7[" << index << "] is off" << endl;
}
}
//bitvec.reset(); // set all the bits to 0.
bitvec7.set();
cout << "bitvec7 set all on:" << bitvec7 << endl;
//bitvec.set(); // set all the bits to 1
bitvec7.reset();
cout << "bitvec7 set all off:" << bitvec7 << endl;
bitvec7.flip(0);
//bitvec[0].flip(); // also reverses the first bit
cout << "bitvec7 reverses value of first bit:" << bitvec7 << endl;
bitvec7.flip();
cout << "bitvec7 reverses value of all bits:" << bitvec7 << endl;
unsigned long ulong = bitvec7.to_ulong();
cout << "bitvec7 ulong = " << ulong << endl;
bitset<32> bitvec8(0xffff); // bits 0 ... 15 are set to 1; 16 ... 31 are 0
cout << "bitvec8: " << bitvec8 << endl;
return 0;
} - The bitset operations (Table 3.7) define various operations to test or set one or more bits in the bitset.
Exercise 3.4:
// Exercise 3.4.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>;
#include <iostream>;
using namespace std;
using std::string;
string s;
int main()
{
/*
What are the values of s and s2?
string s;
int main() {
string s2; }
*/
string s2;
cout << "The string s is \"" << s << "\"" << endl;
cout << "The string s2 is \"" << s2 << "\"" << endl;
/*
string s1;
Default constructor; s1 is the empty string
string s2(s1);
Initialize s2 as a copy of s1
string s3("value");
Initialize s3 as a copy of the string literal
string s4(n, 'c');
Initialize s4 with n copies of the character 'c'
*/
s2 = "s2 string";
string s3 (s2);
cout << "The string s3 is '" << s3 << "'" <<endl;
string s4 ("s4 string");
cout << "The string s4 is '" << s4 << "'" << endl;
string s5(10,'A');
cout << "The string s5 is '" << s5 << "'" << endl;
return 0;
}
Exercise 3.5:
// Exercise 3.5.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
//Write a program to read the standard input a line at a time. Modify your program to read a word at a time.
string sInput;
cout << "Read from console a line at a time! Please try!" << endl;
while( getline(cin,sInput))
{
cout << sInput << endl;
}
if(!(cin))
{
cout << "Skip the ctrl+Z (^Z) from getline, clear the cin" << endl;
cin.clear();
}
cout << "Read from console a word at a time! Please try!" << endl;
while( cin >> sInput )
{
cout << sInput << endl;
}
return 0;
}
Exercise 3.7:
// Exercise 3.7.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
/*
Write a program to read two strings and report whether the strings are equal. If not, report which of the two is the larger. Now, change the program to report whether the strings have the same length and if not report which is longer.
*/
cout << "Please input two Strings ans split by backspace:" << endl;
string s1, s2;
cin >> s1 >> s2;
cout << "Your first string is '" << s1 << "'" << " the length is " << s1.size() << endl;
cout << "Your second string is '" << s2 << "'" << " the length is " << s2.size() << endl;
if(s1 > s2 )
{
cout << "The first is bigger than the second!" << endl;
}
else
{
cout << "The first is less than the second!" << endl;
}
if(s1.size() == s2.size())
{
cout << "The two string has the same length:" << s1.size() <<endl;
}
else if(s1.size() > s2.size())
{
cout << "The first is longer than the second!" <<endl;
}
else
{
cout << "The second is longer than the first!" << endl;
}
return 0;
}
Exercise 3.8:
// Exercise 3.8.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
/*
Write a program to read strings from the standard input, concatenating what is read into one large string. Print the concatenated string. Next, change the program to separate adjacent input strings by a space.
*/
cout << "Please input strings:" <<endl;
const string SPLITER(" ");
string allInput,input;
while(cin >> input)
{
allInput += input + SPLITER;
}
cout << "All your input is :" << allInput << endl;
return 0;
}
Exercise 3.9:
// Exercise 3.9.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
/*
What does the following program do? Is it valid? If not, why not?
string s;
cout << s[0] << endl;
*/
string s;
// The string is empty so there is no the zero index char in it.
//cout << s[0] << endl; // error with the index out of range
string s2("9");
cout << s2[0] << endl; //works fine!
return 0;
}
Exercise 3.10:
// Exercise 3.10.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
/*
Write a program to strip the punctuation from a string. The input to the program should be a string of characters including punctuation; the output should be a string in which the punctuation is removed.
*/
cout << "Please input string with punctuation:" << endl;
string input;
string result;
if(getline(cin,input))
{
for (string::size_type index = 0; index < input.size(); ++index)
{
if(!ispunct(input[index]))
{
result += input[index];
//cout << input[index];
}
}
}
cout << result ;
cout << endl;
return 0;
}
Exercise 3.11:
Exercise 3.12:
// Exercise 3.11.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main()
{
/*
Exercise 3.11:
Which, if any, of the following vector definitions are in error?
(a) vector< vector<int> > ivec;
(b) vector<string> svec = ivec;
(c) vector<string> svec(10, "null");
*/
vector< vector<int> > ivec;
//vector<string> svec1 = ivec; // error C2440: 'initializing' : cannot convert from 'std::vector<_Ty>' to 'std::vector<_Ty>'
vector<string> svec20(10, "null");
/*
Exercise 3.12:
How many elements are there in each of the following vectors? What are the values of the elements?
(a) vector<int> ivec1;
(b) vector<int> ivec2(10);
(c) vector<int> ivec3(10, 42);
(d) vector<string> svec1;
(e) vector<string> svec2(10);
(f) vector<string> svec3(10, "hello");
*/
vector<int> ivec1; // zero
cout << "The ivec1's size is " << ivec1.size() << endl;
cout << endl;
vector<int> ivec2(10); // 10 and every element is 0;
cout << "The ivec2's size is " << ivec2.size() << endl;
for (vector<int>::size_type index = 0; index < ivec2.size(); ++index)
{
cout << index << "th element is " << ivec2[index] << endl;
}
cout << endl;
vector<int> ivec3(10, 42); // 10 and every element is 42;
cout << "The ivec3's size is " << ivec3.size() << endl;
for (vector<int>::size_type index = 0; index < ivec3.size(); ++index)
{
cout << index << "th element is " << ivec3[index] << endl;
}
cout << endl;
vector<string> svec1; // zero;
cout << "The svec1's size is " << svec1.size() << endl;
cout << endl;
vector<string> svec2(10); // 10 and every element is empty;
cout << "The svec2's size is " << svec2.size() << endl;
for (vector<string>::size_type index = 0; index < svec2.size(); ++index)
{
cout << index << "th element is " << svec2[index] << endl;
}
cout << endl;
vector<string> svec3(10, "hello"); // 10 and every element is "hello"
cout << "The svec3's size is " << svec3.size() << endl;
for (vector<string>::size_type index = 0; index < svec3.size(); ++index)
{
cout << index << "th element is " << svec3[index] << endl;
}
return 0;
}
Exercise 3.13:
// Exercise 3.13.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
/*
Read a set of integers into a vector. Calculate and print the sum of each pair of adjacent elements in the vector. If there is an odd number, tell the user and print the value of the last element without summing it. Now change your program so that it prints the sum of the first and last elements, followed by the sum of the second and second-to-last and so on.
*/
cout << "Please input a list of integers:" << endl;
int input;
vector<int> iVector;
while(cin >> input)
{
iVector.push_back(input);
}
if( 0 != (iVector.size() % 2 ) )
{
cout << "The number of Your inputs is odd number!" << endl;
}
else
{
for (vector<int>::size_type index = 0 ; index != iVector.size()/2; ++index)
{
cout << index << "th pair numbers' sum is " << iVector[index] + iVector[iVector.size() -1 - index] << endl;
}
}
return 0;
}
Exercise 3.14:
// Exercise 3.14.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
using namespace std;
int main()
{
/*
Read some text into a vector, storing each word in the input as an element in the vector. transform each word into uppercase letters. Print the transformed elements from the vector, printing eight words to a line.
*/
cout << "Please input letters:" << endl;
string input;
vector<string> iVector;
while (cin >> input)
{
for (string::size_type index = 0; index != input.size(); ++index)
{
input[index] = toupper(input[index]);
}
iVector.push_back(input);
}
cout << "All your input is:" << endl;
for (vector<string>::size_type index = 0; index != iVector.size(); ++index)
{
//cout << iVector[index];
cout << index << "th word is " << iVector[index] << endl;
}
cout << endl;
return 0;
}
Exercise 3.15:
// Exercise 3.15.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
/*
Is the following program legal? If not, how might you fix it?
vector<int> ivec;
ivec[0] = 42; // there is no the 0th element in vector;
*/
vector<int> ivec(1);
ivec[0] = 42;
cout << ivec[0] << endl;
return 0;
}
Exercise 3.16:
// Exercise 3.16.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
/*
List three ways to define a vector and give it 10 elements, each with the value 42. Indicate whether there is a preferred way to do so and why.
*/
vector<int> iVector(10,42);
vector<int> iVector1(10);
for (vector<int>::size_type index = 0; index != iVector1.size(); ++index)
{
iVector1[index] = 42;
}
vector<int> iVector2;
for (vector<int>::size_type index =0; index != 10; ++index)
{
iVector2.push_back(42);
}
return 0;
}
Exercise 3.17:
Exercise 3.18:
// Exercise 3.18.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
/*
Write a program to create a vector with 10 elements. Using an iterator, assign each element a value that is twice its current value.
*/
vector<int> iVector(10);
int counter(0);
for (vector<int>::iterator iter = iVector.begin(); iter != iVector.end();++iter)
{
*iter = counter;
counter++;
}
for (vector<int>::iterator iter=iVector.begin(); iter != iVector.end();++iter)
{
*iter = *iter * 2;
}
for (vector<int>::const_iterator iter = iVector.begin(); iter!= iVector.end();++iter)
{
cout << *iter <<endl;
}
return 0;
}
Exercise 3.19:
Exercise 3.20:
Exercise 3.21:
Exercise 3.22:
// Exercise 3.22.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
/*
What happens if we compute mid as follows:
vector<int>::iterator mid = (vi.begin() + vi.end()) / 2;
*/
vector<int> iVector(10,20);
//vector<int>::iterator mid = (iVector.begin() + iVector.end())/2; //error C2678: binary '+' : no operator found which takes a left-hand operand of type 'std::_Vector_iterator<_Myvec>' (or there is no acceptable conversion)
//vector<int>::iterator mid = (iVector.begin() + 6)/2; // error C2676: binary '/' : 'std::_Vector_iterator<_Myvec>' does not define this operator or a conversion to a type acceptable to the predefined operator
vector<int>::iterator mid = iVector.begin();
cout << *mid << endl;
return 0;
}
Exercise 3.23:
// Exercise 3.23.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <bitset>
#include <iostream>
using namespace std;
int main()
{
// Explain the bit pattern each of the following bitset objects contains:
//
//(a) bitset<64> bitvec(32);
//(b) bitset<32> bv(1010101);
//(c) string bstr; cin >> bstr; bitset<8>bv(bstr);
bitset<64> bitvec(32);
cout << "bitvec:" << bitvec << endl;
//0000000000000000000000000000000000000000000000000000000000100000
bitset<32> bv1(1010101);
cout << "bv1:" << bv1 <<endl;
// 00000000000011110110100110110101
string bstr;
cin >> bstr;
bitset<8>bv2(bstr);
cout << "bv2:" << bv2 <<endl;
return 0;
}
Exercise 3.24:
// Exercise 3.24.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <bitset>
#include <string>
#include <iostream>
using namespace std;
int main()
{
/*
Consider the sequence 1,2,3,5,8,13,21. Initialize a bitset<32> object that has a one bit in each position corresponding to a number in this sequence. Alternatively, given an empty bitset, write a small program to turn on each of the appropriate bits.
*/
bitset<32> bitvec;
for ( int index = 0; index != bitvec.size(); ++index)
{
if((index == 1) || (index == 2) || (index == 3) || (index == 5)
|| (index == 8) || (index == 13) || (index == 21))
{
//1,2,3,5,8,13,21
bitvec[index] = 1;
}
}
cout << bitvec <<endl;
bitset<32> bitvec2;
bitvec2.set();
cout << bitvec2 << endl;
return 0;
}
No comments:
Post a Comment