2023 C/C++ Software Development Engineer School Recruitment Interview Frequently Asked Knowledge Points Review Part 7

46. ​​The initialization sequence and expansion of member variables of C++ classes

Notice:

1. constMembers 引用must be initialized in the member variable initialization list, and cannot be initialized in the constructor

  • why? Because the "initialization" in the constructor is actually not initialization but assignment , and const variables are not allowed to be assigned
  • Quoting is essentially again const 指针, the same reason as above

2. staticMember variables must be initialized outside the class

3. A custom type without a default constructor must be initialized in the initialization list, the reason is at the end of this section


Key points for the initialization sequence:

1. The initialization order of the member variables of the class is related to the order of the member variables in the class definition, and has nothing to do with the order in the initialization list

  • That is to say, in the class definition, if the variable is defined first int a;and then the variable is defined, int b;then the storage order of the member variables has been determined at the program compilation stage, so it must be initialized first a, and then initializedb

2. If the member variables of the class are initialized in the constructor, they will be initialized in the order in the constructor


If you bring a static variable, its initialization sequence is:

1. Static variables or global variables of the base class

2. Static variables or global variables of derived classes

3. Member variables of the base class

4. Member variables of derived classes


成员初始化列表与构造函数内进行初始化的区别?

  1. In fact, the member variables of the class have been initialized before entering the function body of the constructor ; if there is a member initialization list (called 显式的初始化) it will be initialized according to the list, if there is no explicit initialization, that is, there is no initialization list, then the compiler Will use the default constructor for initialization.

  2. So initialization inside a constructor is essentially an assignment

  3. For built-in types, references and pointers, the two methods are the same in efficiency and results

  4. The biggest difference is the custom type member variable

    • If an initializer list is used to initialize a custom type, only one construction, such as copy construction, is done before entering the constructor
    • If the initialization list is not used, before entering the constructor, the compiler calls the default constructor to initialize the member variable once, which is the first construction; then enters the constructor and assigns another value, which produces the second construction, This is inefficient. Because of the double construction, it is recommended to use the initialization member list for initialization of custom types

Another problem, if the member variable of the custom type does not have a default constructor, it must be explicitly initialized in the initialization list, otherwise it will report an error because the compiler cannot find the default constructor

47. Mandatory conversion type operation symbol
  1. static_cast<newType>(data)

    • Generally used for benign conversions

    • The original will not have any unexpected conversions: int–>double, short–>int, const–>non-const, upcasting (conversion from derived class to base class)

    • void*–>int*, int*–>void*Similar to the conversion between void pointers and concrete types

    • Conversion between classes with conversion constructors and type conversion functions and other types: conversion between double and Complex

      Cannot be used for conversion between unrelated types, because conversion between unrelated types is risky

      For example: conversion between specific types, conversion from integer to pointer

  2. const_cast<newType>(data)

    • It is generally used to const/volatileerase the characteristics, that is, to change the const/volatile type into a non-const/volatile type

    • Usually, newType is a pointer type, which means converting a pointer to a const type into a pointer to a non-const type

      const int n = 15;
      int * p = const_cast<int*>(&n);
      // 注意:&n的类型是const int*   p的类型是int* 
      
  3. reinterpret_cast<newType>(data)

    • It is a simple and crude conversion method, or it is not converted based on conversion rules, but directly reinterpreted at the binary level

    • You can do some conversions that are not allowed by normal conversions: such as converting a pointer of different types to each other , converting a pointer of a class to a pointer of a common type

  4. dynamic_cast<newType>(data)

    • It is used to convert between the inheritance relationship of the class, which can be converted up ( derived class -> base class ) and down converted ( base class -> derived class )

      • Upcasting is unconditional because it must succeed, equivalent tostatic_cast<>()
      • The downcast is conditional, must be safe, is detected with the help of RTTI, so only some will succeed
    • This conversion requires that both sides of the conversion must be pointers or references

      • For pointers, conversion failure will returnNULL
      • For references, conversion failure will returnstd::bad_cast异常
    • The working process of dynamic_cast :

      1. During the running of the program, the inheritance chain will be traversed . If the target type to be converted is encountered on the way, the conversion will be successful.
      2. If the target type to be converted is still not found until the top of the inheritance chain, that is, the top base class, then the conversion fails
    • In essence, dynamic_cast can only upcast. The so-called conversion of the base class to the derived class is nothing more than pointing to a derived class with a different base class pointer.

      : class A--->class B--->class C--->class D
          A* pa = new D();
      	B* pb;
      	C* pc;
      	D* pd;
      
      	pb = dynamic_cast<B*>(pa);  // pa 向下转换为pb  不过这些都是表现  本质就是不同的基类指针指向派生类而已
      	pc = dynamic_cast<C*>(pa);
      	pd = dynamic_cast<D*>(pa);
      

There is a risk of loss of precision when converting int to float, and there is no risk of loss of precision when converting int to double

Both int and float are generally stored in 32 bits, but the storage formats are different

  1. int: The first bit is the sign bit, 1~31 bits are used to represent data, so the range of data that int can represent is − 2 31 -2^{31}231–> 2 31 − 1 2^{31}-1 2311 , immediately-2,147,483,648 到 2,147,483,647

  2. float: from high to low are 符号, 阶码, 尾数(1, 8, 23) , so the largest part of the index is only the 23rd power

  • Combining the above ① and ②, we found that the range of data that int can represent is larger than that of float, so there is a risk of loss of precision when converting int to float; and converting int to double, 64 bits, high to low Yes 符号, 阶码, 尾数(1, 11, 52) will not lose precision, because the exponent part of double has a maximum power of 52.
 cout << "测试 int ---> float的转换\n";
 int MAX = INT_MAX; // 设置int为最大值
 float MAXf = (float)MAX;
 cout << MAX << " " << setprecision(9) << MAXf << endl;
 cout << (int)MAXf << endl; // -2147483648 与 INT_MAX已经不一样了,说明转换到float后精度损失了

 double MAXd = (double)MAX;
 cout << MAX << " " << setprecision(9) << MAXd << endl;
 cout << (int)MAXd << endl; // 2147483647 == INT_MAX 说明没有发生精度损失
48. const member function – constant member function and constant object
  • Constant object:
const Obj;
  1. constAdd decoration to an object , indicating that Objyou do not want to modify its ordinary member variables after initialization
  2. Does the decoration of constant objects work on static member variables ? Answer: It doesn't work, because constit is the instantiated object that is modified at this time, not the modified class, and then the static member variable belongs to this class and not to this object.
  • Constant objects cannot be called普通成员函数 , and can be called 常量成员函数and静态成员函数
  1. Because ordinary member functions may modify the members of the object during execution
  2. The constant member function can guarantee that the members of the object will not be modified , so the constant member function can be called
  3. Static member functions are independent of class objects and belong to classes, so they cannot access non-static member variables, and naturally there is no possibility of modifying non-static member variables, so they can be called by constant objects
  • const member functions —as followsint getAValue() const
class A{
    
    
    int a;
public:
    A() : a(0) {
    
    }
    int getAValue() const{
    
    
       return a; 
    }
};
  1. The const after the function indicates that this is a constant member function
  2. A const member function does not change an object's member variables
  3. Constant member functions are mainly called by constant objects
  4. If the compiler finds that there is a behavior of modifying non-static member variables inside the constant member function , it will report an error, but internal modification of static member variables will not report an error
    • Because the static variables of the class are not constrained by the const object
#include <iostream>
using namespace std;

class Test
{
    
    
public:
    static int a;
    int b;

public:
    Test() : b(0) {
    
    }
    // 常量成员函数
    void changeStatic(int _a) const
    {
    
    
        a = _a; // 此处在const成员函数修改了静态变量,允许。
        // b++;    // 修改非静态成员变量,不允许
    }
    // 普通函数
    void changeB()
    {
    
    
        b++;
    }
    static void setStatic()
    {
    
    
        a--;
    }
};
int Test::a = 100; //静态类成员的类外初始化

int main()
{
    
    
    Test t;
    cout << t.b << " " << t.a << endl;

    const Test conT;
    conT.changeStatic(111); // 常量对象调用常量成员函数
    // conT.changeB();         // 常量对象不允许调用普通成员函数
    cout << conT.a << endl;

    conT.setStatic(); // 常量对象可以调用静态函数
    return 0;
}
  • About overloading: const modification can be used as a distinguishing mark for function overloading
    • The const object calls the const version
    • Non-constant object calls the normal version
  • If a function will not modify the member variables of the class, it can be defined as const type, which is a good habit
    • Because both ordinary objects and const objects can call const member functions
49. volatile keyword

Declaration syntax:int volatile vInt;

  • When using this keyword to declare, the system will always re-read data from the memory where it is located, even if the previous instruction has just read data from it;
  • volatileIt is used to inform the compiler that the variable is variable at any time , so the optimization of the compiler is prohibited , and the program reads the latest data every time
    • (Sometimes the compiler optimizes based on the structure of the code without reading the latest data from a register or memory location)

Generally speaking, the usage scenarios of volatile are:

  1. Variables modified in the interrupt service routine for detection by other programs need to be addedvolatile
  2. Flags or data shared between tasks in a multitasking environment should be addedvolatile
  3. Memory-mapped hardware registers usually also need to be volatileexplained, so each read and write to him may have different meanings

volatile can also modify the variable pointed to by the pointer like const

const int* pInt;   // 表示pInt指向的数据是一个常量
volatile int* pVInt; // 表示pVInt指向的数据是一个易变的量

// ---------------------------------------------

int* const pInt;		// 表示指针pInt本身是不可变的
int* volatile pVInt; 	// 表示指针pVInt本身是易变的

This keyword specifies that the storage location of an object is in memory rather than in registers. Because the general object compiler may copy it to the register to speed up the execution of the instruction.

The main thing is to let the compiler take it out of the memory every time it operates on the variable, instead of using the value already in the register

50. Huffman tree

赫夫曼树的相关概念:

  1. Path length: The branch from one node to another constitutes a path between two nodes , and the number of branches on the path is called the path length
  2. The path length of the tree : the sum of the path lengths from the root of the tree to each node
    • Note that each node, not each leaf node
  3. The weighted path length of a node : the product of the path length from the node to the root of the tree and the node weight
  4. The weighted path length of the tree : the sum of the weighted path lengths of all leaf nodes in the tree

赫夫曼树的概念:

A binary tree with the smallest weighted path length is called a Huffman tree

赫夫曼树的构造:——Taking A(5), B(15), C(40), D(30), E(10) as examples

  1. First arrange the leaf nodes with weights in ascending order
    • {A(5)、E(10)、B(15)、D(30)、C(40)}
  2. Take the two nodes with the smallest weight A(5)、E(10)as N1the child nodes of a new node
    • Relatively smaller A(5)as left child, larger E(10)as right child
    • Delete two nodes in the sequenceA(5)、E(10)
    • Will N1(15)be added to the sequence, still in ascending order, the weight of N1 is equal to the sum of the weights of the left and right children
      • {N1(15)、B(15)、D(30)、C(40)}
  3. Take the two nodes with the smallest weight again N1(15)、B(15)as N2the child nodes of a new node
    • N1(15) and B(15) have the same weight, so left and right children can be used
    • delete two nodes in the sequenceN1(15)、B(15)
    • Add N2(30) to the sequence in ascending order, the weight of N2 is equal to the sum of the weights of the left and right children
      • {N2(30)、D(30)、C(40)}
  4. repeat step 2
    • N3(60)has two child nodes N2 and D
      • {C(40)、N3(60)}
  5. repeat step 2
    • N4(100)has two child nodes C(40)andN3(60)
      • At this time there is only one root node in the sequence{N4(100)}
  6. construction completed
			     N4(100)
                 /     \
              C(40)   N3(60)
                     /     \
				  N2(30)  D(30)
                 /     \
              N1(15)   B(15)
              /    \
            A(5)  E(10)

The characteristics of Huffman tree

1. A full binary tree is not necessarily a Huffman tree

2. The Huffman tree is not unique, because there may be trees with the same weighted shortest path , and there are multiple Huffman trees at this time

3、Leaf nodes with larger weights are closer to the root, and leaf nodes with smaller weights can be slightly farther from the root

4. The degree of the nodes of the Huffman tree is either 0 or 2, and there is no node with degree 1

5. A forest containing n nodes needs to be merged n-1 times to form a Huffman tree. At this time, there are 2n - 1 nodes in total, so n - 1 nodes are newly added

Huffman coding

  • The original design of Huffman coding is mainly used for data compression problems in long-distance communication
  • Because whether it is Chinese or English, the frequency of use of different characters is different, some are used very frequently, and some are used less frequently; therefore, if the frequently used characters can be encoded in a shorter code during the encoding process, Characters that are used less frequently can use longer codes to compress data. At this time, a Huffman tree is used, where the frequency of use of characters is its weight
  • The encoding result of each character is located at the leaf node

Huffman coding definition

In general, assume that the character set to be encoded is {d1, d2, ···, dn}, and the frequency set used by each character is {w1, w2, ···, wn}, with d1, d2, ··· , dn as leaf nodes, and w1, w2, ···, wn as leaf weights to construct a Huffman tree.

It is stipulated that the left branch of the Huffman tree represents 0 , and the right branch represents 1 , then the sequence of 0 and 1 composed of the path branches passed from the root node to the leaf node is the corresponding character code of the node, which is Huff Mann coding

1. Huffman coding is a special prefix code, because only leaf nodes represent a certain character

2. Each character code of Huffman code is not a prefix of other codes

51. Prefix tree
  • Also known as dictionary tree, it is an ordered tree used to store associative arrays, the keys of which are usually strings
  • The key of the prefix tree is not directly stored in the node, but is determined by the position of the node in the tree
  • All descendants of a node use the same prefix, which is the string corresponding to the current node
  • The root node corresponds to an empty string
  • Not all nodes have corresponding values, only the keys corresponding to leaf nodes and some internal nodes have relevant values

insert image description here

Guess you like

Origin blog.csdn.net/qq_40459977/article/details/127519181
Recommended