性能優(yōu)化篇(2):小心“STL 低效率用法”所帶來的性能開銷

性能優(yōu)化篇(2):小心“STL 低效率用法”所帶來的性能開銷

Author:stormQ

Sunday, 17. November 2019 1 03:53PM

  • 目錄

    • 善用 reserve 預(yù)分配內(nèi)存

    • 善用 emplace_back 避免不必要的開銷

    • 善用 std::move 避免不必要的拷貝開銷


善用 reserve 預(yù)分配內(nèi)存

  • reserve 函數(shù)的作用

    將容器的容量(即可以容納的最大元素?cái)?shù)量)調(diào)整為指定的大小n,如果n小于容器的當(dāng)前容量,那么直接忽略此操作。

  • 適用場景

    • 如果std::vector中要添加的元素?cái)?shù)量已知,那么在添加元素前(通常是在循環(huán)中添加元素前)使用reserve函數(shù)預(yù)分配std::vector需要的內(nèi)存。這樣,可以避免由于std::vector內(nèi)部發(fā)生數(shù)據(jù)遷移而帶來的不必要的元素拷貝開銷、創(chuàng)建新內(nèi)存開銷和釋放舊內(nèi)存開銷,從而改善程序性能。
  • 代碼示例

源碼:

// main.cc


#include <vector>

#define N 1980*1024

class TestObject
{
public:
    TestObject(int a, int b, int c) : a_(a), b_(b), c_(c) {}

    int a_;
    int b_;
    int c_;
};

std::vector<TestObject> test_data_1;
std::vector<TestObject> test_data_2;

void poor(std::vector<TestObject> &data)
{
    for (int i = 0; i < N; i++)
    {
        data.push_back(TestObject(i, i+1, i+2));
    }
}

void better(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.push_back(TestObject(i, i+1, i+2));
    }
}

int main()
{
    poor(test_data_1);
    better(test_data_2);
    return 0;
}

編譯:

$ g++ -std=c++11 -g -Og -o main_Og main.cpp

函數(shù)耗時統(tǒng)計(jì):

啟動程序方式 第一次執(zhí)行耗時(us) 第二次執(zhí)行耗時(us) 第三次執(zhí)行耗時(us) 第四次執(zhí)行耗時(us) 第五次執(zhí)行耗時(us)
./main_Og <li>poor:43301</li> <li>better:16609</li> <li>poor:41402</li> <li>better:15822</li> <li>poor:60415</li> <li>better:15650</li> <li>poor:62450</li> <li>better:14970</li> <li>poor:41866</li> <li>better:16222</li>

從統(tǒng)計(jì)結(jié)果中可以看出,示例中better()函數(shù)的執(zhí)行速度比poor()函數(shù)至少快 2倍。

  • 代碼調(diào)試

分析poor()函數(shù)添加前 9 個元素時,std::vector內(nèi)部數(shù)據(jù)遷移情況。具體調(diào)試過程如下:

(gdb) l 25
20  
21  void poor(std::vector<TestObject> &data)
22  {
23      for (int i = 0; i < N; i++)
24      {
25          data.push_back(TestObject(i, i+1, i+2));
26      }
27  }
28  
29  void better(std::vector<TestObject> &data)
(gdb) b 25
Breakpoint 3 at 0x401065: file m.cpp, line 25.
(gdb) c
Continuing.

Breakpoint 3, poor (data=std::vector of length 0, capacity 0) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
(gdb) c
Continuing.

Breakpoint 3, poor (data=std::vector of length 1, capacity 1 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
(gdb) display data.size()
1: data.size() = 1
(gdb) display &data[0]
2: &data[0] = (TestObject *) 0x614c20
(gdb) c
Continuing.

Breakpoint 3, poor (data=std::vector of length 2, capacity 2 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 2
2: &data[0] = (TestObject *) 0x614c40
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 3, capacity 4 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 3
2: &data[0] = (TestObject *) 0x614c60
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 4, capacity 4 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 4
2: &data[0] = (TestObject *) 0x614c60
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 5, capacity 8 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 5
2: &data[0] = (TestObject *) 0x614ca0
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 6, capacity 8 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 6
2: &data[0] = (TestObject *) 0x614ca0
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 7, capacity 8 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 7
2: &data[0] = (TestObject *) 0x614ca0
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 8, capacity 8 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 8
2: &data[0] = (TestObject *) 0x614ca0
(gdb) 
Continuing.

Breakpoint 3, poor (data=std::vector of length 9, capacity 16 = {...}) at m.cpp:25
25          data.push_back(TestObject(i, i+1, i+2));
1: data.size() = 9
2: &data[0] = (TestObject *) 0x614d10

可以看出,poor()函數(shù)在添加第 2、3、5、9 個元素時,第一個元素的地址——&data[0]發(fā)生了變化。即在這添加這些元素時,std::vector內(nèi)部發(fā)生了數(shù)據(jù)遷移。在添加后面的某些元素時,也會導(dǎo)致std::vector內(nèi)部數(shù)據(jù)遷移,這里不再贅述。

分析better()函數(shù)添加前 9 個元素時,std::vector內(nèi)部是否有數(shù)據(jù)遷移情況。具體調(diào)試過程如下:

(gdb) l 34
29  void better(std::vector<TestObject> &data)
30  {
31      data.reserve(N);
32      for (int i = 0; i < N; i++)
33      {
34          data.push_back(TestObject(i, i+1, i+2));
35      }
36  }
37  
38  int main()
(gdb) b 34
Breakpoint 4 at 0x400ffe: file m.cpp, line 34.
(gdb) c
Continuing.

Breakpoint 4, better (data=std::vector of length 0, capacity 2027520) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
(gdb) c
Continuing.

Breakpoint 4, better (data=std::vector of length 1, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
(gdb) display data.size()
3: data.size() = 1
(gdb) display &data[0]
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) c
Continuing.

Breakpoint 4, better (data=std::vector of length 2, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 2
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 3, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 3
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 4, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 4
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 5, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 5
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 6, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 6
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 7, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 7
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 8, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 8
4: &data[0] = (TestObject *) 0x7ffff2ba6010
(gdb) 
Continuing.

Breakpoint 4, better (data=std::vector of length 9, capacity 2027520 = {...}) at m.cpp:34
34          data.push_back(TestObject(i, i+1, i+2));
3: data.size() = 9
4: &data[0] = (TestObject *) 0x7ffff2ba6010

可以看出,better()函數(shù)在添加前 9 個元素時,第一個元素的地址——&data[0]都一直不變。即在這添加這些元素時,std::vector內(nèi)部未發(fā)生數(shù)據(jù)遷移。在添加后面的其他元素時,也不會導(dǎo)致std::vector內(nèi)部數(shù)據(jù)遷移,有興趣的可以使用條件斷點(diǎn)——b 34 if &data[0]!=0x7ffff2ba6010(0x7ffff2ba6010 為此處調(diào)試時 &data[0] 的值)驗(yàn)證這一點(diǎn)。這正是使用 reserve 預(yù)分配內(nèi)存帶來的效果。

上面示例中還有一處可以優(yōu)化的地方,下文會詳細(xì)說明。


善用 emplace_back 避免不必要的開銷

  • emplace_back 函數(shù)的作用

    相比與push_back函數(shù),emplace_back函數(shù)可以直接構(gòu)造元素,從而避免不必要的臨時對象構(gòu)造/析構(gòu)開銷。

  • 適用場景

    • 如果std::vector中要添加的元素需要傳入?yún)?shù)構(gòu)造時,用emplace_back而不是push_back函數(shù)來直接構(gòu)造元素以避免不必要的開銷,從而改善程序性能。
  • 代碼示例

源碼:

// main.cc


#include <vector>

#define N 1980*1024

class TestObject
{
public:
    TestObject(int a, int b, int c) : a_(a), b_(b), c_(c) {}

    int a_;
    int b_;
    int c_;
};

std::vector<TestObject> test_data_1;
std::vector<TestObject> test_data_2;

void poor(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.push_back(TestObject(i, i+1, i+2));
    }
}

void better(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.emplace_back(i, i+1, i+2);
    }
}

int main()
{
    poor(test_data_1);
    better(test_data_2);
    return 0;
}

編譯:

$ g++ -std=c++11 -g -Og -o main_Og main.cpp

函數(shù)耗時統(tǒng)計(jì):

啟動程序方式 第一次執(zhí)行耗時(us) 第二次執(zhí)行耗時(us) 第三次執(zhí)行耗時(us) 第四次執(zhí)行耗時(us) 第五次執(zhí)行耗時(us)
./main_Og <li>poor:27588</li> <li>better:16733</li> <li>poor:26789</li> <li>better:17094</li> <li>poor:30049</li> <li>better:16380</li> <li>poor:28057</li> <li>better:16301</li> <li>poor:26616</li> <li>better:17038</li>

從統(tǒng)計(jì)結(jié)果中可以看出,示例中better()函數(shù)的執(zhí)行速度比poor()函數(shù)至少快 1.5倍。

  • 代碼調(diào)試

為了驗(yàn)證emplace_back函數(shù)的作用,引入三個全局變量g_count_1g_count_2g_count_3用于統(tǒng)計(jì)構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)被調(diào)用的次數(shù)。修改后的源碼為:

// main.cc


#include <vector>

#define N 1980*1024

int g_count_1 = 0;
int g_count_2 = 0;
int g_count_3 = 0;

class TestObject
{
public:
    TestObject(int a, int b, int c) : a_(a), b_(b), c_(c)
    {
        g_count_1++;
    }

    TestObject(const TestObject &other)
    {
        if (this != &other)
        {
            this->a_ = other.a_;
            this->b_ = other.b_;
            this->c_ = other.c_;
        }
        g_count_2++;
    }

    ~TestObject()
    {
        g_count_3++;
    }

    int a_;
    int b_;
    int c_;
};

std::vector<TestObject> test_data_1;
std::vector<TestObject> test_data_2;

void poor(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.push_back(TestObject(i, i+1, i+2));
    }
}

void better(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.emplace_back(i, i+1, i+2);
    }
}

int main()
{
    g_count_1 = 0;
    g_count_2 = 0;
    g_count_3 = 0;
    poor(test_data_1);

    g_count_1 = 0;
    g_count_2 = 0;
    g_count_3 = 0;
    better(test_data_2);
    g_count_1 = 0;

    return 0;
}

具體調(diào)試過程如下:

(gdb) l
61  
62  int main()
63  {
64      g_count_1 = 0;
65      g_count_2 = 0;
66      g_count_3 = 0;
67      poor(test_data_1);
68  
69      g_count_1 = 0;
70      g_count_2 = 0;
71      g_count_3 = 0;
72      better(test_data_2);
73      g_count_1 = 0;
74  
75      return 0;
76  }
(gdb) b 69
Breakpoint 2 at 0x400b3e: file main.cpp, line 69.
(gdb) b 73
Breakpoint 3 at 0x400b66: file main.cpp, line 73.
(gdb) display g_count_1
1: g_count_1 = 0
(gdb) display g_count_2
2: g_count_2 = 0
(gdb) display g_count_3
3: g_count_3 = 0
(gdb) c
Continuing.

Breakpoint 2, main () at main.cpp:69
69      g_count_1 = 0;
1: g_count_1 = 2027520
2: g_count_2 = 2027520
3: g_count_3 = 2027520
(gdb) n
70      g_count_2 = 0;
1: g_count_1 = 0
2: g_count_2 = 2027520
3: g_count_3 = 2027520
(gdb) 
71      g_count_3 = 0;
1: g_count_1 = 0
2: g_count_2 = 0
3: g_count_3 = 2027520
(gdb) 
72      better(test_data_2);
1: g_count_1 = 0
2: g_count_2 = 0
3: g_count_3 = 0
(gdb) n

Breakpoint 3, main () at main.cpp:73
73      g_count_1 = 0;
1: g_count_1 = 2027520
2: g_count_2 = 0
3: g_count_3 = 0
(gdb) n
76  }
1: g_count_1 = 0
2: g_count_2 = 0
3: g_count_3 = 0

可以看出,poor函數(shù)在執(zhí)行過程中調(diào)用容器元素構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)的次數(shù)都是 2027520(2027520 = 1980 x 1024),驗(yàn)證了push_back函數(shù)的三種開銷:臨時對象的構(gòu)造/析構(gòu)開銷和拷貝開銷。better函數(shù)在執(zhí)行過程中調(diào)用容器元素構(gòu)造函數(shù)、析構(gòu)函數(shù)和拷貝構(gòu)造函數(shù)的次數(shù)分別是 2027520、0、0,驗(yàn)證了emplace_back函數(shù)只有必要的構(gòu)造元素開銷,不會引入臨時對象,避免了不必要的臨時對象構(gòu)造/析構(gòu)開銷,從而改善程序性能。


善用 std::move 避免不必要的拷貝開銷

  • std::move 函數(shù)的作用

    移動而非拷貝數(shù)據(jù)以避免不必要的拷貝開銷,從而改善程序性能。

  • 適用場景

    • 構(gòu)造函數(shù)的參數(shù)列表中有類型為std::vector的參數(shù),并且該參數(shù)只在構(gòu)造過程中使用。
  • 代碼示例

源碼:

// main.cpp


#include <list>
#include <vector>

#define N 1980*1024

class TestObject
{
public:
    TestObject(int a, int b, int c) : a_(a), b_(b), c_(c) {}

    int a_;
    int b_;
    int c_;
};

class Element
{
public:
    explicit Element(const std::vector<TestObject> &data)
        : data_(data)
    {
    }

    explicit Element(std::vector<TestObject> &&data)
        : data_(std::move(data))
    {
    }

private:
    std::vector<TestObject> data_;
};

std::list<Element> test_data_1;
std::list<Element> test_data_2;
Element *g_ele = nullptr;

void poor(std::list<Element> &data)
{
    std::vector<TestObject> vec;
    vec.reserve(N);
    for (int i = 0; i < N; i++)
    {
        vec.push_back(TestObject(i, i+1, i+2));
    }

    data.emplace_back(vec);
    g_ele = &data.front();
}

void better(std::list<Element> &data)
{
    std::vector<TestObject> vec;
    vec.reserve(N);
    for (int i = 0; i < N; i++)
    {
        vec.emplace_back(i, i+1, i+2);
    }

    data.emplace_back(std::move(vec));
    g_ele = &data.front();
}

int main()
{
    poor(test_data_1);
    better(test_data_2);
    return 0;
}

編譯:

$ g++ -std=c++11 -g -Og -o main_Og main.cpp

函數(shù)耗時統(tǒng)計(jì):

啟動程序方式 第一次執(zhí)行耗時(us) 第二次執(zhí)行耗時(us) 第三次執(zhí)行耗時(us) 第四次執(zhí)行耗時(us) 第五次執(zhí)行耗時(us)
./main_Og <li>poor:37272</li> <li>better:13913</li> <li>poor:39693</li> <li>better:14832</li> <li>poor:37982</li> <li>better:14439</li> <li>poor:38427</li> <li>better:14590</li> <li>poor:38391</li> <li>better:14502</li>

從統(tǒng)計(jì)結(jié)果中可以看出,示例中better()函數(shù)的執(zhí)行速度比poor()函數(shù)至少快 2倍。

  • 代碼調(diào)試

驗(yàn)證data.emplace_back(vec); 會引發(fā)拷貝開銷,具體調(diào)試過程如下:

(gdb) l 50
35  
36  std::list<Element> test_data_1;
37  std::list<Element> test_data_2;
38  Element *g_ele = nullptr;
39  
40  void poor(std::list<Element> &data)
41  {
42      std::vector<TestObject> vec;
43      vec.reserve(N);
44      for (int i = 0; i < N; i++)
45      {
46          vec.push_back(TestObject(i, i+1, i+2));
47      }
48  
49      data.emplace_back(vec);
50      g_ele = &data.front();
51  }
52  
53  void better(std::list<Element> &data)
54  {
55      std::vector<TestObject> vec;
56      vec.reserve(N);
57      for (int i = 0; i < N; i++)
58      {
59          vec.emplace_back(i, i+1, i+2);
60      }
61  
62      data.emplace_back(std::move(vec));
63      g_ele = &data.front();
64  }
(gdb) b 50
Breakpoint 3 at 0x40131c: file p.cpp, line 50.
(gdb) c
Continuing.

Breakpoint 3, poor (data=std::__cxx11::list = {...}) at p.cpp:50
50      g_ele = &data.front();
(gdb) display vec._M_impl
1: vec._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x7ffff5a2b010, 
  _M_finish = 0x7ffff715f010, _M_end_of_storage = 0x7ffff715f010}
(gdb) n
42      std::vector<TestObject> vec;
1: vec._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x7ffff5a2b010, 
  _M_finish = 0x7ffff715f010, _M_end_of_storage = 0x7ffff715f010}
(gdb) display g_ele->data_._M_impl
2: g_ele->data_._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x7ffff42f6010, 
  _M_finish = 0x7ffff5a2a010, _M_end_of_storage = 0x7ffff5a2a010}

可以看出,poor函數(shù)中的臨時對象vec的內(nèi)存地址范圍為0x7ffff5a2b010~0x7ffff715f010(vec._M_impl._M_start 的值為 0x7ffff5a2b010,vec._M_impl.M_end_of_storage 的值為 0x7ffff715f010)。全局變量test_data_1首元素的數(shù)據(jù)成員data_的內(nèi)存地址范圍為0x7ffff42f6010~0x7ffff5a2a010(g_ele->data._M_impl.M_start 的值為 0x7ffff42f6010,g_ele->data._M_impl._M_end_of_storage 的值為 0x7ffff5a2a010)。兩者的內(nèi)存地址范圍不相同。因此,全局變量test_data_1首元素的數(shù)據(jù)成員data_是從臨時對象vec拷貝而來的,從而驗(yàn)證了data.emplace_back(vec); 會引發(fā)拷貝開銷。

驗(yàn)證data.emplace_back(std::move(vec)); 不會引發(fā)拷貝開銷,具體調(diào)試過程如下:

(gdb) b 59
Breakpoint 4 at 0x4010ba: file p.cpp, line 59.
(gdb) b 63
Breakpoint 5 at 0x401147: file p.cpp, line 63.
(gdb) c
Continuing.
poor_function elapsed_time: 59091892 us

Breakpoint 4, better (data=empty std::__cxx11::list) at p.cpp:59
59          vec.emplace_back(i, i+1, i+2);
2: g_ele->data_._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x7ffff42f6010, 
  _M_finish = 0x7ffff5a2a010, _M_end_of_storage = 0x7ffff5a2a010}
(gdb) display vec._M_impl
3: vec._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x616060, _M_finish = 0x616060, 
  _M_end_of_storage = 0x1d4a060}
(gdb) d 4
(gdb) c
Continuing.

Breakpoint 5, better (data=std::__cxx11::list = {...}) at p.cpp:63
63      g_ele = &data.front();
2: g_ele->data_._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x7ffff42f6010, 
  _M_finish = 0x7ffff5a2a010, _M_end_of_storage = 0x7ffff5a2a010}
3: vec._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0, 
  _M_end_of_storage = 0x0}
(gdb) n
55      std::vector<TestObject> vec;
2: g_ele->data_._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x616060, _M_finish = 0x1d4a060, 
  _M_end_of_storage = 0x1d4a060}
3: vec._M_impl = {<std::allocator<TestObject>> = {<__gnu_cxx::new_allocator<TestObject>> = {<No data fields>}, <No data fields>}, _M_start = 0x0, _M_finish = 0x0, 
  _M_end_of_storage = 0x0}

可以看出,better函數(shù)中的臨時對象vec的內(nèi)存地址范圍為0x616060~0x1d4a060(vec._M_impl._M_start 的值為 0x616060,vec._M_impl.M_end_of_storage 的值為 0x1d4a060)。全局變量test_data_2首元素的數(shù)據(jù)成員data_的內(nèi)存地址范圍為0x616060~0x1d4a060(g_ele->data._M_impl.M_start 的值為 0x616060,g_ele->data._M_impl._M_end_of_storage 的值為 0x1d4a060)。兩者的內(nèi)存地址范圍相同。因此,全局變量test_data_2首元素的數(shù)據(jù)成員data_是直接將臨時對象vec移動而來的,從而驗(yàn)證了data.emplace_back(std::move(vec)); 不會引發(fā)拷貝開銷。另外,需要注意的是,臨時對象vec在被移動之后將不再有效。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容