面試總結:鵝廠Linux後臺開發面試筆試C++知識點參考筆記

檸檬橙1024發表於2020-05-16

文章每週持續更新,各位的「三連」是對我最大的肯定。可以微信搜尋公眾號「 後端技術學堂 」第一時間閱讀(一般比部落格早更新一到兩篇)

文章是由自己筆試面試騰訊的筆記整理而來,整理的時候又回顧了一遍,中間工作忙斷斷續續整理了半個月,才完成現在的樣子。主要是針對面試的C++後臺開發崗位,涵蓋了大部分C++相關的可能會被問到的技術點,作為面試技術的參考回頭查閱。

文末提供了本文知識點學習資源獲取方式,需要的同學自取。

這篇筆記是基礎C++知識點總結,沒有過多的闡述後臺開發的系統架構和分散式後臺服務設計相關,還有c++11新特性,這些筆試面試也會被問到但不在這篇討論範圍,可以關注專欄後面如果有機會再補上。

為什麼解構函式要是虛擬函式?

基類指標可以指向派生類的物件(多型性),如果刪除該指標delete []p;就會呼叫該指標指向的派生類解構函式,而派生類的解構函式又自動呼叫基類的解構函式,這樣整個派生類的物件完全被釋放。如果解構函式不被宣告成虛擬函式,則編譯器實施靜態繫結,在刪除基類指標時,只會呼叫基類的解構函式而不呼叫派生類解構函式,這樣就會造成派生類物件析構不完全。所以,將解構函式宣告為虛擬函式是十分必要的。

gdb除錯命令

step和next的區別?

當前line有函式呼叫的時候,next會直接執行到下一句 ,step會進入函式.

檢視記憶體

(gdb)p &a //列印變數地址

gdb)x 0xbffff543 //檢視記憶體單元內變數

0xbffff543: 0x12345678

(gdb) x /4xb 0xbffff543 //單位元組檢視4個記憶體單元變數的值

0xbffff543: 0x78 0x56 0x34 0x12

多執行緒除錯

(gdb) info threads:檢視GDB當前除錯的程式的各個執行緒的相關資訊

(gdb) thread threadno:切換當前執行緒到由threadno指定的執行緒

break filename:linenum thread all 在所有執行緒相應行設定斷點,注意如果主執行緒不會執行到該行,並且啟動all-stop模式,主執行緒執行n或s會切換過去

set scheduler-locking off|on\step 預設off,執行s或c其它執行緒也同步執行。on,只有當前相稱執行。step,只有當前執行緒執行

show scheduler-locking 顯示當前模式

thread apply all command 每個執行緒執行同意命令,如bt。或者thread apply 1 3 bt,即執行緒1,3執行bt。

檢視呼叫堆疊

(gdb)bt

(gdb)f 1 幀簡略資訊

(gdb)info f 1 幀詳細資訊

斷點

b test.cpp:11

b test.cpp:main

gdb attach 除錯方法:

gdb->file xxxx->attach pid->這時候程式是停止的->c 繼續執行

帶引數除錯

輸入引數命令set args 後面加上程式所要用的引數,注意,不再帶有程式名,直接加引數,如:

(gdb)set args -l a -C abc

list命令

list linenum  顯示程式第linenum行的周圍的程式

list function  顯示程式名為function的函式的源程式

static關鍵字的作用

軟硬連結

ln -s 原始檔 目標檔案, ln -s / /home/good/linkname連結根目錄/到/home/good/linkname

1、軟連結就是:“ln –s 原始檔 目標檔案”,只會在選定的位置上生成一個檔案的映象,不會佔用磁碟空間,類似與windows的快捷方式。

2、硬連結ln原始檔目標檔案,沒有引數-s, 會在選定的位置上生成一個和原始檔大小相同的檔案,無論是軟連結還是硬連結,檔案都保持同步變化。

函式指標

函式指標 int (*func)(int, int)

函式指標陣列 int (*funcArry[10])(int, int)

const int* p; 指向const int的指標

int const* p; 同上

int* const p; const指標

設計模式

單例模式

觀察者模式(也叫釋出訂閱模式)

工廠模式 三種:簡單工廠模式、工廠方法模式、抽象工廠模式

為什麼要用工廠模式?原因就是對上層的使用者隔離物件建立的過程;或者是物件建立的過程複雜,

使用者不容易掌握;或者是物件建立要滿足某種條件,這些條件是業務的需求也好,是系統約束也好

,沒有必要讓上層使用者掌握,增加別人開發的難度。所以,到這時我們應該清楚了,無論是工廠模式,

還是上面的戰友說的開閉原則,都是為了隔離一些複雜的過程,使得這些複雜的過程不向外暴露,

如果暴露了這些過程,會對使用者增加麻煩,這也就是所謂的團隊合作。

資料結構

各種排序演算法

堆排序

關鍵:1.初始建堆從最後一個非葉節點開始調整 2.篩選從頂點開始往下調整

通俗易懂的快排

二叉樹定理

度為2節點數 = 葉子節點數 - 1

證明:樹枝數=節點數-1, n00 +n11 +n2*2 = n0+n1+n2-1 (n0代表度為0的節點數,以此類推)

互斥鎖

pthread_mutex_t m_mutex;
pthread_mutex_init(&m_mutex, NULL)等效於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);
}

大小端轉換

#define BigLittleSwap32(A) ((((uint32)(A) & 0xff000000) >> 24) | \
                     (((uint32)(A) & 0x00ff0000) >> 8) | \
                     (((uint32)(A) & 0x0000ff00) << 8) | \
                     (((uint32)(A) & 0x000000ff) << 24))

io多路複用

為什麼 IO 多路複用要搭配非阻塞IO

設定非阻塞 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 < 0) perror (“select”);
else if (ret = = 0) printf(“time out”);
else
{
	printf(“ret = %d/n”,ret);
	if(FD_ISSET(socket, &rdfds)){
  	/* 讀取socket控制程式碼裡的資料 */
}注意select函式的第一個引數,是所有加入集合的控制程式碼值的最大那個那個值還要加1.比如我們建立了3個控制程式碼;

poll實現

poll的實現和select非常相似,只是描述fd集合的方式不同,poll使用pollfd結構而不是select的fd_set結構,其他的都差不多,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll沒有最大檔案描述符數量的限制。poll和select同樣存在一個缺點就是,包含大量檔案描述符的陣列被整體複製於使用者態和核心的地址空間之間,而不論這些檔案描述符是否就緒,它的開銷隨著檔案描述符數量的增加而線性增大。

epoll原理

https://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

#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);

epoll對檔案描述符的操作有兩種模式:LT(level trigger)和ET(edge trigger)。LT模式是預設模式,LT模式與ET模式的區別如下:

LT模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式可以不立即處理該事件。下次呼叫epoll_wait時,會再次響應應用程式並通知此事件。

ET模式:當epoll_wait檢測到描述符事件發生並將此事件通知應用程式,應用程式必須立即處理該事件。如果不處理,下次呼叫epoll_wait時,不會再次響應應用程式並通知此事件。

ET模式在很大程度上減少了epoll事件被重複觸發的次數,因此效率要比LT模式高。epoll工作在ET模式的時候,

必須使用非阻塞套介面,以避免由於一個檔案控制程式碼的阻塞讀/阻塞寫操作把處理多個檔案描述符的任務餓死。

Epoll ET模型下,為什麼每次EPOLLIN事件都會帶一次EPOLLOUT事件: https://bbs.csdn.net/topics/390630226

udp套接字

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); 

網路套接字

udp原理與套接字

udp服務端:

sockListener=socket(AF_INET,SOCK_DGRAM,0)

bind(sockListener,(struct sockaddr*)&addrListener,sizeof(addrListener))

nMsgLen=recvfrom(sockListener,szBuf,1024,0,(struct sockaddr*)&addrClient,&addrLen)   

udp客戶端

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;
    }
}    

UDP中使用 connect 函式成為已連線的套接字

已連線 UDP 套接字 相對於 未連線 UDP 套接字 會有以下的變化:

  1. 不能給輸出操作指定目的 IP 地址和埠號(因為呼叫 connect 函式時已經指定),即不能使用 sendto 函式,而是使用 write 或 send 函式。寫到已連線 UDP 套接字上的內容都會自動傳送到由 connect 指定的協議地址;

  2. 不必使用 recvfrom 函式以獲悉資料包的傳送者,而改用 read、recv 或 recvmsg 函式。在一個已連線 UDP 套接字上,由核心為輸入操作返回的資料包只有那些來自 connect 函式所指定的協議地址的資料包。目的地為這個已連線 UDP 套接字的本地協議地址,發源地不是該套接字早先 connect 到的協議地址的資料包,不會投遞到該套接字。即只有發源地的協議地址與 connect 所指定的地址相匹配才可以把資料包傳輸到該套接字。這樣已連線 UDP 套接字只能與一個對端交換資料包;

  3. 由已連線 UDP 套接字引發的非同步錯誤會返回給它們所在的程式,而未連線 UDP 套接字不會接收任何非同步錯誤;

tcp套接字

服務端:

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)

客戶端:

sockfd = socket(AF_INET , SOCK_STREAM , 0)

connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr))

write(sockfd , sendline , strlen(sendline))

在這裡插入圖片描述

IP分片與重組

參考1

參考2

MTU是1500是指的乙太網的MTU,可以用 netstat -i 命令檢視這個值。如果IP層有資料包要傳,而且資料包的長度超過了MTU,

那麼IP層就要對資料包進行分片(fragmentation)操作,使每一片的長度都小於或等於MTU。

我們假設要傳輸一個UDP資料包,乙太網的MTU為1500位元組,一般IP首部為20位元組,UDP首部為8位元組,資料的淨荷(payload)

部分預留是1500-20-8=1472位元組。如果資料部分大於1472位元組,就會出現分片現象,

偏移量的單位為8Byte

以ID標示是不是同一個分片,以偏移量標示在保文裡的位置,每個不完整的ID報文有一個等待計時器,到時丟棄IP層不保證能夠送達,

如果丟了上層自己處理參考rfc 791

IP報文長度單位口訣

4位元組單位- 首部長度單位 1位元組單位-總長度單位 8位元組單位-片偏移單位

STL容器

vector與list

1.vector資料結構

vector和陣列類似,擁有一段連續的記憶體空間,並且起始地址不變。

因此能高效的進行隨機存取,時間複雜度為o(1);

但因為記憶體空間是連續的,所以在進行插入和刪除操作時,會造成記憶體塊的拷貝,時間複雜度為o(n)。

另外,當陣列中記憶體空間不夠時,會重新申請一塊記憶體空間並進行記憶體拷貝。

2.list資料結構

list是由雙向連結串列實現的,因此記憶體空間是不連續的。

只能通過指標訪問資料,所以list的隨機存取非常沒有效率,時間複雜度為o(n);

但由於連結串列的特點,能高效地進行插入和刪除。

Vector動態記憶體分配

這個問題其實很簡單,在呼叫push_back時,若當前容量已經不能夠放入心得元素(capacity=size),那麼vector會重新申請一塊記憶體,把之前的記憶體裡的元素拷貝到新的記憶體當中,然後把push_back的元素拷貝到新的記憶體中,最後要析構原有的vector並釋放原有的記憶體。所以說這個過程的效率是極低的,為了避免頻繁的分配記憶體,C++每次申請記憶體都會成倍的增長,例如之前是4,那麼重新申請後就是8,以此類推。當然不一定是成倍增長,比如在我的編譯器環境下實測是0.5倍增長,之前是4,重新申請後就是6

TinySTL

預處理指令

#pragma once 防止標頭檔案重複引用

一位元組對齊

#pragma pack(push, 1)

#pragma pack(pop)

class物件導向

類繼承

class LayerManager : public ILayerManager{};

覆蓋虛擬函式機制

在某些情況下,希望覆蓋虛擬函式機制並強制函式呼叫使用虛擬函式的特定版

本,這裡可以使用作用域操作符:

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);

這段程式碼強制將 net_price 呼叫確定為 Item_base 中定義的版本,該呼叫

將在編譯時確定。

只有成員函式中的程式碼才應該使用作用域操作符覆蓋虛擬函式機制。

為什麼會希望覆蓋虛擬函式機制?最常見的理由是為了派生類虛擬函式呼叫基

類中的版本。在這種情況下,基類版本可以完成繼承層次中所有型別的公共任務,

而每個派生型別只新增自己的特殊工作。例如,可以定義一個具有虛操作的 Camera 類層次。Camera 類中的 display

函式可以顯示所有的公共資訊,派生類(如 PerspectiveCamera)可能既需要顯

示公共資訊又需要顯示自己的獨特資訊。可以顯式呼叫 Camera 版本以顯示公共

資訊,而不是在 PerspectiveCamera 的 display 實現中複製 Camera 的操作。

在這種情況下,已經確切知道呼叫哪個例項,因此,不需要通過虛擬函式機制。

派生類虛擬函式呼叫基類版本時,必須顯式使用作用域操作符。

如果派生類函式忽略了這樣做,則函式呼叫會在執行時確定並

且將是一個自身呼叫,從而導致無窮遞迴。

名字衝突與繼承

雖然可以直接訪問基類成員,就像它是派生類成員一樣,但是成員保留了它

的基類成員資格。一般我們並不關心是哪個實際類包含成員,通常只在基類和派

生類共享同一名字時才需要注意。

與基類成員同名的派生類成員將遮蔽對基類成員的直接訪問。

struct Base
{
    Base(): mem(0) { }
    protected:
    int mem;
};

struct Derived : Base 
{
    Derived(int i): mem(i) { } // initializes Derived::mem
    int get_mem() { return mem; } // returns Derived::mem
    protected:
    int mem; // hides mem in the base
};

get_mem 中對 mem 的引用被確定為使用 Derived 中的名字。如果編寫如下程式碼:
Derived d(42);
cout << d.get_mem() << endl; // prints 42

則輸出將是 42。

使用作用域操作符訪問被遮蔽成員

可以使用作用域操作符訪問被遮蔽的基類成員:

struct Derived : Base 
{
	int get_base_mem() { return Base::mem; }
};

作用域操作符指示編譯器在 Base 中查詢 mem。

設計派生類時,只要可能,最好避免與基類資料成員的名字相同

類成員函式的過載、覆蓋和隱藏區別?

a.成員函式被過載的特徵:

(1)相同的範圍(在同一個類中);

(2)函式名字相同;

(3)引數不同;

(4)virtual 關鍵字可有可無。

b.覆蓋是指派生類函式覆蓋基類函式,特徵是:

(1)不同的範圍(分別位於派生類與基類);

(2)函式名字相同;

(3)引數相同;

(4)基類函式必須有virtual 關鍵字。

c.“隱藏”是指派生類的函式遮蔽了與其同名的基類函式,規則如下:

(1)如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual關鍵字,基類的函式將被隱藏(注意別與過載混淆,僅同名就可以)。

(2)如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual 關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)

純虛擬函式

class Disc_item : public Item_base 

{
    public:
    double net_price(std::size_t) const = 0;
};

含有(或繼承)一個或多個純虛擬函式的類是抽象基類。除了作

為抽象基類的派生類的物件的組成部分,甚至不能建立抽象型別Disc_item的物件。

模板程式設計

函式模板

template <typename T> 
int compare(const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}


使用compare(1, 2)

類别範本

template <class Type> class Queue 

{

public:

    Queue (); // default constructor
    Type &front (); // return element from head of Queue
    const Type &front () const;
    void push (const Type &); // add element to back of Queue
    void pop(); // remove element from head of Queue
    bool empty() const; // true if no elements in the Queue
    private:
    // ...
};

使用Queue qi;

操作符過載

輸出操作符

輸出操作符通常是非成員函式,定義成類的友元

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;
}

算術和關係操作

算術和關係操作符定義為非成員函式

為了與內建操作符保持一致,加法返回一個右值,而不是一個引用。

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);
}

複製控制

包括,一個拷貝建構函式,一個賦值運算子,一個解構函式,一對取址運算子

如果你這麼寫:class Empty{};

和你這麼寫是一樣的:

class Empty 
{
    public:
    Empty();            // 預設建構函式
    Empty(const Empty& rhs);    // 拷貝建構函式
    ~Empty();            // 解構函式 ---- 是否
             // 為虛擬函式看下文說明
     Empty& operator=(const Empty& rhs);  // 賦值運算子
     Empty* operator&();       // 取址運算子
     const Empty* operator&() const;
};

Empty(const Empty& rhs)
{
    a = rhs.a
}

類賦值操作符必須是類的成員,以便編譯器可以知道是否需要合成一個, 賦值必須返回對 *this 的引用。

一般而言,賦值操作符與複合賦值操作符應返回操作符的引用

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;
};

注意,檢查對自己賦值的情況
c& c::operator=(const c& rhs)
{
 // 檢查對自己賦值的情況
 if (this == &rhs) return *this;
 ...
}

建構函式初始化式

初始化const物件和引用物件的唯一機會。P389 C++ Primer 5th

協議

RTP/RTSP/RTCP

RTP協議RFC1889和RFC3550 G711 PCMU

HTTP

Linux基礎

Linux shell之陣列:https://www.cnblogs.com/Joke-Shi/p/5705856.html

Linux expr命令:http://www.runoob.com/linux/linux-comm-expr.html

shell中變數型別:local,global,export關鍵字: https://www.cnblogs.com/kaishirenshi/p/10274179.html

Linux let 命令:http://www.runoob.com/linux/linux-comm-let.html

vim修改tab成4個空格寫python: http://www.cnblogs.com/wi100sh/p/4938996.html

python判斷檔案是否存在的幾種方法: https://www.cnblogs.com/jhao/p/7243043.html

python--檔案操作刪除某行: https://www.cnblogs.com/nopnog/p/7026390.html

pytho3字典遍歷的幾種操作: https://www.jb51.net/article/138414.htm

chmod

命令名稱: chmod

執行許可權: 所有使用者

功能描述: 改變檔案或目錄許可權

語法: 第一種方法 chmod [{ugoa}{+-=}{rwx}] [檔案或目錄]

      備註:       u:所有者  g:所屬組  o:其他人  a:所有人

                  +:為使用者增加許可權   -:為使用者減少許可權   =:為使用者賦予許可權

                  r:讀許可權   w:寫許可權   x:執行許可權 





      第二種方法   chmod  -R  [mode=421]   [檔案或目錄]   ←(這種方法用的比較多)

       備註:      r:4  w:2  x:1

                  r為讀許可權,可以用4來表示,

                  w為寫許可權,可以用2來表示,

                  x為執行許可權,可以用1來表示。

new操作

動態分配陣列int *pia = new int[10]; // array of 10 uninitialized ints

釋放分配的陣列 delete [] pia;

new陣列

int *arr = new int[1024]
delte [] a
# 堆上new 物件
class MyClass
{
    MyClass(int a) {};
    int empty() {return 0;};
};

MyClass *p = new MyClass(1);
delete p;

# 棧上分配 物件
MyClass test(1);

放置式new

區分以下幾種操作符號:

new operator-普通的new關鍵字

operator new-僅僅申請記憶體返回void*

placement new-在指定記憶體呼叫建構函式初始化類

new [] operator-如果是類物件,會在首部多申請4位元組記憶體用於儲存物件個數

深入探究 new 和 delete https://blog.csdn.net/codedoctor/article/details/76187567

當我們使用關鍵字new在堆上動態建立一個物件A時,比如 A* p = new A(),它實際上做了三件事:

向堆上申請一塊記憶體空間(做夠容納物件A大小的資料)(operator new)

呼叫建構函式 (呼叫A的建構函式(如果A有的話))(placement new)

返回正確的指標

當然,如果我們建立的是簡單型別的變數,那麼第二步會被省略。

當我們delete的時候也是如此,比如我們delete p 的時候,其行為如下:

定位到指標p所指向的記憶體空間,然後根據其型別,呼叫其自帶的解構函式(內建型別不用)

然後釋放其記憶體空間(將這塊記憶體空間標誌為可用,然後還給作業系統)

將指標標記為無效(指向NULL)

https://blog.csdn.net/rain_qingtian/article/details/14225211

void* p=::operator new (sizeof(Buffer));  //建立一塊記憶體;冒號表示全域性的new
Buffer* bp= start_cast<Buffer*>(p);  //指標進行裝換
Buffer* buf3=new(bp) Buffer(128);   //把bp指標指向的記憶體租借buf3,
buf3->put('c');
buf3->~Buffer();  //這裡析夠函式要顯示呼叫
::operator delete(p);

放置new構造物件陣列

放置new物件陣列

在棧上分配類記憶體: https://www.cnblogs.com/weekbo/p/8533368.html

new與malloc區別

b. new和malloc最大區別: new會呼叫類的建構函式,malloc不會;

c. delete和free同理;new/delete是運算子,malloc/free函式。所以new/delete效率應該會高點。

Linux IPC機制彙總

管道

 #include <unistd.h>

無名管道: int pipe(int pipedes[2])

有名管道:int mkfifo(const char *pathname, mode_t mode)

訊息佇列

#include <sys/msg.h>

int msgget(key_t key, int msgflg) //建立

int msgctl(int msqid, int cmd, struct msqid_ds *buf) //設定/獲取訊息佇列的屬性值

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) //傳送訊息到訊息佇列(新增到尾端)

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) //接收訊息

共享記憶體

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg) //建立一個共享記憶體空間

int shmctl(int shmid, int cmd, struct shmid_ds *buf) //對共享記憶體程式操作,包括:讀取/設定狀態,刪除操作

void *shmat(int shmid, const void *shmaddr, int shmflg) //將共享記憶體空間掛載到程式中

int shmdt(const void *shmaddr) //將程式與共享記憶體空間分離 **(****只是與共享記憶體不再有聯絡,並沒有刪除共享記憶體****)**

訊號

#include</usr/include/bits/signum.h>

手動實現strcpy

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 ;
}

C++物件記憶體佈局

這部分詳細內容可以參考深度探索C++物件模型

虛擬函式多型機制

通過虛表指標訪問虛成員函式,對普通成員函式的訪問區別於虛成員函式。具體如下:

virtual member function虛成員函式normalize()的呼叫實際上轉換成:

(*ptr->vpter[1])(ptr)
在這裡插入圖片描述

函式指標也有差別,下面第一個是普通函式指標或者static member function。第二個是non-static member function成員函式指標。
在這裡插入圖片描述

不同繼承層次的物件記憶體佈局

單一繼承

在這裡插入圖片描述
在這裡插入圖片描述

多重繼承

在這裡插入圖片描述
在這裡插入圖片描述
多重繼承2

結語

終於寫完了篇幅較長,寫這篇文章是一方面是希望能給想來鵝廠或者準備面試任何一家公司C++開發的同學一些參考,另一方面是對知識的回顧。對程式設計和技術感興趣的小夥伴可以關注我的公眾號,以後有更新會第一時間推送。

本文提到的後臺開發學習的知識點,我整理了電子書和學習資料,在公眾號 「後端技術學堂」 關注後回覆 「1024」 即可免費獲取,資料和書都是我幾年來學習過程中收集整理的分享給大家。

可以微信搜尋公眾號「 後端技術學堂 」回覆「資料」有我給你準備的各種程式設計學習資料。文章每週持續更新,我們下期見!

相關文章