C++ Knowledge in Coding Interview (面試常用C++知識和技巧)

C++ Knowledge in Coding Interview

Table of Contents

  • Variables definitions, assignments, and initiations
  • Type conversions
  • Pointer
  • Object-Oriented Programming
  • STL
  • Other common usages
  • Explanation of keywords

Variables definitions, assignments and initializations

Uniform initializations

Integers

int a;
int a = 5;
int a(5);
int a{5};

Char

char c;
c = 'a';
c = 97; // same as c = 'a'; cout << c will print 'a' as well.
char c = 'a';
char c('a');
char c{'a'};

Float and double

float f;
f = 5.0f;
float f = 5.0f;
float f(5.0f);
float f{5.0f};
double d;
d = 5.0;
double d = 5.0;
double d(5.0);
double d{5.0};

C-style string

char str[] = "string"; //\0 is added automatically
str = "rope"; // not ok!
char str[] = {'S', 'a', 'm', '\0'}; //\0 is required
char *str = "hi"; //error, 
C++11 does not allow conversion from string literal to 'char *', instead, use below:
const char *str = "hi";

std::string

// init
string str;
str = "string";
string str = "string";
string str("string");
string str{"string"};
string s(4, 'Q'); //QQQQ
string s = 'a';
string s{'a'};
string s("my string");
string str(s);
string str(s, 3); //string
string str(s, 3, 4); //stri
const char *cs("my string");
string str(cs); //my string
string str(cs, 4); //my s (length)

Array

int a[5];
a[0] = 1;
int a[] = { 0, 1, 2, 3 };
int a[] { 0, 1, 2, 3 };
int a[5] = { 0, 1, 2, 3 }; //0,1,2,3,0
int a[5] { 0, 1, 2, 3 }; //0,1,2,3,0

Pointer

int i;
int *p;
p = &i;
p = q; //q is another pointer pointing to int;
int *p = &i;
const int *p = &i; //cannot change value of i via p
int const *p = &i; //same as above
int * const p = &i; // cannot change p
const int * const p = &i;

Dynamic array

  • Using just raw pointers there’s no way to know if they point to an array or a single value. A pointer that is being used as an array and a pointer to a single value are identical — they’re both just a memory address.
char *str;
str = new char[5]{"abcd"};
int *p = new int{5};
int *q = new int[3]{1,2,3};
cout << p << endl; //0x7fa2bec02be0
cout << p+1 << endl; // 0x7fa2bec02be4
cout << q << endl; //0x7fd959c02bf0
cout << q+1 << endl; //0x7fd959c02bf4
delete p;
delete[] q;

Enum

enum Color
{
    COLOR_BLACK,
    COLOR_RED,
    COLOR_BLUE,
    COLOR_GREEN,
    COLOR_WHITE, //the comma here is allowed in C++11
};
Color paint = COLOR_WHITE;
Color house(COLOR_BLUE);
Color apple { COLOR_RED };

Enum Class

enum class Color // "enum class" defines this as a scoped enumeration instead of a standard enumeration
{
    RED, // RED is inside the scope of Color
    BLUE
};
Color color = Color::RED; // note: RED is not directly accessible any more, we have to use Color::RED
Color color(Color::RED);
Color color { Color::RED };

Struct

struct Date
{
    int year;
    int month;
    int day;
};
Date today;
today.year = 2020;
Date today = { 2020, 10, 14 }; //initialization list
Date today { 2020, 10, 14 }; //uniform initialization
struct S1 { int a, b; } s { 0, 1 };

Class

class DateClass
{
public:
    int m_year;
    int m_month;
    int m_day;
    DateClass();//no parameter 
    DateClass(int year, int month, int day);
};
DateClass today(2020,10,14); // constructor DateClass(int, int, int)
DateClass today = { 2020, 10, 14 }; //initialization list, DateClass(int, int, int)
DateClass today { 2020, 10, 14 }; //initialize using parameterized constructor (C++11), DateClass(int, int, int)
// following work only if there is constructor with no parameter
DateClass today;  //DateClass()
today.year = 2020;
//DateClass today(); wrong, empty parentheses interpreted as a //function declaration
DateClass *p = new DateClass(); //DateClass()
DateClass *q = new DateClass; //DateClass()

Type conversions

String

  • convert numbers, char to string
// number to string (basically to_string works for all kinds of numbers)
std::to_string(123); // "123"      (int to string) std::to_string(3.1); // "3.100000" (double to string) std::to_string(.1f); // "0.100000" (float to string)
// another way to convert number and char to string is via stringstream (or simply ostringstream) class
#include <sstream>
std::stringstream ss; 
ss << 3.14; 
string str = ss.str(); // "3.14"
std::stringstream ss; 
ss << 1 << '+' << 2.0 << "=" << 1 + 2; 
string s = ss.str(); // "1+2=3"
// to convert char to string, we can use stringstream as above, or do as below
char ch = 'a'; 
string str{ch}; // "a"
string str(to_string(ch)); //"97" not "a"
string str = to_string(ch) + "test"; //"97test" not "atest"
// Other 
string s;
s += 'a' + 1; //b, 'a' + 1 is of type int, then converted to string
  • convert string to numbers, const char*, char []
// convert string to number using istringsream
std::istringstream iStream("123");
int i; //same works for other numerical types
iStream >> i;
// convert string to number use functions
string str = "3.14";
int i = std::stoi(str); // 3, need to pass str.c_str() in c
double d = std::stod(str); // 3.14, need to pass str.c_str() in c
// convert string to const char *
string s = "hello";
const char *ch = s.c_str();//null terminator appended
// convert string to char[]
string str = "cat";
char ca[4];
strcpy(ca, str.c_str()); //or use strncp
cout << ca << endl; //cat

Pointer

G4G

Basic usage

int var = 10;
int *ptr = &Var;
cout << var << endl; //10 
cout << *ptr << endl; //10
cout << &var << endl; //0x7fffa0757dd4, address of Var
cout << ptr << endl; //0x7fffa0757dd4, value of pointer == address of Var it points to
cout << &ptr << endl; //0x7fff98b499e8, address of pointer
//function pointers
int (*fcnPtr)();
int (*const fcnPtr)(); //cannot change the function points to
int (*fcnPtr)(int); //point to a function with one argument
// pass reference of pointer
void myfunction (int *&p){p == <somevalue>};

3 ways to pass C++ arguments to a function:

  1. call-by-value
  2. call-by-reference with pointer argument (pass by address, address is copied)
  3. call-by-reference with reference argument
//call by value
//Address of n1(&n1) and n(&n) are different
//n1 won't change after function call
int square1(int n){...}
square1(n1);
//Pass-by-Reference with Pointer Arguments
//Address of n2(&n2) and n(n) are the same
//n2 can be changed via the function call
void square2(int *n){...} 
square2(&n2);
//Pass-by-Reference with Reference Arguments
//Address of n3(&n3) and n(&n) are the same
//n3 can be changed via the function call
void square3(int &n){...}
square3(n3);

Passing array to a function

  • C++ does not allow to pass an entire array as an argument to a function. We can pass a pointer to an array by specifying the array’s name without an index. The pointer to the array is copied to the function, thus the value of elements can be changed via the function, but not the the address of the array.
void func(int a[], int n){
 cout << "value of a in func: " << a << endl;
}
void func1(int *a, int n){
 cout << "value of a in func1: " << a<< endl;
}
int a[] = {1,2,3};
cout << "value of a: " << a << endl;
func(a, 3); //func(&a[0], 3) also works
func1(a, 3); //func1(&a[0], 3) also works
value of a: 0x7ffee117e78c
value of a in func: 0x7ffee117e78c
value of a in func1: 0x7ffee117e78c

Array name as pointers and pointer arithmetic

  • An array name contains the address of the first element of the array which acts like a constant pointer. It means, the address stored in the array name can’t be changed.
int a[] = {1,2,3};
int *p = a; //a contains the address of a[0]
a++; //compile error: cannot increment value of type 'int [3]'
p++; //ok, points to a[1]
cout << a+1; //ok, address of a[1]
cout << p+1; //ok, address of a[1]

Pointer and two-dimensional numeric arrays

int nums[2][3]  =  { { 16, 18, 20 }, { 25, 26, 27 } };
  • nums[ i ][ j ] is equivalent to *(*(nums+i)+j), e.g.
*(*nums) == 16
*(*(nums+1)+2) == 27
  • Although nums,*nums and nums[0] have the same value, they have different types. nums is a type of int **, *nums and nums[0] is a type of int * .
value of nums: 0x7ffee1218770
value of *nums: 0x7ffee1218770
value of nums[0]: 0x7ffee1218770
value of nums + 1: 0x7ffee121877c //3 elements * 4 bytes = 12
value of *nums + 1: 0x7ffee1218774 //1 element * 4 bytes = 12
value of nums[0] + 1: 0x7ffee1218774 // 1 elements * 4 = 4
size of nums: 24 // 2 rows * 3 columns * 4 bytes
size of nums[0]: 12 // 3 elements in a row * 4 bytes

Pointers and String literals

  • String literals are arrays containing null-terminated character sequences. String literals are arrays of type character plus terminating null-character, with each of the elements being of type const char (as characters of string can’t be modified).
const char * ptr = "geek";
cout << sizeof("geek") << endl; // 5
ptr[0] = 'a'; //compile error: read-only variable is not assignable
char *ptr1 = "geek";// compile error: conversion from string literal to 'char *' is deprecated

Void pointers

  • Void pointers are pointers that point to a value that has no type (and thus also an undetermined length and undetermined dereferencing properties).
  • Void pointers cannot be directly dereferenced. They have to be first transformed into some other pointer type that points to a concrete data type before being dereferenced.
int a = 0;
void *p = &a;
int *p1 = (int*)p;
cout << *p << endl; //compile error: 
cout << *p1 << endl;//ok, 0

Function pointers

  • In the example below, foo is a pointer to a function taking one argument, an integer, and that returns void.
void my_int_func(int x)
{
    printf( "%d\n", x );
}
void (*foo)(int); // declare a function pointer
foo = &my_int_func; //Initializing Function Pointers, the ampersand is optional
//Use a function pointer
//call my_int_func (note that you do not need to write (*foo)(2))
foo( 2 ); // you can also use (*foo)( 2 );
  • Example uses of function pointers:
  1. Callback Functions
  2. Functions as arguments to other functions (I was asked in one of my video interviews). See the example in another article (Section: Pragmatics of Sorting). The fourth argument of the quicksort function in the standard library (C: stdlib.h, C++: cstdlib) is a function pointer.
void qsort(void *base, size_t num, size_t size, 
           int (*compare)(const void*, const void*))

STL

STL example and explanation, STL has four components:

  • Algorithms: sorting, searching, etc.
  • Containers: vector, stack, set, map, unordered_map, etc
  • Functions: functors
  • Iterators: iterators

 

Algorithms

qsort (standard c library, stdlib.h)

  • Standard C library provides qsort function that can be used for sorting an array.
  • The prototype of qsort() function
// Sort an array of any type. The parameters are, base
// address of array, size of array and pointer to
// comparator function
void qsort(void *base, size_t num, size_t size, 
           int (*compare)(const void*, const void*))
  • Comparator function: it requires two const void* parameters and it returns int.
int comparator(const void* p1, const void* p2);
Return value meaning
<0 The element pointed by p1 goes before the element pointed by p2
0  The element pointed by p1 is equivalent to the element pointed by p2
>0 The element pointed by p1 goes after the element pointed by p2
  • Example of qsort
int compare_int(const void *a, const void *b) // ascending order
{
    return *(int*)a - *(int*)b;
}
int compare_struct(const void *p, const void *q)// ascending order based on marks
{ 
    int l = ((struct Student *)p)->marks; 
    int r = ((struct Student *)q)->marks;  
    return (l - r);
}
int arr[] = {1, 6, 5, 2, 3, 9, 4, 7, 8};
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), compare_int);
Output: 1,2,3,4,5,6,7,8,9

sort : Japanese explanation

  • C++ STL provides a similar function sort that sorts a vector or array (items with random access).
  • Prototypes of C++ sort() function
// To sort in default or ascending order.
template 
void sort(T first, T last);

// To sort according to the order specified by comp.
template
void sort(T first, T last, Compare comp);
  • The order of equal elements is not guaranteed to be preserved. C++ provides std::stable_sort (same usage as sort) that can be used to preserve order.
  • Comparator cplusplus
  1. Binary function that accepts two elements in the range as arguments, and returns a value convertible to bool.
  2. The value returned indicates whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
  3. The function shall not modify any of its arguments.
  4. This can either be a function pointer or a function object.
bool myfunction (int i, int j) { return (i<j); }

struct myclass {
  bool operator() (int i,int j) { return (i<j);}
} myobject;
 
int compareSec(const pair<string, int>& a, const pair<string, int>& b) {
    return a.second > b.second;
}
  • Example (ascending order)
//Array
int arr[] = {1, 5, 8, 9, 6, 7, 3, 4, 2, 0};
int n = sizeof(arr)/sizeof(arr[0]);
sort(arr, arr+n);
//Vector
vector<int> data(arr, arr + n);
sort(data.begin(), data.end());
  • Example (descending order)
//Array
int arr[] = {1, 5, 8, 9, 6, 7, 3, 4, 2, 0};
int n = sizeof(arr)/sizeof(arr[0]);
sort(arr, arr+n, greater<int>());
//Vector
vector<int> data(arr, arr + n);
sort(data.begin(), data.end(), greater<int>());
  • Sort elements in unordered_map: It seems impossible to sort elements in unordered_map as sort function only supports containers with random access available (i.e: ele[i]). Thus, to sort elements in unordered_map, we need to make a vector coping data from unordered_map and sort elements of the vector. Example:
//descending order on a basis of second element
int compareSec(const pair<string, int>& a, const pair<string, int>& b) {
    return a.second > b.second;
}
//c++11 unodered_map initialisation
unordered_map<string, int> mp({{"practice", 3}, {"makes", 2}, {"perfect", 1}, {"just", 4}});
vector<pair<string, int>> vec(mp.begin(), mp.end());
sort(vec.begin(), vec.end(), compareSec);
Output of vector:
just 4
practice 3
makes 2
perfect 1
  • Sorting vector of pairs: G4G, G4G. By default, the sort function sorts the vector elements on the basis of the first element of pairs.
  • Sorting vector of vector. By default, it sorts by the first element of each vector in ascending order.
vector<vector<int>>v{{2,1}, {1,3},{4,2}};
sort(v.begin(), v.end());
for(auto i : v){
    cout << i[0] << " " << i[1] << endl;
}
// {1,3}, {2,1}, {4,2}
// sort by second element of the vector in ascending order
int comp(const vector<int> &a, const vector<int> &b){
    return a[1] < b[1];
}

partial_sort

Searching

  • std::binary_search (return bool)
int a[] = { 1, 5, 8, 9, 6, 7, 3, 4, 2, 0 };
int asize = sizeof(a) / sizeof(a[0]);
sort(a, a + asize);
if (std::binary_search(a, a + 10, 2))
    cout << "Element found in the array";
std::vector<int> haystack {1, 3, 4, 5, 9};
if (std::binary_search(haystack.begin(), haystack.end(), needle)) {
    cout << "Found " << needle << '\n';
  • std::bsearch (return void*)
// Binary predicate 
int compare(const void* ap, const void* bp) 
{ 
    // Typecasting 
    const int* a = (int*)ap; 
    const int* b = (int*)bp; 
  
    if (*a < *b) 
        return -1; 
    else if (*a > *b) 
        return 1; 
    else
        return 0; 
}
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; 
int ARR_SIZE = sizeof(arr) / sizeof(arr[0]); 
int key1 = 4;
int* p1 = (int*)std::bsearch(&key1, arr, ARR_SIZE, sizeof(arr[0]), compare);
if (p1) 
    cout << key1 << " found at position " << (p1 - arr);

Important STL Algorithms

  • Non-Manipulating Algorithms
int arr[] = {10, 20, 5, 23 ,42 , 15};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
// Reversing the Vector
reverse(vect.begin(), vect.end());
cout << *max_element(vect.begin(), vect.end());//Maximum element
cout << *min_element(vect.begin(), vect.end());//Minimum element
cout << accumulate(vect.begin(), vect.end(), 0);//summation from 0
cout << count(vect.begin(), vect.end(), 20); //Counts the occurrences of 20
if(find(vect.begin(), vect.end(),5) != vect.end()){}
vector<int>::iterator it = lower_bound(vect.begin(), vect.end(), 20); //first occurrence of 20
vector<int>::iterator it = upper_bound(vect.begin(), vect.end(), 20); //last occurrence of 20
vect.erase(vect.begin()+1); //Delete second element of vector
vect.erase(unique(vect.begin(),vect.end()),vect.end()); //Deletes the duplicate occurrences, vect must be sorted
next_permutation(vect.begin(), vect.end());//modifies vector to its next permutation order
prev_permutation(vect.begin(), vect.end());modifies vector to its previous permutation order
cout << distance(vect.begin(),max_element(vect.begin(),vect.end())); //Return distance of first to maximum element
swap(x,y); //x and y are int
swap(vect1,vect2);
  • Useful Array algorithms
int ar[6] =  {1, 2, 3, 4, 5, -6};
if(all_of(ar, ar+6, [](int x){ return x>0; }))//all elements are positive
if(any_of(ar, ar+6, [](int x){ return x<0; }))//if any ele are negative
if(none_of(ar, ar+6, [](int x){ return x<0; })) //no ele is negative
copy_n(ar, 6, ar1);//copy 6 elements from ar to ar1
iota(ar, ar+6, 20); //assign 6 values to ar starting from 20 (21,22..)

Containers

Sequence Containers: Implement data structures which can be accessed in a sequential manner.

  • vector: dynamic array capable of growing as needed to contain its elements
// init
vector<int> v;
v.push_back(1);
vector<int> v(n, 10); //n elements with value 10
vector<int> v = { 10, 20, 30 };
vector<int> v{ 10, 20, 30 };
vector<int> v(arr, arr + n); //arr is int array
vector<int> v(v1.begin(), v2.end()); //init from another vector
// common usage
cout << v.empty();
cout << v.size();
cout << v.front();
cout << v.back();
v.push_back(1);
v.pop_back(); //delete last
v.assign(5, 10); // fill the array with 10 five times
v.insert(v.begin(), 5); //insert 5 at beginning
v.erase(v.begin()); //remove the first element
v1.swap(v2); //swap two vectors
// other useage
vector<vector<int>>vv;
vector<vector<int>>vv{{}}; //push an empty vector
vv.push_back(vector<int>()); //push an empty vector
vv.push_back(vector<int>{}); //push an empty vector
vv.push_back({}); //push an empty vector
vector<vector<int>>vv(4, {}); //error
vector<vector<int>>vv(4, vector<int>()); //ok
vector<vector<int>>vv(4, vector<int>{}); //ok
//return vector
vector<int> func(){ 
    return {1,2};
    return vector<int>{1,2};
    return {};
}
// Important
if(0 < v.size() - 1)
    cout << "entered" << endl;
if vec is empty, it will enter the body, as v.size()-1 be a very large number as size() returns unsigned int. To avoid unexpected results, we can use if(0 < (int)v.size() - 1) or 0+1 < v.size().
e.g. https://leetcode.com/problems/binary-search-tree-iterator/
  • list: a doubly linked list
// init
list<int> lst;
lst.push_back(2);
lst.push_front(1); //now: 1,2
list<int> lst(10); // size 10
list<int> lst(10, 5); //10 elements with all 5
list<int> lst{4, 6, 5}; // value: 4,6,5
list<int> lst(arr, arr + n); //arr is an array
list<int> lst(lst1); //copy constructor, lst1 is another list
// common usage
cout << lst.front();
cout << lst.last();
cout << lst.size();
cout << lst.empty();
lst.push_back(1);
lst.push_front(2);
lst.pop_front(); //delete first
lst.pop_back(); //delete last
lst.erase(itr); //delete at a position
lst.clear(); //delete all elements, size 0, free memory
lst.swap(lst1); //swap elements of two lists
  • deque: double-ended queue class
// declare
deque<int> deq;
// common usage
deq.push_back(1);
deq.push_front(2);
// init
array<int,2> a;
a[0] = 1;
a[1] = 2;
array<int,6> ar = {1, 2, 3, 4, 5, 6};
array<int,6> ar1{2, 3, 4, 5, 6, 7};
// common usage
cout << ar.front(); //1
cout << ar.back(); //6
cout << ar[1]; //2
cout << ar.size(); //6
cout << ar.empty(); //0
for ( auto it = ar.begin(); it != ar.end(); ++it )
    std::cout << ' ' << *it; //1,2,3,4,5,6
ar.fill(0); // Filling array with 0, all elements are 0 now
ar.swap(ar1); // Swapping ar1 values with ar
  • forward_list( Introduced in C++11): implements singly linked list.
// Declare
forward_list<int> fl;
forward_list<int> fl = {10, 20, 30, 40, 50};
forward_list<int> fl{10, 20, 30, 40, 50};


// common usage
fl.assign({1, 2, 3}); // 1,2,3
fl.assign(5, 10); //5 elements with value 10
fl.push_front(10); //insert at front
fl.pop_front(); //delete frist
fl.remove(40); //Removes all occurrences of 40

Container Adaptors: provide a different interface for sequential containers.

// Declare
queue<int>q;
queue<int>{{1}};
//common usage
q.push(1);
q.pop();
int i = q.front();
int i = q.back();
cout << q.empty();
cout << q.size();
  • priority_queue: a type of queue where the elements are kept sorted. it can be used to create min-heap or max-heap. 
    1. top() = O(1)
    2. pop()= O(logn)
    3. push()= O(logn)
#include <queue> //not #include <priority_queue>
// declare
priority_queue<int> pq; //descending order (max heap)
priority_queue<int, vector<int>, greater<int>> pq; //ascending order (min heap)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > pq; //ascending order (min heap, sorted by first element in pair)
//common usage
pq.push(10);
pq.push(30);
pq.push(20);
pq.push(make_pair(1,2)); //pair
pq.push({1,2}); //pair
cout << pq.top();//30, as it is sorted
pq.pop();
//Declare
stack<char> st;
stack<char> st{{'a'}};
// common usage
st.push('a');
st.pop(); //remove last element, return void. segmentation fault if empty
char c = st.top(); //return last element, segmentation fault if empty
if(st.empty()) //empty or not
cout << st.size(); //size

Associative Containers : implement sorted data structures using binary search tree that can be quickly searched (O(log n) complexity). C++’s map and set are actually ordered map and ordered set. They are not implemented using hash functions (instead red-black trees, a BST). Every query would take O(logn) and not O(1), but the values will be always sorted.

  • set: stores unique elements, with duplicate elements disallowed. The elements are sorted ( can be searched with log(n) ) according to their values. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.
// init
set<int>s; //ascending order
set<int, greater<int>> s; //descending order
s.insert(1);
set<vector<int>> res; //use vector as elements, this is useful when we need a set of vector without duplicates, however, we cannot define unordered_set the same way.
// common usage
if (s.find(1) != s.end()){}
if (s.count(1)){}
cout << s.size();
cout << s.empty();
for (auto it = s.begin(); it != s.end(); it++){ //set<int>::iterator
    cout << *it << endl;
}
  • multiset: is a set where duplicate elements are allowed, it can be used to create min or max heap.
Same usage as std::set
  • map: is a set where each element is a key/value pair. The key is used for sorting and indexing the data, and must be unique. The value is the actual data. The elements in map are sorted by key.
// init
map<int, int> mp;
mp.insert(pair<int, int>(1, 40));
mp.insert(make_pair(2,3));
mp.insert({ 3, 40 });
mp[4] = 30;
map<int, int> mp1(mp.begin(), mp.end());
// common usage
cout << mp.size();
cout << mp.empty();
mp.erase(mp.begin(), mp.find(3)); //remove all elements up to element with key=3
cout << mp.erase(4); //remove all elements with key=4, returns 1 if the key element is found in the map else returns 0.
mp.clear(); //remove all elements
for (auto itr = mp.begin(); itr != mp.end(); ++itr) {
    cout << '\t' << itr->first << '\t' << itr->second << '\n';
}
  • multimap: is a map that allows duplicate keys.
Same usage as std::map

Unordered Associative Containers : implement unordered data structures using hashing that can be quickly searched. While they are not sorted, most queries and operations are possible in O(1) averagely.

// init method 1
unordered_set<int> us;
us.insert(1);
// init method 2
unordered_set<int> us{1,2}; //cannot use us(1,2)
// init method 3
unordered_set<int> us{v.begin(), v.end()}; //or us(v.begin(), v.end())
// common usage
if ((us.find(1) != us.end()) // find element
if ((us.count(1)) // find element
if (us1 == us2) // check equality
for (auto it = us.begin(); it != us.end(); it++){
    cout << *it << endl;
}
// init method 1
unordered_map<char, int> um;
um['a'] = 1;
um['b'] = 2;
um['c']++; //if there was no key 'c', the value will be 1.
// init method 2
unordered_map<char, int> um;
um.insert(std::make_pair('a',1));
// init method 3
unordered_map<char, int> um {{'a',1},{'b',1}};
// Common usage
cout << um['a'] << endl; // 1
cout << um.empty() << endl; // 0
if ((um.find('a') != um.end()) // find element
if ((um.count('a')) // find element
auto it = um.find('a'); //or unordered_map<char, int>::iterator
cout << it->first << " " << it->second << endl; // a, 1
for(auto it = um.begin(); it != um.end(); it++){
    cout << it->first << "  " << it->second << endl;
}

Other Common Usage

The following are other commonly used tricks / tips in a coding interview.

Variable declarations, definitions, etc.

Char

// uppercase to lower case
char lower = upper + 'a' - 'A'; // or upper+32
// lowercase to upper
char upper = lower - ('a' - 'A'); // or lower-32
char c = 'A';
cout << (char)toLower(c); //a, if without char, 97 #include<cctype>
cout << isalnum(c); //1, #include<cctype>
// Toggle case
char a = 'a', b = 'B';
a ^= (1<<5);
b ^= (1<<5);
cout << a << " " << b; // A b

Integer

// negative integer
int n = -12;
cout << n / 10 << ", " << n % 10; // -1, -2
// reverse integer
int ret = 0;
while(n){
    ret = ret*10 + n%10;
    n /= 10;
}

String

// find index of char
size_t index = str.find('?');
if(index != string::npos){}
// link string with char
string s = "Abc";
string s1 = "test";
string s2 = s1 + s[0]; // s2 is testA, no explicit type cast required
string s3 = s[0]; // error
string s4 = static_cast<string>(s[0]); // error
string s4 = (string)s[0]; //error
string s5{s[0]}; // error
string s6(s[0]); // OK

std::ostringstream

// instead of using std::string to add strings for output, we can use ostringstream.
std::ostringstream oss;
oss << "test" << 123 << std::endl;
std::cout << oss.str();

Exception

try {
    if (n < 0)
        throw std::runtime_error("Bad things happened");
}        catch (const std::exception& e) {
    assert(std::string(e.what()) == std::string("Bad things happened"));
}

priority_queue (3 ways of creating min-heap)

// 1. comparison using struct to create min-heap
struct compare
{
    bool operator()(const pair<int,pair<int, int> >& a, const pair<int,pair<int, int> >& b)
    {
        return a.first>b.first;
    }
};
priority_queue< pair<int,pair<int, int> >, vector<pair<int, pair<int, int> > >, compare > p;
// 2. comparison using function to create min-heap
auto comp = [](ListNode* a, ListNode* b) { return a->val > b->val; };
priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> pq(comp);
// 3. using std::greater<T> to create min-heap
priority_queue<int, vector<int>, create<int>> pq;

Reverse linked list (three pointers required)

//iterative
ListNode *current, *prev, *next;
current = head; // or current = *pointerToHead
prev = nullptr;
while(current)
{
    next = current->next; //we can define next as tmp here as well
    current->next = prev;
    prev = current;
    current = next;
}
//current, next are nullptr now
head = prev; // or *pointerToHead = prev
//recursive
ListNode* reverseList(ListNode* head) {
    if(head == nullptr || head->next == nullptr) return head;
    ListNode *node = reverseList(head->next);
    head->next->next = head;
    head->next = nullptr;
    return node;
}

Delete a node in linked list (two pointers required)

void Delete(int n) //delete n-th node
{
    Node* temp1 = head; // or ListNode* temp1 = *pointerToHead
    if (n == 1){
        head = temp1->next; // head now points to the second node, or *pointerToHead = temp1->next;
        delete temp1;
        return;
    }
    for (int i = 0; i < n - 2; i++){ // temp1 points to n-1 th node, delete temp1->next
        temp1 = temp1->next; 
    }
    ListNode* temp2 = temp1->next;
    temp1->next = temp2->next;
    delete temp2;
}

Explanation of keywords

static

  • Static variables: variables in a function, variables in a class
  • Static members of class: Class objects and functions in a class
  • Make variable declared outside of a function to have internal linkage (can only be used in that file)
static int a = 0; //internal linkage
int myFun(){
    static int b = 0; //static variable in a function, same life time of global variable
}
class Example{
    static int c; //static memeber variable, not belong to any object
    static void myFunc(); //static member method, can only call other static members
}
int Example::c = 1; //have to define outside
static Examlae example; //similar to static variable, have a scope till the lifetime of program

extern

  • Used to make const variables declared outside of a function to have external linkage (can be used in other files). In other words, it is used to extend the visibility of variables/functions.
  • It can also mean “this is a forward declaration for an external variable that is defined somewhere else”.
extern int var = 0; //define var, it can be used by other files, extern keyword can be omitted here.extern int var; //forward declaration

using

  • Import namespace: using namespace std;
  • Used for type alias is identical to typedef. e.g.using T = int;
  • Change an inherited member’s access specifier in the derived class.
class Base
{
protected:
    void printValue() { std::cout << "base"; }
};
class Derived: public Base
{
public:
    // Base::printValue was inherited as protected, so the public has no access
    // But we're changing it to public via a using declaration
    using Base::printValue; // note: no parenthesis here
};

override

  • Use override specifier for each override function in the derived class to ensure you’ve actually overridden the function you think you have.
class A
{
public:
    virtual const char* getName1(int x) { return "A"; }
    virtual const char* getName2(int x) { return "A"; }
    virtual const char* getName3(int x) { return "A"; }
};
 
class B : public A
{
public:
    virtual const char* getName1(short int x) override { return "B"; } // compile error, function is not an override
    virtual const char* getName2(int x) const override { return "B"; } // compile error, function is not an override
    virtual const char* getName3(int x) override { return "B"; } // okay, function is an override of A::getName3(int)
};

final

  • The final specifier can be used to make a virtual function no longer overridable. To prevent inheriting from a class, the final specifier is applied after the class name.
class B : public A
{
public:
    // note use of final specifier on following line -- that makes this function no longer overridable
    virtual const char* getName() override final { return "B"; } // okay, overrides A::getName()
};

class C : public B
{
public:
    virtual const char* getName() override { return "C"; } // compile error: overrides B::getName(), which is final
};
// to prevent inheriting from class A
class A final
{
}

auto and decltype

  • auto keyword specifies that the type of the variable that is being declared will be automatically deducted from its initializer.
  • decltype lets you extract the type from the variable, so decltype is sort of an operator that evaluates the type of passed expression.
auto x = 4; //x is of type intdecltype(fun1()) x; // type of x is same as return type of fun1()

explicit: to prevent converting constructors.

MyString mine = 'x'; //implicitly convert 'x' to MyString if there is a proper constructor, say: MyString(int x).
explicit MyString(int x) {
    m_string.resize(x);
}
MyString mine = 'x'; // compile error, since MyString(int) is now explicit and nothing will match this

delete

  • Similar purpose as explicit to prevent converting constructors.
MyString(char) = delete; // any use of this constructor is an error
MyString mine('x'); // compile error, since MyString(char) is deleted
  • Hide functionality that exists in the base class, so that it can not be accessed through the derived class.
class Derived : public Base
{
public:
    int getValue() = delete; // mark this function as inaccessible, note that the Base version of getValue() is still accessible though
};
  • Delete dynamically allocated memory (return to OS).
int* array = new int[10];
delete[] array;
int* ptr1 = new int;
delete ptr1;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章