The articles are updated every week, and your “three companies” are the biggest affirmation for me. You can search the public account “Back-end Technology School” on wechat to read it for the first time (usually one or two posts are updated earlier than the blog).
The article is arranged by the notes of my written interview with Tencent, and then reviewed again. It took me half a month to finish what I am now. Mainly for the interview of C++ background development positions, covering most of the C++ related technical points that may be asked, as the interview technology reference refer back to.
This note is a summary of basic C++ knowledge points. It does not elaborate too much on the system architecture of background development and distributed background service design, as well as the new features of C++ 11. These will also be asked in the written interview, but it is not the scope of this discussion.
Why is a destructor virtual?
A pointer to a base class can point to an object of a derived class (polymorphism) if the pointer is deleted delete []p; The derived destructor to which the pointer points is called, which in turn automatically calls the base destructor, and the entire derived object is freed. If the destructor is not declared as virtual, then the compiler implements static binding and only calls the destructor of the base class and not the derived class when the pointer to the base class is deleted, resulting in incomplete destructor of the derived object. Therefore, it is necessary to declare the destructor as virtual.
GDB debugging command
What’s the difference between Step and Next?
When the current line has a function call,next will execute directly to the next line, and step will enter the function.
Check the memory
(GDB) p&a // Prints the variable address
GDB) x 0xBffff543 // Check the variables in the memory unit
0xbffff543: 0x12345678
(GDB) x /4xb 0xBffFF543 // Single byte View the values of four memory unit variables
0xbffff543: 0x78 0x56 0x34 0x12
Multithreaded debugging
(GDB) info Threads: View information about each thread of the program being debugged by GDB
(GDB) Thread ThreadNo: Switches the current thread to the thread specified by threadno
Break filename:linenum Thread all Sets a breakpoint on the corresponding line of all threads. Note that if the main thread does not execute to this line and the all-stop mode is enabled, the main thread performs n or s and switches over
Set the scheduler – locking off | on \ step off by default, perform s or c other thread synchronization. On, only the current match is executed. Step, executed only by the current thread
Show scheduler-locking Displays the current mode
Thread apply all Command Each thread executes the consent command, such as bt. Or thread apply 1, 3 bt.
Look at the call stack
(gdb)bt
(GDB)f 1 frame brief
(GDB)info f 1 Frame details
The breakpoint
b test.cpp:11
b test.cpp:main
GDB ATTACH debugging method:
GDB ->file XXXX ->attach PID -> At this time the process is stopped -> C continue running
Debugging with parameters
Set args = set args = set args = set args = set args
(gdb)set args -l a -C abc
The list command
List linenum Displays the programs around the linenum line of a program
List function Displays the source of the function named function
The static keyword is used
Hard and soft links
Ln -s Source file Destination file, ln -s /home/good/linkname Links the root directory/to /home/good/linkname
1. Soft link is ln -s source file destination file. It only generates an image of a file in the selected location and does not occupy disk space, similar to the Shortcut of Windows.
2. Hard link Ln Source file The target file does not have the parameter -s. A file with the same size as the source file is generated in the selected location.
A function pointer
Int (*func)(int, int)
Int (*funcArry[10])(int, int)
const int* p; Pointer to const int
int const* p; Same as above
int* const p; Pointer to a const
Design patterns
The singleton pattern
Observer mode (also known as publish subscribe mode)
There are three factory modes: simple factory mode, factory method mode and abstract factory mode
Why factory model? The reason is to isolate the object creation process from upper-level consumers; Or the object creation process is complicated,
Users are not easy to master; Or object creation meets certain conditions, whether they are business requirements or system constraints
, there is no need to let the upper users master, increase the difficulty of others development. So it should be clear by this point that either the factory model,
Or the open and closed principle mentioned by my comrades above, both of which are to isolate some complex processes, so that these complex processes are not exposed to the outside.
Exposing these processes adds to the user’s problems, which is known as teamwork.
The data structure
Various sorting algorithms
Heap sort
Key: 1. The initial heap is adjusted from the last non-leaf node. 2
Easy to understand quick sorting
Binomial tree theorem
The degree is 2. Number of nodes = number of leaf nodes -1
Proof: Number of branches = number of nodes -1, n00 + N11 +n2*2 = N0 + N1 + N2-1 (n0 represents number of nodes with degree 0, and so on)
The mutex
pthread_mutex_t m_mutex; Pthread_mutex_init (&m_MUtex, NULL) is equivalent to pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER pthread_mutex_lock(&m_mutex); pthread_mutex_unlock(&m_mutex) pthread_mutex_destroy(&m_mutex) int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); bool g_flag = false; void* t1(void* arg) { cout << "create t1 thread success" << endl; pthread_mutex_lock(&m_mutex); g_flag = true; pthread_mutex_unlock(&m_mutex); } void* t2(void* arg) { cout << "create t2 thread success" << endl; pthread_mutex_lock(&m_mutex); g_flag = false; pthread_mutex_unlock(&m_mutex); } int main(int argc, char* argv[]) { pthread_t tid1, tid2; pthread_create(&tid1, NULL, t1, NULL); sleep(2); pthread_create(&tid2, NULL, t2, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); }Copy the code
Size end conversion
#define BigLittleSwap32(A) ((((uint32)(A) & 0xff000000) >> 24) | \
(((uint32)(A) & 0x00ff0000) >> 8) | \
(((uint32)(A) & 0x0000ff00) << 8) | \
(((uint32)(A) & 0x000000ff) << 24))
Copy the code
IO multiplexing
Why IO multiplexing with non-blocking IO
Set non-blocking IO FCNTL (sockfd, F_SETFL, O_NONBLOCK);
select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
fd_set rdfds;
struct timeval tv;
int ret;
FD_ZERO(&rdfds);
FD_SET(socket, &rdfds);
tv.tv_sec = 1;
tv.tv_uses = 500;
ret = select (socket + 1, %rdfds, NULL.NULL, &tv);
if(ret < 0Perror (" select ");else if (ret = = 0) printf(" time out ");else
{
printf(" ret = % d/n ", ret);if(FD_ISSET(socket, &rdfds)){
/* Read data from socket handle */} Notice that the first argument to the select function is the maximum value of all the handles added to the collection1.So let's say we created3A handle;Copy the code
Poll implementation
Poll the implementation and the select of very similar, just describe fd set in a different way, poll using pollfd structure rather than the select fd_set structure, all is the same as the other, to manage multiple descriptor is also poll, according to the status of the descriptor for processing, But poll has no limit on the maximum number of file descriptors. A drawback of poll and SELECT is that arrays containing a large number of file descriptors are copied between the user state and the kernel address space as a whole, regardless of whether the file descriptors are ready or not, and their overhead increases linearly with the number of file descriptors.
Epoll principle
www.cnblogs.com/Anker/archi…
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
Copy the code
Epoll operates on file descriptors in two modes: LT (Level trigger) and ET (edge trigger). The LT mode is the default mode. The differences between the LT mode and ET mode are as follows:
LT mode: When epoll_WAIT detects that a descriptor event has occurred and notifies the application of the event, the application may not process the event immediately. The next time epoll_wait is called, the application is responded again and notified of this event.
ET mode: When epoll_WAIT detects that a descriptor event has occurred and notifies the application of the event, the application must process the event immediately. If not, the next time epoll_wait is called, the application will not respond again and be notified of this event.
ET mode greatly reduces the number of epoll events to be triggered repeatedly, so it is more efficient than LT mode. When epoll works in ET mode,
Non-blocking sockets must be used to avoid starving the task of processing multiple file descriptors due to blocking read/blocking write operations on a single file handle.
In the Epoll ET model, why each EPOLLIN event is accompanied by an EPOLLOUT event: bbs.csdn.net/topics/3906…
Udp socket
ref1
ref1
#include <sys/socket.h>
ssize_t sendto(int sockfd, void *buff, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *addr, socklen_t *addrlen);
Copy the code
Network socket
Udp and sockets
Udp server:
sockListener=socket(AF_INET,SOCK_DGRAM,0)
bind(sockListener,(struct sockaddr*)&addrListener,sizeof(addrListener))
nMsgLen=recvfrom(sockListener,szBuf,1024.0,(struct sockaddr*)&addrClient,&addrLen)
Copy the code
Udp client
sockClient=socket(AF_INET,SOCK_DGRAM,0);
bind(sockClient,(struct sockaddr*)&addrLocal,sizeof(addrLocal))
FD_ZERO(&setHold);
FD_SET(STDIN_FILENO,&setHold);
FD_SET(sockClient,&setHold);
cout<<"you can type in sentences any time"<<endl;
while(true)
{
setTest=setHold;
nReady=select(sockClient+1,&setTest,NULL.NULL.NULL);
if(FD_ISSET(0,&setTest))
{
nMsgLen=read(0,szMsg,1024);
write(sockClient,szMsg,nMsgLen);
}
if(FD_ISSET(sockClient,&setTest))
{
nMsgLen=read(sockClient,szRecv,1024);
szRecv[nMsgLen]='\ 0';
cout<<"read:"<<szRecv<<endl; }}Copy the code
UDP uses the connect function to become a connected socket
A connected UDP socket has the following changes compared to an unconnected UDP socket:
-
You cannot specify the destination IP address and port number for the output operation (because it was specified when you called connect), that is, you cannot use sendto, but write or send. Anything written to a connected UDP socket is automatically sent to the protocol address specified by CONNECT.
-
Instead of using the recvFROM function to know the sender of the datagram, use the read, recv, or recvmsg functions. On a connected UDP socket, the only datagrams returned by the kernel for input operations are those from the protocol address specified by the connect function. Datagrams whose destination is the local protocol address of this connected UDP socket and whose source is not the protocol address to which the socket was previously connected will not be delivered to this socket. That is, datagrams can be transmitted to the socket only if the protocol address of the birthplace matches the address specified by CONNECT. Thus connected UDP sockets can exchange datagrams with only one peer;
-
Asynchronous errors raised by connected UDP sockets are returned to their process, while unconnected UDP sockets do not receive any asynchronous errors;
TCP socket
Server:
listenfd = socket(AF_INET , SOCK_STREAM , 0)
bind(listenfd , (struct sockaddr*)&servaddr , sizeof(servaddr))
listen(listenfd , LISTENQ)
connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen))
n = read(connfd , buff , MAX_LINE)
write(connfd , buff , n)
Copy the code
Client:
sockfd = socket(AF_INET , SOCK_STREAM , 0)
connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))
write(sockfd , sendline , strlen(sendline))
Copy the code
IP fragmentation and reassembly
Reference 1
Reference 2
MTU 1500 indicates the MTU of the Ethernet. You can run the netstat -i command to view the MTU. If there is a packet to be transmitted at the IP layer and the packet length exceeds the MTU,
The IP layer divides packets into fragments whose length is smaller than or equal to MTU.
The Ethernet MTU is 1500 bytes, and the IP header is 20 bytes, the UDP header is 8 bytes, and the net payload of the data is transmitted.
Partially reserved: 1500-20-8=1472 bytes. Fragmentation occurs if the data portion is larger than 1472 bytes,
The offset is in 8 bytes
The ID indicates whether the fragment is the same or not, and the offset indicates the position in the security text. Each incomplete ID packet has a waiting timer, and when discarded IP layer, it is not guaranteed to be delivered.
If the upper layer is lost, deal with it by referring to RFC 791
Unit of IP packet length
4 byte unit – Header length unit 1 byte unit – Total length unit 8 byte unit – Slice offset unit
STL containers
The vector with the list
1. Vector data structure
Vector is similar to arrays in that it has a contiguous memory and does not change its starting address.
Therefore, random access can be carried out efficiently and the time complexity is O (1).
However, because the memory space is continuous, memory blocks will be copied during insertion and deletion, and the time complexity is O (n).
In addition, when the memory space in the array is insufficient, a new memory space is allocated and a memory copy is made.
2. List data structure
Lists are implemented by bidirectional linked lists, so memory space is discontinuous.
Data can only be accessed through Pointers, so the random access of list is very inefficient and the time complexity is O (n).
But because of the characteristics of linked lists, they can be inserted and deleted efficiently.
Vector dynamically allocates memory
If capacity=size is insufficient for push_back, the vector allocates a new block of memory, copies the elements from the original memory into the new memory, and copies the elements from push_back into the new memory. Finally, the original vector is destructed and the original memory is freed. Therefore, the efficiency of this process is extremely low. In order to avoid frequent memory allocation, C++ will increase the memory twice each time it is applied, for example, it is 4 before, then it is 8 after reapplied, and so on. Of course, it does not have to be doubled. For example, in my compiler environment, the measured increase is 0.5 times, which was 4 before, and 6 after reapplication
TinySTL
Preprocessing instruction
#pragma once prevents repeated references to header files
One-byte alignment
#pragma pack(push, 1)
#pragma pack(pop)
Class object oriented
Class inheritance
Class LayerManager: public ILayerManager {};
Override virtual function mechanism
In some cases, you want to override the virtual function mechanism and force function calls to use a specific version of the virtual function
Ben, here we can use the scope operator:
Item_base *baseP = &derived;
// calls version from the base class regardless of the dynamic type
of baseP
double d = baseP->Item_base::net_price(42);
This code forces the net_price call to be the version defined in Item_base, the call
Will be determined at compile time.
Only code in member functions should override the virtual function mechanism with scope operators.
Why would you want to override the virtual function mechanism? The most common reason is to call the base for deriving class virtual functions
The version in the class. In this case, the base class version can perform all types of common tasks in the inheritance hierarchy,
Each derived type adds only its own special work. For example, you can define a Camera class hierarchy with virtual operations. Display in Camera class
Function can display all the public information; derived classes (such as PerspectiveCamera) may require both
Displaying public information requires displaying your own unique information. The Camera version can be explicitly called to display public
Rather than copying the actions of the Camera in PerspectiveCamera’s display implementation.
In this case, you already know exactly which instance to call, so you don’t need the virtual function mechanism.
When a derived class virtual function calls the base class version, it must explicitly use the scope operator.
If the derived class function neglects to do so, the function call determines and at run time
And will be a call to itself, resulting in infinite recursion.
Name conflicts and inheritance
Although a base class member can be accessed directly as if it were a derived class member, the member retains it
Base class membership of. Generally we don’t care which actual classes contain members, usually only base classes and pies
Care should be taken when a living class shares the same name.
A derived class member with the same name as a base class member blocks direct access to the base class member.
struct Base
{
Base(): mem(0) {}protected:
int mem;
};
struct Derived : Base
{
Derived(int i): mem(i) { } // initializes Derived::mem
int get_mem(a) { return mem; } // returns Derived::mem
protected:
int mem; // hides mem in the base}; A reference to mem in get_mem is determined to use the name in Derived. If you write the following code:Derived d(42);
cout << d.get_mem() << endl; // prints 42
Copy the code
The output will be 42.
Use scope operators to access masked members
Masked base class members can be accessed using the scope operator:
struct Derived : Base
{
int get_base_mem(a) { returnBase::mem; }};Copy the code
The scope operator instructs the compiler to look for meM in Base.
When designing derived classes, it is best to avoid having the same name as the data members of the base class whenever possible
What are the differences between overloading, overwriting, and hiding class member functions?
A. Member functions are overloaded:
(1) The same scope (in the same class);
(2) The function name is the same;
(3) Different parameters;
(4) The virtual keyword is optional.
B. Overwriting refers to the function of a derived class covering the function of a base class, characterized by:
(1) Different scopes (respectively in derived and base classes);
(2) The function name is the same;
(3) Same parameters;
(4) Base class functions must have the virtual keyword.
C. “hide” means that a function of a derived class hides a function of its base class with the same name. The rule is as follows:
(1) If the function of the derived class has the same name as the function of the base class, but the parameters are different. At this point, functions of the base class are hidden with or without the virtual keyword (not to be confused with overloading, just the same name will do).
(2) If the function of the derived class has the same name as the function of the base class and has the same parameters, but the base class function does not have the virtual keyword. At this point, the functions of the base class are hidden (be careful not to be confused with overwriting)
Pure virtual function
class Disc_item : public Item_base
{
public:
double net_price(std: :size_t) const = 0;
};
Copy the code
Classes that contain (or inherit) one or more pure virtual functions are abstract base classes. In addition to make
Objects that are part of an object derived from an abstract base class cannot even be created for objects of abstract type Disc_item.
Template programming
A function template
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return - 1;
if (v2 < v1) return 1;
return 0;
}
Copy the code
Using the compare (1, 2)
Class template
template <class Type> class Queue
{
public:
Queue (); // default constructor
Type &front (a); // return element from head of Queue
const Type &front (a) const;
void push (const Type &); // add element to back of Queue
void pop(a); // remove element from head of Queue
bool empty(a) const; // true if no elements in the Queue
private:
// ...
};
Copy the code
Using the Queue qi;
Operator overloading
Output operator
Output operators are usually non-member functions defined as friends of the class
friend ostream& operator<<(ostream& out, const Sales_item& s)
{
out << s.isbn << "\t" << s.units_sold << "\t"
<< s.revenue << "\t" << s.avg_price();
return out;
}
Copy the code
Arithmetic and relational operations
Arithmetic and relational operators are defined as nonmember functions
In keeping with the built-in operators, addition returns an rvalue rather than a reference.
Sales_item operator+ (const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs); // copy lhs into a local object that we'll
ret += rhs; // add in the contents of rhs
return ret; // return ret by value
}
int operator< (const TableIndex2D& right) const;
friend bool operator= = (const UEContext& info1,const UEContext& info2) const
{
if(info1.ContextID ! = info2.ContextID)return false;
return true; }friend bool operator! = (const UEContext& info1,const UEContext& info2) const
{
return! (info1 == info2); }Copy the code
Copy control
There’s a copy constructor, an assignment operator, a destructor, and a pair of address operators
If you write: class Empty{};
It’s the same as if you wrote it this way:
class Empty
{
public:
Empty(); // Default constructor
Empty(const Empty& rhs); // Copy the constructor
~Empty(); // Destructor ---- Yes no
// is a virtual function
Empty& operator= (const Empty& rhs); // The assignment operator
Empty* operator& ();// Address operator
const Empty* operator& ()const;
};
Empty(const Empty& rhs)
{
a = rhs.a
}
Copy the code
The class assignment operator must be a member of the class so that the compiler can know if one needs to be synthesized, and the assignment must return a reference to *this.
In general, assignment and compound assignment operators should return a reference to the operator
Guti& Guti::operator= (const Guti& rhs )
{
mtmsi_m = rhs.mtmsi_m;
mmeCode_m = rhs.mmeCode_m;
mmeGid_m = rhs.mmeGid_m;
plmnId_m = rhs.plmnId_m;
return *this; }; Note that check the assignment to self c& c::operator= (const c& rhs)
{
// Check the assignment to yourself
if (this == &rhs) return *this; . }Copy the code
Constructor initializer
The only chance to initialize const and reference objects. P389 C++ Primer 5th
agreement
RTP/RTSP/RTCP
RTP protocols RFC1889 and RFC3550 G711 PCMU
HTTP
The Linux foundation
Linux shell array: www.cnblogs.com/Joke-Shi/p/…
Linux expr command: www.runoob.com/linux/linux…
The shell variable types: local, global, export key word: www.cnblogs.com/kaishirensh…
Linux let command: www.runoob.com/linux/linux…
Vim modify TAB into four Spaces to write python: www.cnblogs.com/wi100sh/p/4…
Python to determine whether a file exists several kinds of methods: www.cnblogs.com/jhao/p/7243…
Python – file operations to delete a line: www.cnblogs.com/nopnog/p/70…
Pytho3 dictionary traversal operations: www.jb51.net/article/138…
chmod
Command name: chmod
Execute permission: all users
Function Description: Change file or directory permissions
Chmod [{ugoa}{+-=}{RWX}] [file or directory]
Note: U: owner G: owning group O: others A: owner
+ : adds permission to a user. – : decreases permission to a user. = : grants permission to a user
R: read permission W: write permission x: execute permission
The second method chmod -r [mode=421] [file or directory] ← (this method is used more often)
Note: R: 4 W: 2 x: 1
R stands for read permission, which can be represented by 4,
W is the write permission, which can be represented by 2,
X is the execution permission, which can be represented by 1.
New operation
Int *pia = new int[10]; // array of 10 uninitialized ints
Delete [] pia;
The new array
int *arr = new int[1024] delte [] a # on the heapnewObject class MyClass {MyClass(int a) {};
int empty(a) {return 0; }; }; MyClass *p =new MyClass(1);
deletep; Allocate objects on the stackMyClass test(1);
Copy the code
Put the new type
Distinguish between the following operation symbols:
New operator- The normal new keyword
Operator new- Returns void* for memory only
Placement New – Calls the constructor to initialize the class in the specified memory
New [] operator- If it is a class object, an additional 4 bytes of memory will be allocated in the header to hold the number of objects
Delve into new and delete blog.csdn.net/codedoctor/…
When we use the keyword new to dynamically create an object A on the heap, such as A* p = new A(), it actually does three things:
Apply A block of memory to the heap (make enough data to hold object A) (operator new)
Call the constructor (call A’s constructor (if A has one)) (Placement new)
Returns the correct pointer
Of course, if we were creating a variable of a simple type, the second step would be omitted.
The same is true when we delete, for example when we delete p, the behavior is as follows:
Locate the memory space pointed to by pointer P and call its built-in destructor based on its type (built-in type is not used).
Then free up its memory space (mark it as available and return it to the operating system)
Mark pointer as invalid (pointing to NULL)
Blog.csdn.net/rain_qingti…
void* p=::operator new (sizeof(Buffer)); / / Create a block of memory; The colon indicates globalnewBuffer* bp= start_cast<Buffer*>(p); / / The pointer is loaded Buffer* buf3=new(bp) Buffer(128); Buf3, buf3->put('c');
buf3->~Buffer(); // Here is the function to display the call: :operator delete(p);
Copy the code
Place new to construct an array of objects
Class of memory allocated on the stack: www.cnblogs.com/weekbo/p/85…
New is different from malloc
B. New calls the constructor of a class, while malloc does not.
C. Delete is the same as free. New /delete is the operator,malloc/free function. So new/ DELETE should be more efficient.
Summary of Linux IPC mechanisms
The pipe
Int mkfifo(const char *pathname, mode_t mode) #include <unistd.h>Copy the code
The message queue
#include <sys/msg.h>
int msgget(key_t key, int msgflg) / / create
int msgctl(int msqid, int cmd, struct msqid_ds *buf) // Sets/gets the message queue attribute value
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) // Send a message to the message queue (add to the tail)
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) // Receive the message
Copy the code
The Shared memory
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg) // Create a shared memory space
int shmctl(int shmid, int cmd, struct shmid_ds *buf) // Perform operations on a shared memory process, including reading/setting the state and deleting the process
void *shmat(int shmid, const void *shmaddr, int shmflg) // Mount the shared memory space to the process
int shmdt(const void *shmaddr) / / separation processes and Shared memory space * * (* * * * just contact with Shared memory no longer have, no delete Shared memory * * * *) * *
Copy the code
signal
#include</usr/include/bits/signum.h>
Implement StrCPy manually
char *strcpy(char *strDest, const char *strSrc)
{
if ( strDest == NULL || strSrc == NULL)
return NULL ;
if ( strDest == strSrc)
return strDest ;
char *tempptr = strDest ;
while( (*strDest++ = *strSrc++) ! = '/0')return tempptr ;
}
Copy the code
C++ object memory layout
For more details, see exploring the C++ object model in depth
Virtual function polymorphism mechanism
Virtual member functions are accessed through a virtual table pointer, and access to normal member functions is different from that of virtual member functions. Details are as follows:
A call to the virtual member function normalize() actually translates to:
(*ptr->vpter[1])(ptr)
Function Pointers are also different. The first one is a normal function pointer or a static member function pointer. The second is a pointer to a non-static member function.
Memory layout of objects at different inheritance levels
Single inheritance
Multiple inheritance
conclusion
Finally finished the long length, writing this article is on the one hand, I hope to give some reference to the students who want to goose factory or prepare to interview any company C++ development, on the other hand, is a review of the knowledge. If you are interested in programming and technology, you can follow my official account, and I will push updates to you as soon as possible.
For the knowledge points of background development and learning mentioned in this paper, I sorted out e-books and learning materials, and put them on the public account “Back-end Technology School” after paying attention to them and replying to “1024” to obtain them.
Original is not easy, see here move a finger, your “three even” is the biggest support for my continuous creation.
More and more
You can search the public number “back-end technology School” on wechat and reply to “information” with all kinds of programming learning materials I prepared for you. This article is updated every week, and we’ll see you next time!