Tencent interview questions C + + basic chapter

Time:2022-1-6

Tencent interview questions C + + basic chapter

What are the differences between C + + virtual functions and pure virtual functions, and what are their applications?

Firstly, pure virtual function is a special virtual function. In many cases, it cannot be given a meaningful implementation of virtual function in the base class, but it is declared as pure virtual function, and its implementation is left to the derived class of the base class. This is what pure virtual functions do. Generally speaking, a class using pure virtual functions is called an abstract class. This class does not provide methods, but only interfaces. It can also be vividly understood as an API.

Declaration method of pure virtual function: virtual void a() = 0;

Virtual function is to realize polymorphism (polymorphism is a technology that allows you to set the parent object equal to one or more of its child objects. After assignment, the parent object can operate in different ways according to the characteristics of the child object currently assigned to it

The role of iterators in STL, why do we need iterators when there are pointers (HR often asks)

1. Iterator

Iterator mode, also known as cursor mode, is used to provide a method to access each element in an aggregate object sequentially without exposing the internal representation of the object. In other words, it may be easier to understand that the iterator pattern is a pattern applied to aggregate objects. By using this pattern, we can access each element in the aggregate object in a certain order (the method provided by the iterator) without knowing the internal representation of the object.

Due to the above characteristics of iterator mode: it is coupled with aggregation objects, which limits its wide application to a certain extent. It is generally only used for the underlying aggregation support classes, such as STL’s list, vector, stack and other container classes and ostream_ Iterator and so on.

2. The difference between iterators and pointers

Iterators are not pointers, but class templates that behave like pointers. He just simulated some functions of the pointer and overloaded some operations of the pointer
Symbols, ->, *, + +, — etc. The iterator encapsulates the pointer and is a “traversable STL (Standard Template Library) container
The object of “all or part of elements” essentially encapsulates the native pointer. It is a lift of the pointer concept and provides a higher level than the pointer
Level behavior is equivalent to an intelligent pointer. It can realize different operations such as + +, – according to different types of data structures.
The iterator returns the object reference rather than the value of the object, so cout can only output the value obtained by the iterator using * instead of directly
Itself.

3. Causes of iterators

The access method of iterator class is to abstract the access logic of different collection classes, so that the internal structure of the collection is not exposed
The effect of loop traversal collection.

How can the pre + + and post + + operators be overloaded

When we use the post operator, the compiler provides an argument with a value of 0 for this XINCAN. Although syntactically speaking, post functions can use this additional formal parameter, they usually don’t do so in practice. The only function of this parameter is to distinguish the functions of the pre version and the Post version, rather than really participating in the operation when implementing the Post version. ” The following is the implementation code of pre + + and post + +. Please pay attention to the differences between the two implementations,One is to return a reference and the other is to return a temporary object

Prefix operator + + (), + + returns the added value
Suffix operator + + (int), + + returns the previous value

#include <iostream>
using namespace std;
class Complex
{
public:
        Complex(float x, float y)
                :_x(x), _y(y) {}
        void display()
        {
                cout << "(x = " << _x << ", y = " << _y << ")" << endl;
        }
        //Be sure to return a reference, because + + will change the operand, and if it is a temporary object, the value of the operation data will not change
        //Before++
        Complex& operator++()
        {
                ++this->_x;
                ++this->_y;
                return *this;
        }
        //After + +, add any type in the parameter to indicate that it is after + +, which is called sub element
         const Complex operator++(int)
        {
                //Save a temporary variable of * this first
                Complex tmp(*this);
                //For this itself++
                this->_x++;
                this->_y++;
                //Returns a temporary variable
                return tmp;
        }
private:
        float _x;
        float _y;
};

Will the subclass destructor call the destructor of the parent class? What is the order of execution? Subclass constructor call order?

The order of destructor calls is to destruct the derived class first and then the base class, that is, when the base class is destructed and called, all the information of the derived class has been destroyed.

When an object is defined, it calls the constructor of the base class and then calls the constructor of the derived class. The analysis is just the opposite: first invoke the destructor of the derived class, then call the destructor of the base class.

reason:

Some initializations in the derived class constructor may be based on the base class, so it is specified that the construction starts at the root of the class hierarchy, and at each layer, the base class constructor is called first, Then the calling member (the members here only refer to various objects, such as QString a, which do not contain basic type variables such as int n, pointer variables such as QString *a) object constructors (because the member variables of C++ are not automatically initialized, and can only be initialized using initialization list. (call the constructor of the member. If it is not explicitly called in the initialization list, it will implicitly call the default constructor of the member variable, which can be found through assembly) or initialize in the constructor of this layer) reference:http://www.cnblogs.com/lidabo…)。

If the constructor of the base class is not explicitly called, the parameterless constructor of the base class is automatically called. If the base class only has a constructor with parameters, an error will be reported. An explicit parameterless constructor is not necessary. You can explicitly call the constructor with parameters of the base class.

#include<iostream>
using namespace std;
class Base{
public:
    Base (int c) {cout < < base class constructor with parameters < < C < < endl;}
    ~Base() {cout < < base class destructor < < endl;}
};
class Derived:public Base{
public:
    Derived (int c): base (c) // explicitly call the base class constructor
    {
        Cout < < derived class constructor with parameters < < C < < endl;
    }
    ~Derived() {cout < < derived class destructor < < endl;}
};
int main()
{
    int i = 9;
    Derived d1(i);
    return 0;
}

Output results:

Base class constructor with parameters 9

Derived class constructor with parameters 9

Derived class destructor

Base class deconstruction

Memory distribution of programs

Stack space, heap space, data area, code segment

Stack space: local variable, function parameter, automatic variable. Stack space features, first in and last out, and the space is managed by the system; The stack space life cycle is at the end of the function execution
Post release; When the local variables saved in the stack space are not initialized, they are initialized to random values by default.

Heap space: the heap space of control bits allocated by malloc, calloc and ralloc. Heap space features: first in first out, managed by users

Data area (static area): divided into BSS segment Data section and constant area. Among them The BSS segment stores uninitialized global variables. When global variables are uninitialized, the system initializes to 0 by default. Constants are saved in the constant area. The values saved in the constant area cannot be modified and can only be read The data section stores the initialized global variables and the static modified variables (static variables). The declaration cycle of the data area is released after the execution of the whole program.

Code snippet: save the code.

The difference between copy constructor and assignment function. The equal sign is when the copy constructor appears

1. Object is initialized with another object when it is created

Person p(q); // At this point, the copy constructor is used to create the instance P
Person p = q; // At this point, the copy constructor is used to initialize instance P when defining instance P

2. Object as an argument to a function

f(p); // At this time, P is passed as a parameter of the function. When p is stacked, the copy constructor will be called to create a local object with the same scope as the local variable in the function

It should be noted that assignment does not call the copy constructor, but the assignment operator (overload) works

p = q; // There is no call to copy constructor at this time!

Simply remember, if an object is declared and another existing object is assigned to it, the copy constructor will be called; If an object already exists and another existing object is assigned to it, the assignment operator (overload) is called

The default copy constructor and assignment operator are “share copy”, which simply copy fields. Therefore, if the object contains dynamically allocated memory, we need to rewrite the copy constructor or overload the assignment operator to implement “deep copy” to ensure data integrity and security.

When the assignment operator needs to be overloaded.

When we need to make a deep copy, we need to overload the assignment operator, that is, when a class needs to apply for resources

Deep and shallow copies

In some cases, member variables in the class need to dynamically open up heap memory. If bit copy is implemented, that is, the value in the object is completely copied to another object, such as a = B. At this time, if a member variable pointer in B has applied for memory, the member variable in a also points to the same block of memory. There is a problem: when B releases the memory (e.g. destruct), the pointer in a is a wild pointer, and a running error occurs.

Deep copy and shallow copy can be simply understood as: if a class has resources, when the object of this class is copied, the resource is reallocated, which is a deep copy. On the contrary, if the resource is not reallocated, it is a shallow copy. Here is an example of a deep copy.

#include <iostream>
using namespace std;
class CA
{
 public:
  CA(int b,char* cstr)
  {
   a=b;
   str=new char[b];
   strcpy(str,cstr);
  }
  CA(const CA& C)
  {
   a=C.a;
   str=new char[a]; // Deep copy
   if(str!=0)
    strcpy(str,C.str);
  }
  void Show()
  {
   cout<<str<<endl;
  }
  ~CA()
  {
   delete str;
  }
 private:
  int a;
  char *str;
};
 
int main()
{
 CA A(10,"Hello!");
 CA B=A;
 B.Show();
 return 0;
}

Investigate the timing of virtual function calls

B and C are derived from a, and D is integrated from B and C. what’s the problem

The difference between heap sorting and stack memory, and their responsibilities

The difference between new and malloc

(1) Malloc and new both open up memory on the heap
Malloc is only responsible for developing memory, and has no initialization function. Users need to initialize themselves; New not only opens up memory, but also can be initialized, such as new int (10); Indicates that a 4-byte int shaping memory is opened up on the heap. The initial value is 10. For example, new int [10] (); Indicates that an array containing 10 shaping elements is opened on the heap, and the initial value is 0.

(2) Malloc is a function that requires the number of bytes passed in to open up memory, such as malloc (100); Indicates that 100 bytes of memory has been opened up on the heap, and the return void * indicates the starting address of the allocated heap memory. Therefore, the return value of malloc needs to be forcibly converted to the address of the specified type; New is an operator. It needs to specify the type to open up memory and return the address of the specified type. Therefore, no forced conversion is required.

If there is an error on the heap:

int *p1 = (int*)malloc(sizeof(int));   
=>The memory is opened up according to the number of bytes passed in and is not initialized

int *p2 = new int(0); 
=>Open up a shaping memory according to the specified type int and initialize to 0

int *p3 = (int*)malloc(sizeof(int)*100);  
=>Open up 400 bytes of memory, which is equivalent to an array containing 100 shaping elements. It is not initialized

int *p4 = new int[100]();  
=>Open up 400 bytes of memory and an integer array of 100 elements, all of which are initialized to 0

(3)Malloc failed to open up memory and returned null. New failed to open up memory and threw bad_ Alloc type exception, you need to catch exceptions to judge whether the memory development is successful or failed. The new operator is actually a call to the operator new function. Its underlying call is malloc to open up memory. New has more initialization functions than malloc. For class types, the so-called initialization is to call the corresponding constructor.

try {
            int* p = new int[SIZE];
            //Other codes
        } catch ( const bad_alloc& e ) {
            return -1;
        }

(4)Malloc’s memory is always released through free; For new single element memory, delete is used. For new [] array, delete [] is used to free memory

(5) Malloc has only one way to open up memory, while new has four types: ordinary new (bad_alloc exception thrown when memory development fails), nothlow version of new, const new and locating new.

Note here: if you ask malloc, you may also ask memcpy, etc. whether realloc functions can be used in C + +, absolutely not, because these functions copy memory values (that is, shallow copies of objects), which will lead to the serious problem of shallow copies!

The difference between vector and list

1. Vector data structure
Like an array, a vector has a continuous memory space and the starting address remains unchanged.
Therefore, it can carry out random access efficiently, and the time complexity is O (1);
However, because the memory space is continuous, the insertion and deletion operations will cause the copy of memory blocks, and the time complexity is O (n).
In addition, when the memory space in the array is insufficient, a new memory space will be applied for and a memory copy will be made.

2. List data structure
List is implemented by a two-way linked list, so the memory space is discontinuous.
The data can only be accessed through pointers, so the random access of list is very inefficient, and the time complexity is O (n);
However, due to the characteristics of linked list, it can insert and delete efficiently.

3. Difference between vector and list
Vector and list have different support for iterators.
The same thing is that both vector < int >:: iterator and list < int >:: iterator overload the “+ +” operation.
The difference is that the iterator supports “+”, “+ =”, “<” and other operations in the vector, but not in the list.
Let’s look at a simple example of using vector and list:

#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main()
{
    vector<int> v;
    list<int> l;
    For (int i = 0; I < 8; I + +) // / add elements to V and L respectively
    {
        v.push_back(i);
        l.push_back(i);
    }
    cout<<"v[2]="<<v[2]<<endl;
    //cout<<"l[2]="<<l[2]<<endl;  // Compilation error, list does not overload []
    cout<<(v.begin()<v.end())<<endl; 
    //cout<<(l.begin()<l.end())<<endl; / Compilation error, list:: iterator does not overload < or >
    cout<<*(v.begin()+1)<<endl;
    //cout<<*(l.begin()+1)<<endl; // Compilation error, list:: iterator is not overloaded+
    vector<int>::iterator itv=v.begin();
    list<int>::iterator itl=l.begin();
    itv = itv+2;
    //itl=itl+2; // Compilation error, list:: iterator is not overloaded+
    itl++; // List:: iterator is overloaded with + +, and can only be accessed iteratively with + +.
    itl++;
    cout<<*itv<<endl;
    cout<<*itl<<endl;
    getchar();
    return 0;
}

Virtual function, destructor, under what circumstances will lead to memory leakage.

#include <iostream>
using namespace std;

class Father
{
public:
    Father(){cout<<"contructor Father!"<<endl;};
    virtual ~Father(){cout<<"destructor Father!"<<endl;};
};

class Son:public Father
{
public:
    Son(){cout<<"contructor Son!"<<endl;};
    ~Son(){cout<<"destructor Son!"<<endl;};
};


int main(int argc, char* argv[])
{

    Father *pfather=new Son;
    delete pfather;
    pfather=NULL;

    return 0;
}

Tencent interview questions C + + basic chapter

#include <iostream>
using namespace std;

class Father
{
public:
    Father(){cout<<"contructor Father!"<<endl;};
     ~Father(){cout<<"destructor Father!"<<endl;};
};

class Son:public Father
{
public:
    Son(){cout<<"contructor Son!"<<endl;};
    ~Son(){cout<<"destructor Son!"<<endl;};
};


int main(int argc, char* argv[])
{

    Father *pfather=new Son;
    delete pfather;
    pfather=NULL;

    return 0;
}

Tencent interview questions C + + basic chapter

If memory space is requested in a derived class, it is released in its destructor. Assuming that a non virtual destructor is used in the base class, when the derived class object pointed to by the base class pointer is deleted, the dynamic binding will not be triggered, so only the destructor of the base class will be called, not the destructor of the derived class. In this case, the space requested in the derived class cannot be released, resulting in memory leakage.

The difference between rewriting and overloading

(1) Scope difference: the rewritten and rewritten functions are in different classes, and the overloaded and overloaded functions are in the same class.

(2) Parameter difference: the parameter list of rewritten and rewritten functions must be the same, and the parameter list of overloaded and overloaded functions must be different.

(3) The difference between virtual: the rewritten base class must be modified by virtual. Overloaded functions and overloaded functions can be modified by virtual or not.

How to implement polymorphism in C + +, that is, how to implement virtual functions (virtual function table)?

Virtual tables belong to classes, not to a specific object,A class only needs a virtual table。 All objects of the same class use the same virtual table.
In order to specify the virtual table of the object,The object contains a pointer to a virtual table to point to the virtual table it uses。 In order to have a virtual table pointer for each class object containing a virtual table, the compiler adds a pointer to the class*__ VPTR, used to point to the virtual table. In this way, when the class object is created, it has this pointer, and the value of this pointer will be automatically set to point to the virtual table of the class.
Principle of virtual function table

class A {
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};
 
class B : public A {
public:
    virtual void vfunc1();
    void func1();
private:
    int m_data3;
};
 
class C: public B {
public:
    virtual void vfunc2();
    void func2();
private:
    int m_data1, m_data4;
};

Tencent interview questions C + + basic chapter

Public / private / protected which can be inherited by subclasses

Tencent interview questions C + + basic chapter

The return values of the function are string and string *. Is there a problem

C + + RVO mechanism

Tell me about the design patterns you know (single example mode and factory mode)

Overloading, redefinition and rewriting of functions

answer:
1. Override: polymorphism between parent and child classes. Subclass

Redefine virtual functions with the same name and parameters in the parent class.

2. Overload: it means that the function name is the same, but its parameter table has more than one column

Number or order, different types. However, it cannot be judged by the return type.

3. Redefining a subclass redefining a parent class with the same name

Non virtual function (the parameter list can be different).

Can member variables in the class be set with memset? What’s the problem?

Answer: No. This is not to say no, not really no, but really don’t! In some cases, it can be used because the class is only a description and the object is also a materialized memory block of the class. When you memset an object, it initializes the memory of the object. There will be no problem without affecting the internal structure. This is why sometimes there will be no error when you use memset an object. If the class contains virtual functions, you cannot initialize the class object with memset.

class GraphicsObject{
protected:
char *m_pcName;
int    m_iId;
//etc
public:
virtual void Draw() {}
virtual int Area() {}
char* Name() { return m_pcName;}
};

class Circle: public GraphicsObject{
void Draw() { /*draw something*/ }
int Area() { /*calculate Area*/ }
};

void main()
{
GraphicsObject *obj = new Circle; //  create object
memset((void *)obj,NULL,sizeof(Circle)); // memset to 0
obj->Name(); //  No problem with non virtual function calls
obj->Draw(); //  It's ugly to die here
}

As a result, I stopped talking.Because every class object containing virtual functions has a pointer to the virtual function table (VTBL)。 This pointer is used to solve the problem of calling virtual functions at run time and dynamic type cast. The pointer is hidden and inaccessible to programmers.When the memset operation is performed, the value of this pointer will also be overwrittenIn this way, as soon as the virtual function is called, the program crashes. This is easy for many programmers from C to C + +, and it is difficult to check.
To avoid this, remember that for class objects with virtual functions, memset must never be used for initialization. Instead, initialize member variables with the default constructor or other init routines.

Intelligent pointer explanation

Four smart pointers in C + +: Auto_ ptr, unique_ ptr,shared_ ptr,weak_ PTR, of which the last three are supported by C + + 11, and the first has been abandoned by C + + 11.

Why to use smart pointers: we know that the memory management of C + + is a headache for many people. When we write a new statement, we usually write the delete statement directly, but we can’t avoid jumping before the program is deleted or returning without executing the last delete statement in the function, If we do not release resources before every statement that may jump or return, we will cause memory leakage. Using smart pointers can largely avoid this problem, because smart pointers are a class. When beyond the scope of the class, the class will automatically call the destructor, and the destructor will automatically release resources.

How to solve the pointer failure problem?

What is the process of C language function call?

Most program implementations on CPU use stack to support function call operation. Stack is used to transfer function parameters, store return information, temporarily save the original value of register for recovery, and store local variables.

The stack part used for function call operation is called stack frame structure. Each function call has its own stack frame structure. The stack frame structure is specified by two pointers, frame pointer (pointing to the start) and stack pointer (pointing to the top of the stack). The function accesses most data based on frame pointer.
Stack pointer and frame pointer generally have special registers. EBP register is usually used as frame pointer and ESP register is used as stack pointer.

The frame pointer points to the head of the stack frame structure, which stores the head address of the previous stack frame, and the stack pointer points to the top of the stack.

C language parameter stack order?

From right to left.

To answer this question, we have to talk about the printf () function. The prototype of the printf function is printf (const char * format,…)

Yes, it is an indefinite parameter function, so how do we know the number of parameters in practical use? This depends on format. The compiler determines the number of parameters by the number of% placeholders in format.

Now let’s assume that the stack pressing order of parameters is from left to right. At this time, when the function is called, format enters the stack first, then each parameter enters the stack, and finally PC enters the stack. At this time, because format is advanced in the stack, it is pressed with an unknown number of parameters. If you want to know the number of parameters, you must find format, and if you want to find format, you must know the number of parameters, This will fall into an unsolvable loop!!

What happens if the parameters are pressed from right to left? When calling a function, first press several parameters into the stack, then press format, and finally press PC. in this way, the pointer at the top of the stack plus 2 will find the format. Through the% placeholder in the format, obtain the number of subsequent parameters, so as to correctly obtain all parameters.

How does the C language handle return values?

When the variable value is returned, the value of the local variable is directly passed to the register eax, that is, after the function returns, although the local variable has been released, there is also a copy of the value in eax.

When returning the pointer, the instruction is usedlea, the function of this instruction is to pass the address of the data unit [ebp-4] (this unit corresponds to the data storage location of the variable local_data on the stack) to the eax register. However, the temporary data storage unit opened on the stack like this will release the data inside as long as the function is called. Therefore, although a pointer is returned, The data pointed to by the pointer has been destroyed by the system, which leads to the return pointer pointing to unpredictable data.

Refer to this article

An unordered array of integers so that odd numbers precede and all even numbers follow

Characteristics of C + + 11

Intelligence question: 36 horses, 6 tracks, no stopwatch, choose the top three, how many rounds at least

It should be 8 times
(1) First, 36 horses are divided into 6 groups, and the first place in each group can be determined in 6 races;
(2) The first group in each group is in a competition. The group that gets the first place, the group that gets the second place, and the group that gets the third place continue. The group that gets the fourth place, the group that gets the fifth place and the group that gets the fourth place do not need to compete, because the best place of the group that gets the fourth place is that the fourth place cannot be ranked in the top three, and the group that gets the fifth place will have no chance. All those who get the next round of competition are:
The first, second and third place of the first group, and the first and second place of the second group (the third is not necessary, because the best result of the second group is the second), and the first place of the third group (a total of 6) can be ranked in the top three just after one Dojo; All 6 + 1 + 1 = 8 games in total

Recommended Today

Summary of import and export usage in JavaScript

import import 和 require 的区别 import 和js的发展历史息息相关,历史上 js没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。这对开发大型工程非常不方便。在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。也就是我们常见的 require 方法。 比如 `let { stat, exists, readFile } = require(‘fs’);` 。ES6 在语言标准的层面上,实现了模块功能。ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。 import 的几种用法: 1. import defaultName from ‘modules.js’; 2. import { export } from ‘modules’; 3. import { export as ex1 } from […]