成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

當我把 push_back 換成 emplace_back 后,代碼性能竟然......

開發
如果你經常和 C++ 的 vector、list 這些容器打交道,那么今天這篇文章絕對值得你花幾分鐘時間——因為我要告訴你一個小技巧,它能讓你的代碼不僅寫起來更爽,還能跑得更快!

大家好,我是小康。

你有沒有過這樣的經歷?寫了一大堆代碼,明明邏輯沒問題,程序跑得卻像蝸牛一樣慢。特別是當你在處理大量數據,往容器里瘋狂塞東西的時候。

如果你經常和 C++ 的 vector、list 這些容器打交道,那么今天這篇文章絕對值得你花幾分鐘時間——因為我要告訴你一個小技巧,它能讓你的代碼不僅寫起來更爽,還能跑得更快!

它就是容器的"隱藏技能":emplace_back()。

"又是一個新函數?我的push_back不香了嗎?"

別急,咱們先來看個例子,感受一下這兩者的區別:

#include <vector>
#include <string>

class Person {
public:
    Person(std::string name, int age) : m_name(name), m_age(age) {
        printf("構造了一個人:%s, %d歲\n", name.c_str(), age);
    }
    
    // 拷貝構造函數
    Person(const Person& other) : m_name(other.m_name), m_age(other.m_age) {
        printf("拷貝構造了一個人:%s\n", m_name.c_str());
    }
    
private:
    std::string m_name;
    int m_age;
};

int main() {
    std::vector<Person> people;
    
    printf("=== 使用push_back ===\n");
    people.push_back(Person("張三", 25));
    
    people.clear();  // 清空容器
    
    printf("\n=== 使用emplace_back ===\n");
    people.emplace_back("李四", 30);
}

運行這段代碼,你會看到這樣的輸出:

=== 使用push_back ===
構造了一個人:張三, 25歲
拷貝構造了一個人:張三

=== 使用emplace_back ===
構造了一個人:李四, 30歲

看出區別了嗎?

  • 使用push_back時,我們先創建了一個臨時的 Person 對象,然后 vector 把它拷貝到容器里
  • 而使用emplace_back時,我們直接傳入構造 Person 所需的參數,vector 直接在容器內部構造對象

結果就是:push_back額外調用了一次拷貝構造函數,而emplace_back沒有!

"所以emplace_back就是直接傳構造函數參數?"

沒錯!這就是它最大的特點。

  • push_back(x) 需要你先構造好一個對象x,然后把它放進容器
  • emplace_back(args...) 則是直接把構造函數的參數 args 傳進去,在容器內部構造對象

這個差別看似小,實際上在性能上卻能帶來很大的提升,尤其是當:

對象構造成本高(比如有很多成員變量)

拷貝成本高(比如內部有動態分配的內存)

你需要插入大量對象時

"來點實際的例子!"

好的,我們來看一個真正能展示差異的例子。我們創建一個拷貝成本真正很高的類,這樣才能看出 emplace_back 的威力:

#include <vector>
#include <string>
#include <chrono>
#include <iostream>
#include <memory>

// 設計一個拷貝成本很高的類
class ExpensiveToCopy {
public:
    // 構造函數 - 創建一個大數組
    ExpensiveToCopy(const std::string& name, int dataSize) 
        : m_name(name), m_dataSize(dataSize) {
        // 分配大量內存,模擬昂貴的資源
        m_data = new int[dataSize];
        for (int i = 0; i < dataSize; i++) {
            m_data[i] = i;  // 初始化數據
        }
    }
    
    // 拷貝構造函數 - 非常昂貴,需要復制整個大數組
    ExpensiveToCopy(const ExpensiveToCopy& other)
        : m_name(other.m_name), m_dataSize(other.m_dataSize) {
        // 深拷貝,非常耗時
        m_data = new int[m_dataSize];
        for (int i = 0; i < m_dataSize; i++) {
            m_data[i] = other.m_data[i];
        }
        
        // 輸出提示以便觀察拷貝構造函數的調用情況
        std::cout << "拷貝構造: " << m_name << std::endl;
    }
    
    // 析構函數
    ~ExpensiveToCopy() {
        delete[] m_data;
    }
    
    // 禁用賦值運算符以簡化例子
    ExpensiveToCopy& operator=(const ExpensiveToCopy&) = delete;
    
private:
    std::string m_name;
    int* m_data;
    int m_dataSize;
};

// 計時輔助函數
template<typename Func>
long long timeIt(Func func) {
    auto start = std::chrono::high_resolution_clock::now();
    func();
    auto end = std::chrono::high_resolution_clock::now();
    return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}

int main() {
    const int COUNT = 100;  // 對象數量,減少一點以便看到輸出
    const int DATA_SIZE = 100000;  // 每個對象中數組的大小
    
    std::cout << "=== 測試push_back ===\n";
    long long pushTime = timeIt([&]() {
        std::vector<ExpensiveToCopy> objects;
        objects.reserve(COUNT);  // 預分配空間避免重新分配的影響
        
        for (int i = 0; i < COUNT; i++) {
            // 創建臨時對象然后放入vector
            // 這個過程會調用拷貝構造函數
            objects.push_back(ExpensiveToCopy("對象" + std::to_string(i), DATA_SIZE));
        }
    });
    
    std::cout << "\n=== 測試emplace_back ===\n";
    long long emplaceTime = timeIt([&]() {
        std::vector<ExpensiveToCopy> objects;
        objects.reserve(COUNT);  // 預分配空間避免重新分配的影響
        
        for (int i = 0; i < COUNT; i++) {
            // 直接傳遞構造函數參數
            // 直接在vector內部構造對象,避免了拷貝
            objects.emplace_back("對象" + std::to_string(i), DATA_SIZE);
        }
    });
    
    std::cout << "\npush_back耗時: " << pushTime << " 微秒" << std::endl;
    std::cout << "emplace_back耗時: " << emplaceTime << " 微秒" << std::endl;
    double percentDiff = (static_cast<double>(pushTime) / emplaceTime - 1.0) * 100.0;
    std::cout << "性能差異: push_back比emplace_back慢了 " << percentDiff << "%" << std::endl;
}

在我的電腦上,大約是這樣的結果:

=== 測試emplace_back ===

push_back耗時: 66979 微秒
emplace_back耗時: 35858 微秒
性能差異: push_back比emplace_back慢了 86.7896%

這意味著push_back比emplace_back慢了約86%!這可不是小數目,尤其是在處理大對象時。

"看起來emplace_back完勝啊!為什么還有人用push_back?"

好問題!emplace_back雖然在大多數情況下更快,但并不是所有場景都適合用它:

  • 當你已經有一個現成的對象時,push_back可能更直觀
  • 對于基本類型(int, double等),兩者性能差異可以忽略不計
  • 對于某些編譯器優化情況,比如移動語義,差距可能不明顯

來看一個例子,說明什么時候兩者其實差不多:

std::vector<int> numbers;

// 對于基本類型,這兩個是等價的
numbers.push_back(42);
numbers.emplace_back(42);

// 如果已經有一個現成的對象
std::string name = "張三";
std::vector<std::string> names;

// 這種情況下,如果 string 支持移動構造,兩者性能接近
names.push_back(name);               // 拷貝name
names.push_back(std::move(name));    // 移動name(推薦)
names.emplace_back(name);            // 拷貝name
names.emplace_back(std::move(name)); // 移動name(推薦)

"完美轉發是什么鬼?聽說emplace_back跟這個有關?"

沒錯!emplace_back的強大之處,部分來自于它使用了"完美轉發"(Perfect Forwarding)技術。

簡單來說,完美轉發就是把函數參數"原汁原味"地傳遞給另一個函數,保持它的所有特性(比如是左值還是右值,是const還是non-const)。

在C++中,這通常通過模板和std::forward實現:

template <typename... Args>
void emplace_back(Args&&... args) {
    // 在容器內部直接構造對象
    // 完美轉發所有參數
    new (memory_location) T(std::forward<Args>(args)...);
}

這樣的設計讓emplace_back能夠接受任意數量、任意類型的參數,并且完美地轉發給對象的構造函數。這就是為什么你可以直接這樣寫:

people.emplace_back("張三", 25);  // 直接傳構造函數參數

而不需要先構造一個對象。

"還有其他 emplace 系列函數嗎?"

是的!STL容器中有一系列的emplace函數:

  • vector、deque、list: emplace_back()
  • list, forward_list: emplace_front()
  • 所有容器: emplace()(在指定位置構造元素)
  • 關聯容器(map, set等): emplace_hint()(帶提示的插入)

它們的共同點是:直接在容器內部構造元素,而不是先構造再拷貝/移動。

實戰建議:什么時候用 emplace_back?

  • 復雜對象插入:當你要插入的對象構造成本高、拷貝代價大時
  • 大量數據操作:需要插入大量元素時,性能差異會更明顯
  • 直接傳參更方便時:比如插入 pair 到 map
// 不那么優雅
std::map<int, std::string> m;
m.insert(std::make_pair(1, "one"));

// 更優雅,也更高效
m.emplace(1, "one");
  • 臨時對象場景:當你需要創建臨時對象并插入容器時

總結

emplace_back本質上是通過減少不必要的對象創建和拷貝來提升性能。它利用了 C++ 的完美轉發功能,讓你可以直接傳遞構造函數參數,而不需要先創建臨時對象。

在處理復雜對象或大量數據時,這種優化尤為明顯。當然,對于簡單類型或已有對象,兩者差異不大。

所以下次當你在寫:

myVector.push_back(MyClass(arg1, arg2));

的時候,不妨試試:

myVector.emplace_back(arg1, arg2);

代碼更簡潔,運行更高效,何樂而不為呢?

記住,在編程世界里,這種看似微小的優化,累積起來就是質的飛躍!

責任編輯:趙寧寧 來源: 跟著小康學編程
相關推薦

2022-03-11 07:59:09

容器代碼元素

2025-02-07 09:58:43

C++11Lvalue對象

2009-10-22 10:18:45

Back-to-BacCCIE

2010-08-10 15:42:31

DB2 back 存檔

2009-12-01 11:31:59

不間斷電源

2009-12-01 11:27:18

不間斷電源

2021-01-04 20:40:21

微軟Windows 10Windows

2024-07-26 08:35:29

2024-01-22 08:21:46

APPHomemCount

2013-12-19 14:32:31

Android ApiAndroid開發Android SDK

2022-01-09 23:38:42

通信協議網絡

2021-02-06 13:11:28

SQL系統數據庫

2024-07-26 09:33:22

2013-06-14 14:41:41

Android開發pushSMS push

2020-10-16 09:09:56

代碼業務模型

2022-09-07 09:22:36

SpringBootWeb

2022-09-13 12:04:53

知乎信息App

2011-11-09 14:54:26

2025-06-05 04:22:00

SQL性能索引

2013-07-31 13:03:51

Windows PhoWindows Pho
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲欧美激情网 | 亚洲字幕在线观看 | 91国产在线视频在线 | 精品在线一区 | 亚洲一区二区三区免费视频 | 亚洲国产精品一区二区三区 | 久草免费电影 | 91综合网| 欧美一级免费 | 91香蕉视频在线观看 | 久久成| 日本小电影网站 | 天堂av中文| 欧美a级成人淫片免费看 | 亚洲欧美在线观看 | 日本天堂视频 | 国产精品1区2区3区 国产在线观看一区 | 国产一区二区视频在线 | 欧美日韩综合一区 | 亚洲97 | www.888www看片| 91av视频在线观看 | 91精品国产91久久久久福利 | 日韩欧美网 | 一区二区三区视频免费看 | 国产精品久久视频 | 九九热这里 | 热久久性 | 黄色大片在线播放 | 在线a视频网站 | 色天堂视频 | 女女爱爱视频 | 国产欧美日韩精品一区 | 亚洲欧美激情国产综合久久久 | 在线观看国产精品一区二区 | 成人av播放 | 男女国产网站 | av一级毛片 | 国内精品一区二区 | 午夜在线视频 | 欧美aaaaaaaa|