===================
== 颢阳's Blog ==
===================
blog for everything

Cpp Primer Chapter 3

Posted on in book • 2180 words • 11 minute read
Tags: cpp, cpp primer

Namespace using Declarations

A using declaration lets us use a name from a namespace without qualifying the name with a namespace_name::prefix. A using declaration has the form:

using namespace::name;

For example:

#include <iostream>
using std::cin;
int main() {
  int i;
  cin >> i; // ok: cin is a synonym for std::cin
  cout << i; // error: no using declaration
  std::cout << i; // ok
  return 0;
}

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.

Library string Type

We can define a string in following ways:

string s1; // ""
string s2 = s2; // copy
string s3 = "hiya"; // literal
string s4(10, 'c'); // "cccccccccc"
string s5("hiya"); // direct init
string s6(s2); // s6 is a copy of s2

If we use =, then we are doing copy initialization. If we don’t use =, like s4, we are using direct initializaiton.

Read in string

Reading an unknown number of strings:

int main() {
  string word;
  while (cin >> word) // read unitl end-of-file
    cout << word << "+" << endl;  
  return 0;
}

This will read until the end of line, and delimited by space or newline.

Given input:

a c
b

c

Ouptut:

a+
c+
b+
c+

Using getline to read an entire line:

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

This will read until there is a new line, and the space in each line remains.

Output (with previous input):

a c+
b+
+
c+

Adding Literals and strings

When we mix strings and string or character literals, at least one operand to each + operator must be of string type: string s = "hello" + "world!"; is wrong, because there is no string operand. string s = "hello" + ", " + s2; is also wrong, since there is no string operand between "hello" and ",".

How whitespace characters are handled in the string input operator and in the getline function.

  • For code like is >> s, input is separated by whitespaces while reading into string s.
  • For code like getline(is, s) input is separated by newline \n while reading into string s. Other whitespaces are ignored.
  • For code like getline(is, s, delim)input is separated by delim while reading into string s. All whitespaces are ignored.

Library vector Type

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. We supply it inside a pair of angle brackets following the template’s name: #include <vector>;

Because references are not objects, we cannot have a vector of references.

Defining and Initializing vector

vector<int> ivect; // initially empty
vector<int> ivec2(ivect); // copy elements of ivec to ivec2
vector<int> ivec3 = ivec; // copy elements of ivec to ivec3
vector<string> articles = {"a", "an", "the"}; // with string literal

We cannot supply a list of initializers using parentheses:

vector<string> v1{"a", "an", "the"}; // ok: using curly braces
vector<string> v2("a", "an", "the"); // error: cannot use parentheses
vector<string> v3{10, "hi"}; // equal to v3(10, "hi");

When we use parentheses, we are saying that the values we supply are to be used to construct the object. When we use curly braces, {…}, we’re saying that, if possible, we want to list initialize the object.

Introducing Iterators

In the case of an iterator, that object is an element in a container or a character in a string. We can use an iterator to fetch an element and iterators have operations to move from one element to another. As with pointers, an iterator may be valid or invalid. A valid iterator either denotes an element or denotes a position one past the last element in a container. All other iterator values are invalid.

Using iterators

Unlike pointers, we do not use the address-of operator to obtain an iterator. Instead, types that have iterators have members that return iterators. In particular, these types have members named begin and end. The begin member returns an iterator that denotes the first element (or first character), if there is one:

auto b = v.begin(), e = v.end(); // b and e are the same type

The iterator returned by end is an iterator positioned “one past the end” of the associated container (or string). This iterator denotes a nonexistent element “off the end” of the container. It is used as a marker indicating when we have processed all the elements. The iterator returned by end is often referred to as the off-the-end iterator or abbreviated as “the end iterator.” If the container is empty, begin returns the same iterator as the one returned by end.

As with pointers, we can dereference an iterator to obtain the element denoted by an iterator:

string s("some string");
if (s.begin() != s.end()) { // make sure s is not empty
  auto it = s.begin();
  *it = toupper(*it);
}

Common Iterators Operations

Imgur

To let us ask specifically for the const_iterator type, the new standard introduced two new functions named cbegin and cend:

vector<int> v;
auto it = v.cbegin(); // it has type vector<int>::const_iterator

To simplify the operation of (*it).empty(), c++ introduces a shortcut it -> empty(), and the arrow sign can be applied directly to pointer.

Using Iterator Arithmetic

Using iterator for binary search:

auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg) / 2;
while (mid != end && *mid != sought) {
  if (sought < *mid) 
    end = mid;
  else
    beg = mid + 1;
  mid = beg + (end - beg) / 2;
}

Arrays

An array is a data structure that is similar to the library vector type but offers a different trade-off between performance and flexibility. Like a vector, an array is a container of unnamed objects of a single type that we access by position. Unlike a vector, arrays have fixed size; we cannot add elements to an array. Because arrays have fixed size, they sometimes offer better run-time performance for specialized applications. However, that run-time advantage comes at the cost of lost flexibility.

Defining and Initializing Build-in Arrays

It requires a constexpr to define an array’s size:

unsigned cnt = 42;
constexpr unsigned sz = 42;
int arr[10]; // ok
int *parr[sz]; // ok: array of int pointer
string bad[cnt]; // error: cnt isn't constant expression
string strs[get_size()]; // ok if get_size() return a constexpr

Define an array with literals:

const unsigned sz = 3;
int al[sz] = {0, 1, 2};
int a2[] = {0, 1, 2};
int a3[5] = {0, 1, 2}; // ok: equivalent to a3[] = {0, 1, 2, 0, 0};
string a4[3] = {"hi", "bye"}; // ok: equivalent to a4[] = {"hi", "bye", ""};
int a5[2] = {0, 1, 2}; // error: to many initializers

Since String is an array of char, it has its own way of definition:

char a1[] = {'C', '+', '+'}; // list init, no null
char a2[] = {'C', '+', '+', '\0'}; // list init, explicit null
char a3[] = "C++"; // null terminator added automatically
const char a4[6] = "Daniel"; // error: no space for the null

We cannot initialize an array as a copy of another array, nor is it legal to assign one array to another:

int a[] = {0, 1, 2}; // array of three ints
int a2[] = a; // error: cannot initialize one array with another 
a2 = a; // error: cannot assign one array to another

Default values when initialize:

string sa[10];      //all elements are empty strings
int ia[10];         //all elements are 0

int main() {
    string sa2[10]; //all elements are empty strings
    int ia2[10];    //all elements are undefined
}

Pointers and Arrays

Arrays have a special property—in most places when we use an array, the compiler automatically substitutes a pointer to the first element:

string *p2 = nums; // equivalent to p2 = &nums[0]

Pointers are iterators

Using pointers to write a loop to print the elements in arr as follows:

int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = arr; // pointing at the begining of the arr
++p; // pointing to the second element: for demo purpose
int *e = &arr[10]; // pointing at the end of the arr
for (int *b = arr; b != e; ++b) {
  cout << *b << endl;
}

The libray begin() and end() functions

int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints 
int *beg = begin(ia); // pointer to the first element in ia
int *last = end(ia); // pointer one past the last element in ia

begin returns a pointer to the first, and end returns a pointer one past the last element in the given array: These functions are defined in the iterator header.

So we can use these two library function to iterate the arr:

// pbeg points to the first and pend points just past the last element in arr 
int *pbeg = begin(arr), *pend = end(arr);
// find the first negative element, stopping if we’ve seen all the elements 
while (pbeg != pend && *pbeg >= 0)
	++pbeg;

The result of subtracting two pointers is a library type named ptrdiff_t. Like size_t, the ptrdiff_t type is a machine-specific type and is defined in the cstddef header. Because subtraction might yield a negative distance, ptrdiff_t is a signed integral type.

auto n = end(arr) - begin(arr);

Comparing two vectors:

vector<int> vec1 = { 0, 1, 2 };
vector<int> vec2 = { 0, 1, 2 };

if (vec1 == vec2) // will compare all the values in two vectors
  cout << "The two vectors are equal." << endl;
else
  cout << "The two vectors are not equal." << endl;

C library String Functions

The Standard C library provides a set of functions, listed in Table 3.8, that operate on C-style strings. These functions are defined in the cstring header, which is the C++ version of the C header string.h.

Imgur

An easy code snippet for comparing two C-style string:

const char *cs1 = "abc";
const char *cs2 = "def";
auto result = strcmp(cs1, cs2);
if (result == 0)
  cout << "same strings" << endl;
else if (result < 0)
  cout << "abc < def" << endl;
else
  cout << "abc > def" << endl;

Interfacing to Older Code

Many C++ programs predate the standard library and do not use the string and vector types. Moreover, many C++ programs interface to programs written in C or other languages that cannot use the C++ library. Hence, programs written in modern C++ may have to interface to code that uses arrays and/or C-style character strings. The C++ library offers facilities to make the interface easier to manage.

Transform a string to char array:

string s("hello world");
char *str = s; // error:can’t initialize a char* from a string
const char *str = s.c_str(); // ok

Transform any array to vector:

int int_arr[] = {0, 1, 2, 3, 4};
vector<int> ivec(begin(int_arr), end(int_arr));
// or just a subset of the array
vector<int> subVec(int_arr + 1, int_arr + 4);

Multidimensional Arrays

Initialization the elements of a multidimensional array

int ia[3][4] = {
  {0, 1, 2, 3},
  {4, 5, 6, 7},
  {8, 9, 10, 11}
};

// this is the same as the previous ia, but less readable
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; 

int ia[3][4] = {{0}, {4}, {8}}; // we don't need to fill all the rows

// explicitly initialize row 0; the remaining elements are value initialized 
int ix[3][4] = {0, 3, 6, 9};

To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.

// this will work, since row is a reference
for (const auto &row : ia)
  for (auto col : row)	
    cout << col << endl;

// this will not work, since row is a pointer
for (auto row : ia)
	for (auto col : row)

Term

  1. std::size_type: 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 machine-independent manner. The type size_type is one of these companion types. 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. Although we don’t know the precise type of string::size_type, we do know that it is an unsigned type big enough to hold the size of any string.

  2. cctype funtions:

comments powered by Disqus