[C++ Primer Reading Notes] Day 5

文章目录

  • Scope
  • Review
  • Notes
    • 1. not to use using declarations inside headers
    • 2. about type
      • 2.1 several ways of initilization a string
        • 2.1.1 direct initialization
        • 2.1.2 copy initialization
        • 2.1.3 list initialization
        • 2.1.4 default initialization
        • 2.1.5 constructor initialization
      • 2.2 reading and writing operators of string
      • 2.3 the string::size_type type
      • 2.4 the difference of strings literals and strings
      • 2.5 C library headers used in C++
      • 2.6 range-based for used to access characters in a string
      • 2.7 subscript used to access characters in a string
      • 2.8 subscript used in iterator to access characters in a string
    • 3. about vector
      • 3.1 the concpet of the vector
      • 3.2 several ways of initialization of a vector
        • 3.2.1 list initialization
        • 3.2.2 constructor initialization
        • 3.2.3 copy initialization
        • 3.2.4 direct initialization
        • 3.2.5 default initialization
        • 3.2.6 distinguish between these form
      • 3.3 adding elements to a vector
      • 3.4 using subscript in a vector
  • Word List

Scope

Chapter 3.1-3.3

Review

Chapter 3.1-3.3 mainly fouce on two important library types which are string and vector. They are very similar in supporting variable-size objects. String supports variable-length character strings. Vector defines variable-size collections.

Notes

1. not to use using declarations inside headers

Code inside headers ordinarily should not use using declarations. The reason is that the contents of a header are copied into the including program’s text. If a header has a using declaration, then every program that includes that header gets that same using declaration. As a result, a program that didn’t intend to use the specified library name might encounter unexpected name conflicts.

Caution:
We can use #include in header files, but no using. The preprocessor and its head guards would make it sefe for multiple #includes in our program. For example, the string.h uses the head guard. However, there is no such thing to protect multiple usings in our program.

2. about type

2.1 several ways of initilization a string

2.1.1 direct initialization

string str1("hello");

2.1.2 copy initialization

When we initialize a variable using =, we are asking the compiler to copy initialize the object by copying the initializer on the right-hand side into the object being created.

That is, the “=” exactly means copy.

string str2 = "hello";
string str3(str2);

2.1.3 list initialization

string str4{"hello"};
string str5={"hello"};
string str6{'h','e','l','l','o'};
string str7={'h','e','l','l','o'};

2.1.4 default initialization

The result of default initialization of a string is an empty string.

string str8;

2.1.5 constructor initialization

string str9(7,'a');

Caution:
All the ways mention above would add a ‘\0’ at the end of each new string automatically. In the new standard of C++11, a string doesnot necessarily end with a ‘\0’, but most of the compilers still keep this convention.

2.2 reading and writing operators of string

we use the iostream library to read and write values of built-in types such as int, double, and so on. We use the same IO operators to read and write strings.

There are two kinds of operators we can use to read and write string.

The first kind is the same to other built-in types: cin>> and cout<<

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.

That is, it would read the characters between two whitespace characters. It can’t read a whole line that contains whitespaces. It keeps reading until it meets a whitespace character.

int main()
{
	string word;
	while (cin >> word) // read until end-of-file
	cout << word << endl; // write each word followed by a new line
	return 0;
}

The other kind is getline which can read a whole line that contains whitespaces

Sometimes we do not want to ignore the whitespace in our input. In such cases, we can use the getline function instead of the >> operator. This function reads the given stream up to and including the first newline and stores what it read—not including the newline—in its string argument. After getline sees a newline, even if it is the first character in the input, it stops reading and returns. If the first character in the input is a newline, then the resulting string is the empty string.

That is, it keeps reading until it meets a newline character. It don’t read newline character.

int main()
{
	string line;
	// read input a line at a time until end-of-file
	while (getline(cin, line))
	cout << line << endl;  
	return 0;
}

When we print multiple lines, just like the above program, we have to write a newline on our own , because the string readed by getline doesn’t contain a newline.

2.3 the string::size_type type

It might be logical to expect that size returns an int or an unsigned. Instead, size returns a string::size_type value.

The string class—and most other library types—defines several companion types. These companion types make it possible to use the library types in a machineindependent manner. The type size_type is one of these companion types.

Caution:
The type of a variable which can hold the return of size() is not ints, instead, it is a companion type of string–string::size_type. But we can take this kind as an unsigned. We have to be careful when we use the return of size() to calculate with other signed types.

You can avoid problems due to conversion between unsigned and int by not using ints in expressions that use size()

If we don’t bother to specify the string::size_type type, we can also use decltype:

string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0;

2.4 the difference of strings literals and strings

When we mix strings and string or character literals, at least one operand to each + operator must be of string type

That is, we can’t add string literals.

For historical reasons, and for compatibility with C, string literals are not standard library strings. It is important to remember that these types differ when you use string literals and library strings.

It explain the reason why we can’t add string literals. This is because string literal is a different type from string, whose operations are different too. For string literal type, + operation can not be used.

2.5 C library headers used in C++

In addition to facilities defined specifically for C++, the C++ library incorporates the C library. Headers in C have names of the form name .h. The C++ versions of these headers are named c name—they remove the .h suffix and precede the name with the letter c. The c indicates that the
header is part of the C library.

For example, the ctype.h file in C can be used in C++ in the form of cctype:

#include 

2.6 range-based for used to access characters in a string

The range-based for can be applied in two ways to s string.

The first way is used to process every character in the string (only traversing, not changing). The loop control variable have to be defined as char. If we don’t want to specify a type name, we can use auto:

string str("some string");
// print the characters in str one character to a line
for (auto c : str) // for every char in str
	cout << c << endl; 

The second way is used to change the characters while traversing a string. The loop control variable must be defined as a reference type in the range-based for loop.

If we want to change the value of the characters in a string, we must define the loop variable as a reference type
When we use a reference as our control variable, that variable is bound to each element in the sequence in turn.

string s("Hello World!!!");
// convert s to uppercase
for (auto &c : s) // for every char in s (note: c is a reference)
	c = toupper(c); // c is a reference, so the assignment changes the char in s
cout << s << endl;

But in this way can’t access the single character we want to change.

2.7 subscript used to access characters in a string

With the use of subscript, we can change the character in a string.

There are two ways to access (change) individual characters in a string: We can use a subscript or an iterator.

The subscript operator (the [ ] operator) takes a string::size_type value that denotes the position of the character we want to access. The operator returns a reference to the character at the given position.

That is to say, the return of a subscript is actually a reference, therefore we can chage that character bound to that reference.

The value in the subscript is referred to as “a subscript” or “an index.” The index we supply can be any expression that yields an integral value. However, if our index has a signed type, its value will be converted to the unsigned type that string::size_type represents

The number that is not string::size_type would be converted automatically to string::size_type. Therefore it is ok to use an int as an index.

Caution:

Any time we use a subscript, we must ensure that there is a value at the given location.

if (!s.empty()) // make sure there's a character to print
	cout << s[0] << endl; // print the first character in s

2.8 subscript used in iterator to access characters in a string

We can also use an iterator (for iterator) with the subscript to change the character in a string. It should be noted that a for iterator is different from a round-based for.

// process characters in s until we run out of characters or we hit a whitespace
for (decltype(s.size()) index = 0;
	index != s.size() && !isspace(s[index]); ++index)
	s[index] = toupper(s[index]); // capitalize the current character

The && operator used in the for loop is very clever.

This operator yields true if both operands are true and false otherwise.
In this case, we are guaranteed that we will not subscript s unless we know that index is in range.

3. about vector

3.1 the concpet of the vector

A vector is a collection of objects, all of which have the same type. Every object in the collection has an associated index, which gives access to that object.

Like string, we have to #include vector before we use it. The name “vector” is also included in std namespace.

#include 
using std::vector;

A vector is a class template. C++ has both class and function templates.
Templates are not themselves functions or classes. Instead, they can be thought of as instructions to the compiler for generating classes or functions. The process that the compiler uses to create classes or functions from templates is called instantiation. When we use a template, we specify what kind of class or function we want the compiler to instantiate.

Therefore, vector belongs to class template, which is different from function templates.

For a class template, we specify which class to instantiate by supplying additional information.
In the case of vector, the additional information we supply is the type of the objects the vector will hold

vector<int> ivec; // ivec holds objects of type int
vector<Sales_item> Sales_vec; // holds Sales_items
vector<vector<string>> file; // vector whose elements are vectors

Caution:

vector is a template, not a type.

That is to say, we can’t use a vector name where a type name is expected to be placed.

We can define vectors to hold objects of most any type. Because references are not objects, we cannot have a vector of references.
In particular, we can have vectors whose elements are themselves vectors.

3.2 several ways of initialization of a vector

the most common way of using vectors is to define an initially empty vector to which elements are added as their values become known at run time

vector<int> ivec;  // initially empty

3.2.1 list initialization

we can supply a list of element values only by using list initialization in which the initializers are enclosed in curly braces. We cannot supply a list of initializers using parentheses

vector<string> v1{"a", "an", "the"}; // list initialization
vector<string> v2("a", "an", "the"); // error
vector<int> ivec{10};  // one element with value 10

3.2.2 constructor initialization

We can also initialize a vector from a count and an element value.

vector<int> ivec(10, -1); // ten int elements, each initialized to -1
vector<string> svec(10, "hi!"); // ten strings; each element is "hi!
vector<string> svec{10, "hi!"}; // ten strings; each element is "hi!

3.2.3 copy initialization

vector<int> ivec2 = ivec;  // initializer with the copy initialization form
vector<int> ivec2(ivec); 

3.2.4 direct initialization

We can usually omit the value and supply only a size. In this case the library creates a value-initialized element initializer for us. The values of all elements would be default initialized.

If the vector holds elements of a built-in type, such as int, then the element initializer has a value of 0. If the elements are of a class type, such as string, then the element initializer is itself default initialized.

vector<int> ivec(10);  // ten int elements

Caution:

when we use the copy initialization form (i.e., when we use =), we can supply only a single initializer

we can supply only a single initializer; and when we supply an in-class initializer

3.2.5 default initialization

The result of default initialization of a vector is an empty vector.

vector<int> ivec;

3.2.6 distinguish between these form

In a few cases, what initialization means depends upon whether we use curly braces or parentheses to pass the initializer(s).

vector<int> v1(10); // v1 has ten elements with value 0
vector<int> v2{10}; // v2 has one element with value 10
vector<int> v3(10, 1); // v3 has ten elements with value 1
vector<int> v4{10, 1}; // v4 has two elements with values 10 and 1

The key is:

When we use curly braces, {…}, we’re saying that, if possible, we want to list initialize the object.
Only if it is not possible to list initialize the object will the other ways to initialize the object be considered.

Caution:
Sometimes the braces form can be seen as the parentheses form. However, there is no way that the parenthese form can be seen as the braces form. For example:

vector<string> v6("hi"); // error: can't construct a vector from a string literal

3.3 adding elements to a vector

When the size of vector will be very large or unknown,

it is better to create an empty vector and use a vector member named push_back to add elements at run time.

Caution:

we cannot use a range for if the body of the loop adds elements to the vector.

This is because

The body of a range for must not change the size of the sequence over which it is iterating.

It is a restriction for range-based for, not for normal iterator, but better avoid this for both the two situation.

3.4 using subscript in a vector

Just like string, we can use the subscript to access a element in the vector. And the result of using subscript is also a reference.

vector<int> v{1,2,3,4,5,6,7,8,9};
for (auto &i : v) // for each element in v (note: i is a reference)
	i *= i; // square the element value
for (auto i : v) // for each element in v
	cout << i << " "; // print the element

Caution:

The subscript operator on vector (and string) fetches an existing element; it does not add an element

Word List

subscript 下标
instantiation 实例化

你可能感兴趣的:(C++)