C++接口內(nèi)部?jī)?nèi)存分配問題設(shè)計(jì)方案
1. 為什么要傳入“二級(jí)指針” (**)?
當(dāng)你需要 C++ 內(nèi)部產(chǎn)生一份未知大小的數(shù)據(jù),并把數(shù)據(jù)交還給外部時(shí):
- 如果你傳一級(jí)指針 (
DataPoints* ptr):C++ 內(nèi)部執(zhí)行ptr = new DataPoints[10];時(shí),修改的只是ptr這個(gè)變量在棧上的局部副本。函數(shù)一結(jié)束,外部的指針依然是nullptr,不僅拿不到數(shù)據(jù),還會(huì)造成內(nèi)存泄漏。 - 傳入二級(jí)指針 (
DataPoints** ptr_addr):你傳進(jìn)來的是“外部指針變量的地址”。C++ 內(nèi)部執(zhí)行*ptr_addr = new DataPoints[10];時(shí),是直接順著地址找到了外部的那個(gè)指針,把新分配的內(nèi)存首地址硬塞給它。這樣外部就能成功拿到數(shù)據(jù)了。
2. 必須“C++ 內(nèi)部分配,并提供內(nèi)部接口釋放”
傳入一級(jí)指針通常用于以下 三大黃金場(chǎng)景:
場(chǎng)景一:只讀的數(shù)據(jù)輸入(Input Arrays / Structs)
當(dāng)需要把大量數(shù)據(jù)從 C# 傳給 C++ 讓它進(jìn)行計(jì)算時(shí),絕不會(huì)把幾十萬個(gè)坐標(biāo)點(diǎn)按值(By Value)傳進(jìn)去,而是傳首地址(一級(jí)指針)。
- 工作流:C# 在自己的托管堆(或非托管堆)上準(zhǔn)備好了一排
DataPoints,然后把**首個(gè)元素的地址(一級(jí)指針)**傳給 C++。C++ 內(nèi)部只做遍歷和讀?。?code>Bins[i].DataPoints_x),絕對(duì)不會(huì)對(duì)Bins執(zhí)行new或delete。 - 總結(jié):用于**“只讀”**的大塊數(shù)據(jù)傳輸。
場(chǎng)景二:調(diào)用方預(yù)分配內(nèi)存的高速填充
這是工業(yè)視覺和音視頻處理中最高效、最極客的輸出模式。 如果 C# 端提前知道計(jì)算結(jié)果大概有多大(或者結(jié)果大小是固定的),那么由 C# 提前申請(qǐng)好內(nèi)存,傳一級(jí)指針給 C++ 去“填空”,是比“二級(jí)指針內(nèi)部 new”更快的做法!
舉個(gè)實(shí)際的例子: 假設(shè)你的點(diǎn)云重采樣后固定是 1200 個(gè)點(diǎn),C# 完全可以自己提前 new double[1200]。
C++ 接口設(shè)計(jì):
// 傳入一級(jí)指針 pre_alloc_x 和 pre_alloc_y
void ProcessSingleCloud(double* pre_alloc_x, double* pre_alloc_y, int fixed_len) {
// C++ 內(nèi)部絕對(duì)不寫 new!直接往外部傳進(jìn)來的地址里塞數(shù)據(jù)
for(int i=0; i<fixed_len; i++) {
pre_alloc_x[i] = ...; // 直接填充
pre_alloc_y[i] = ...;
}
}C# 調(diào)用方:
// C# 自己分配好內(nèi)存 double[] out_x = new double[1200]; double[] out_y = new double[1200]; // 傳首地址(一級(jí)指針)給 C++ ProcessSingleCloud(out_x, out_y, 1200); // 調(diào)用結(jié)束,數(shù)據(jù)已經(jīng)在 out_x 里了,完全不需要管釋放問題(C# 的 GC 會(huì)自動(dòng)回收)!
優(yōu)勢(shì):徹底干掉了 FreeDataPoints 這步操作!沒有任何跨語言釋放內(nèi)存的風(fēng)險(xiǎn),性能達(dá)到絕對(duì)的物理極限。
劣勢(shì):如果 C++ 計(jì)算出來的結(jié)果大小是未知的(比如不確定會(huì)返回 500 個(gè)點(diǎn)還是 800 個(gè)點(diǎn)),C# 就無法提前精準(zhǔn)分配內(nèi)存,這時(shí)候就只能退回“二級(jí)指針內(nèi)部 new”的方案了。
3 vector結(jié)合二級(jí)指針
既然 vector 這么好用(比如不確定最終會(huì)匹配出多少個(gè)結(jié)果時(shí),可以隨時(shí) push_back),我們當(dāng)然要在內(nèi)部用它。
正確的架構(gòu)模式是:數(shù)據(jù)在函數(shù)內(nèi)部完全用 std::vector 裝載,但在函數(shù)的最后一行,把 vector 里的數(shù)據(jù)“過繼(Copy/Move)”給一個(gè)通過 new[] 分配的裸數(shù)組。
完美結(jié)合 Vector 的代碼實(shí)現(xiàn):
void F_FindSimilarXldPoint(..., DataPoints** DataPoints_tf, int* DataPoints_tfCount) {
// 1. 內(nèi)部愉快地使用 vector,享受動(dòng)態(tài)擴(kuò)容的便利
std::vector<DataPoints> temp_results;
for (int i = 0; i < batch; ++i) {
// 假設(shè)某些條件不滿足,直接 continue,最終數(shù)量不確定
if (/* 匹配失敗 */ false) continue;
// 構(gòu)造單個(gè)結(jié)果
DataPoints dp;
dp.DataPoints_Lenth = 1200;
// ?? 注意:底層坐標(biāo)數(shù)組必須也是 new 出來的,因?yàn)橐獋鹘o外部
dp.DataPoints_x = new double[1200];
dp.DataPoints_y = new double[1200];
// ... 填充坐標(biāo)數(shù)據(jù) ...
temp_results.push_back(dp); // 裝入 vector
}
// ==========================================================
// 2. ?? 核心交接儀式 (Transfer Ownership)
// ==========================================================
int final_count = temp_results.size();
*DataPoints_tfCount = final_count;
if (final_count > 0) {
// 分配一塊干凈的裸數(shù)組內(nèi)存
DataPoints* out_array = new DataPoints[final_count];
// 淺拷貝:把 vector 里的 DataPoints 結(jié)構(gòu)體(包含里面的 x, y 指針)
// 逐個(gè)復(fù)制給 out_array
for (int i = 0; i < final_count; ++i) {
out_array[i] = temp_results[i];
}
// 把裸數(shù)組的地址交給二級(jí)指針
*DataPoints_tf = out_array;
} else {
*DataPoints_tf = nullptr;
}
} // <--- 函數(shù)結(jié)束,temp_results(vector) 被銷毀。
// 但是不用擔(dān)心!因?yàn)?vector 里裝的是指針副本,
// 真正的數(shù)據(jù) (new double[] 和 new DataPoints[]) 已經(jīng)掛在 out_array 上活下來了!極小開銷: 你可能會(huì)擔(dān)心最后的 for 循環(huán)復(fù)制會(huì)慢。其實(shí)完全不會(huì)!這里發(fā)生的是淺拷貝 (Shallow Copy),僅僅是復(fù)制了 DataPoints 結(jié)構(gòu)體里的 3 個(gè)變量(兩個(gè)指針,一個(gè) int),并沒有復(fù)制那 1200 個(gè) double 數(shù)據(jù)。就算有 1000 個(gè)零件,復(fù)制 1000 個(gè)結(jié)構(gòu)體的時(shí)間連 0.01 毫秒都不到。
到此這篇關(guān)于C++接口內(nèi)部?jī)?nèi)存分配問題設(shè)計(jì)方案的文章就介紹到這了,更多相關(guān)c++接口內(nèi)部?jī)?nèi)存分配內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
- C++動(dòng)態(tài)內(nèi)存分配超詳細(xì)講解
- C++使用new和delete進(jìn)行動(dòng)態(tài)內(nèi)存分配與數(shù)組封裝
- C++繼承和動(dòng)態(tài)內(nèi)存分配
- C++ 操作系統(tǒng)內(nèi)存分配算法的實(shí)現(xiàn)詳解
- c/c++內(nèi)存分配大小實(shí)例講解
- c++ class中成員與分配內(nèi)存的問題詳解
- C語言編程C++動(dòng)態(tài)內(nèi)存分配示例講解
- 帶你了解C++的動(dòng)態(tài)內(nèi)存分配
相關(guān)文章
Qt6.0+vs2019環(huán)境配置的實(shí)現(xiàn)教程
這篇文章主要介紹了Qt6.0+vs2019環(huán)境配置的實(shí)現(xiàn)教程,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Qt HTTP Client的實(shí)現(xiàn)示例
Qt的HTTP客戶端API提供了強(qiáng)大的網(wǎng)絡(luò)通信功能,包括GET和POST請(qǐng)求,支持跨平臺(tái)開發(fā),通過QNetworkAccessManager、QNetworkRequest和QNetworkReply三個(gè)核心類,開發(fā)者可以輕松實(shí)現(xiàn)HTTP通信,2026-02-02
C語言數(shù)據(jù)結(jié)構(gòu)與算法時(shí)間空間復(fù)雜度基礎(chǔ)實(shí)踐
這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)與算法中時(shí)間空間復(fù)雜度的基礎(chǔ)實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
C語言實(shí)現(xiàn)可增容動(dòng)態(tài)通訊錄詳細(xì)過程
這篇文章主要為大家介紹了C語言實(shí)現(xiàn)簡(jiǎn)易通訊錄的完整流程,此通訊錄還可以增容,并且每個(gè)環(huán)節(jié)都有完整代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-05-05
c++實(shí)現(xiàn)對(duì)輸入數(shù)組進(jìn)行快速排序的示例(推薦)
下面小編就為大家?guī)硪黄猚++實(shí)現(xiàn)對(duì)輸入數(shù)組進(jìn)行快速排序的示例(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06

