emplace_back与push_back方法的区别
【1】emplace_back源码
1 template <class... _Valty> 2 decltype(auto) emplace_back(_Valty&&... _Val) { 3 // insert by perfectly forwarding into element at end, provide strong guarantee 4 auto& _My_data = _Mypair._Myval2; 5 pointer& _Mylast = _My_data._Mylast; 6 if (_Mylast != _My_data._Myend) { 7 return _Emplace_back_with_unused_capacity(_STD forward<_Valty>(_Val)...); 8 } 9 10 _Ty& _Result = *_Emplace_reallocate(_Mylast, _STD forward<_Valty>(_Val)...); 11 #if _HAS_CXX17 12 return _Result; 13 #else // ^^^ _HAS_CXX17 ^^^ // vvv !_HAS_CXX17 vvv 14 (void) _Result; 15 #endif // _HAS_CXX17 16 }
【2】push_back源码
1 void push_back(const _Ty& _Val) { // insert element at end, provide strong guarantee 2 emplace_back(_Val); 3 } 4 5 void push_back(_Ty&& _Val) { // insert by moving into element at end, provide strong guarantee 6 emplace_back(_STD move(_Val)); 7 }
【3】两者的区别
通过几个示例便于理解分析两者应用区别,具体代码如下:
1 #include <vector> 2 #include <ctime> 3 #include <cassert> 4 #include <iostream> 5 using namespace std; 6 7 #define EXAMPLE_1 8 9 #ifdef EXAMPLE_13 10 #define NEED_LOG (0) 11 #else 12 #define NEED_LOG (1) 13 #endif 14 15 #define MAXSIZE (100000) 16 #define LOOP_COUNT (5) 17 18 class String 19 { 20 public: 21 String(); 22 String(int n, char c); 23 24 #ifdef EXAMPLE_1 25 explicit String(const char* source); 26 #else 27 String(const char* source); 28 #endif 29 30 String(const String& s); 31 String(String&& s) noexcept; 32 String& operator=(char* s); 33 String& operator=(const String& s); 34 String& operator=(String&& s); 35 ~String(); 36 37 char& operator[](int i); 38 const char& operator[](int i) const; 39 String& operator+=(const String& s); 40 String& operator+=(const char* s); 41 42 size_t getSize() const; 43 void setSize(size_t nSize); 44 char* getData() const; 45 void setData(char* pData); 46 47 private: 48 void init(); 49 50 private: 51 size_t m_size; 52 char* m_data; 53 }; 54 55 void String::init() 56 { 57 m_data = new char[1]; 58 assert(m_data != NULL); 59 *m_data = '\0'; 60 m_size = 0; 61 } 62 63 String::String() 64 { 65 init(); 66 } 67 68 String::String(int n, char c) 69 { 70 #if NEED_LOG 71 cout << "constructor: String(int n, char c) | this: " << this << endl; 72 #endif 73 m_data = new char[n + 1]; 74 assert(m_data != NULL); 75 m_size = n; 76 char* temp = m_data; 77 while (n--) 78 { 79 *temp++ = c; 80 } 81 *temp = '\0'; 82 } 83 84 String::String(const char* source) 85 { 86 #if NEED_LOG 87 cout << "constructor: String(const char* source) | this: " << this << endl; 88 #endif 89 if (NULL == source) 90 { 91 init(); 92 } 93 else 94 { 95 m_size = strlen(source); 96 m_data = new char[m_size + 1]; 97 assert(m_data != NULL); 98 strcpy_s(m_data, (m_size + 1), source); 99 } 100 } 101 String::String(const String& s) 102 { 103 #if NEED_LOG 104 cout << "copy constructor: String(const String& s) | src this: " << &s << " dst this: " << this << endl; 105 #endif 106 m_data = new char[s.m_size + 1]; 107 assert(m_data != NULL); 108 strcpy_s(m_data, (s.m_size + 1), s.m_data); 109 m_size = s.m_size; 110 } 111 112 String::String(String&& s) noexcept 113 { 114 #if NEED_LOG 115 cout << "move constructor | src this: " << &s << " dst this: " << this << endl; 116 #endif 117 m_data = s.getData(); 118 m_size = s.getSize(); 119 s.setData(NULL); 120 s.setSize(0); 121 } 122 123 String& String::operator=(char* s) 124 { 125 #if NEED_LOG 126 cout << "call operator=(char* s)" << endl; 127 #endif 128 if (m_data != NULL) 129 { 130 delete[]m_data; 131 } 132 m_size = strlen(s); 133 m_data = new char[m_size + 1]; 134 assert(m_data != NULL); 135 strcpy_s(m_data, (m_size + 1), s); 136 return (*this); 137 } 138 139 String& String::operator=(const String& s) 140 { 141 #if NEED_LOG 142 cout << "call operator=(const String& s)" << endl; 143 #endif 144 145 if (this == &s) 146 { 147 return *this; 148 } 149 if (m_data != NULL) 150 { 151 delete[]m_data; 152 } 153 m_size = strlen(s.m_data); 154 m_data = new char[m_size + 1]; 155 assert(m_data != NULL); 156 strcpy_s(m_data, (m_size + 1), s.m_data); 157 return (*this); 158 } 159 160 String& String::operator=(String&& s) 161 { 162 #if NEED_LOG 163 cout << "call move operator=" << endl; 164 #endif 165 166 if (this == &s) 167 { 168 return (*this); 169 } 170 if (m_data != NULL) 171 { 172 delete[]m_data; 173 } 174 m_data = s.getData(); 175 m_size = s.getSize(); 176 s.setData(NULL); 177 s.setSize(0); 178 return (*this); 179 } 180 181 String::~String() 182 { 183 #if NEED_LOG 184 cout << "call destructor | this: " << this << endl; 185 #endif 186 187 if (m_data != NULL) 188 { 189 delete[]m_data; 190 m_data = NULL; 191 m_size = 0; 192 } 193 } 194 195 char& String::operator[](int i) 196 { 197 return m_data[i]; 198 } 199 200 const char& String::operator[](int i) const 201 { 202 return m_data[i]; 203 } 204 205 String& String::operator+=(const String& s) 206 { 207 size_t len = m_size + s.m_size + 1; 208 char* pTemp = m_data; 209 m_data = new char[len]; 210 assert(m_data != NULL); 211 strcpy_s(m_data, (m_size + 1), pTemp); 212 strcat_s(m_data, len, s.m_data); 213 m_size = len - 1; 214 delete[]pTemp; 215 return (*this); 216 } 217 218 String& String::operator+=(const char* s) 219 { 220 if (NULL == s) 221 { 222 return (*this); 223 } 224 size_t len = m_size + strlen(s) + 1; 225 char* pTemp = m_data; 226 m_data = new char[len]; 227 assert(m_data != NULL); 228 strcpy_s(m_data, (m_size + 1), pTemp); 229 strcat_s(m_data, len, s); 230 m_size = len - 1; 231 delete[]pTemp; 232 return (*this); 233 } 234 235 size_t String::getSize() const 236 { 237 return m_size; 238 } 239 240 void String::setSize(size_t nSize) 241 { 242 this->m_size = nSize; 243 } 244 245 char* String::getData() const 246 { 247 return m_data; 248 } 249 250 void String::setData(char* pData) 251 { 252 if (NULL == pData) 253 { 254 init(); 255 } 256 else 257 { 258 if (m_data != nullptr) 259 { 260 delete[]m_data; 261 m_data = nullptr; 262 } 263 264 m_size = strlen(pData); 265 m_data = new char[m_size + 1]; 266 assert(m_data != NULL); 267 strcpy_s(m_data, (m_size + 1), pData); 268 } 269 } 270 271 // 计时器 272 // 调用clock()函数实现,返回毫秒(ms)数 273 class TestProgramRunTimer 274 { 275 enum { kClockPerSecond = CLOCKS_PER_SEC }; // 每秒时钟的跳数 276 277 public: 278 TestProgramRunTimer() 279 : m_cost_time(0) 280 , m_start_time(0) 281 , m_end_time(0) 282 { 283 start(); 284 } 285 286 ~TestProgramRunTimer() 287 {} 288 289 void reset() 290 { 291 m_cost_time = 0; 292 m_start_time = 0; 293 m_end_time = 0; 294 } 295 296 void start() 297 { 298 m_start_time = clock(); 299 } 300 301 void stop() 302 { 303 m_end_time = clock(); 304 m_cost_time = (m_end_time - m_start_time) / (kClockPerSecond / 1000.0); 305 } 306 307 double cost() 308 { 309 return m_cost_time; 310 } 311 312 protected: 313 double m_cost_time; 314 clock_t m_start_time; 315 clock_t m_end_time; 316 }; 317 318 TestProgramRunTimer tt; 319 320 double FV_ByPushBack(vector<String>& testVector) 321 { 322 tt.reset(); 323 tt.start(); 324 for (int i = 0; i < MAXSIZE; ++i) 325 { 326 #ifndef EXAMPLE_1 327 testVector.push_back("push_back"); 328 #endif 329 } 330 tt.stop(); 331 std::cout << "FV_ByPushBack :: " << tt.cost() << endl; 332 return tt.cost(); 333 } 334 335 double FV_ByEmplaceBack(vector<String>& testVector) 336 { 337 tt.reset(); 338 tt.start(); 339 for (int i = 0; i < MAXSIZE; ++i) 340 { 341 testVector.emplace_back("emplace_back"); 342 } 343 tt.stop(); 344 std::cout << "FV_ByEmplaceBack :: " << tt.cost() << endl; 345 return tt.cost(); 346 } 347 348 int main() 349 { 350 // 显式构造对象:push_back 不支持; emplace_back 支持 351 #ifdef EXAMPLE_1 352 { 353 //String str1 = "kaizen"; // compile error:试图通过隐式转换构造对象,以失败告终:因为参数为const char* source的重载构造函数前加关键字explicit显式限定约束 354 String str2("kaizen"); // ok:显式构造对象成功 355 356 vector<String> tempVec; 357 //tempVec.push_back("kaizen"); // compile error:push_back不支持显式构造对象 358 tempVec.emplace_back("kaizen"); // ok:emplace_bakc支持显式构造对象 359 } 360 /* 361 constructor: String(const char* source) | this: 0000000A3C1DF548 362 constructor: String(const char* source) | this: 000001325C6D3F80 363 call destructor | this: 000001325C6D3F80 364 call destructor | this: 0000000A3C1DF548 365 */ 366 #endif 367 368 // 隐式构造对象:emplace_back 支持;push_back 支持 369 #ifdef EXAMPLE_2 370 { 371 String str1 = "kaizen"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 372 String str2("kaizen"); // ok:构造对象成功 373 374 vector<String> tempVec; 375 tempVec.push_back("kaizen"); // ok:push_back支持隐式转换构造对象 376 tempVec.emplace_back("kaizen"); // ok:emplace_bakc支持隐式转换构造对象 377 } 378 /* 379 constructor: String(const char* source) | this: 0000002AF413F5A8 380 constructor: String(const char* source) | this: 0000002AF413F5D8 381 constructor: String(const char* source) | this: 0000002AF413F708 382 move constructor | src this: 0000002AF413F708 dst this: 0000020194793670 383 call destructor | this: 0000002AF413F708 384 constructor: String(const char* source) | this: 0000020194791610 385 move constructor | src this: 0000020194793670 dst this: 0000020194791600 386 call destructor | this: 0000020194793670 387 call destructor | this: 0000020194791600 388 call destructor | this: 0000020194791610 389 call destructor | this: 0000002AF413F5D8 390 call destructor | this: 0000002AF413F5A8 391 */ 392 #endif 393 394 // 隐式构造对象:emplace_back 支持;push_back 支持 预留足够空间,避免移动元素影响 395 #ifdef EXAMPLE_3 396 { 397 String str1 = "kaizen"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 398 String str2("kaizen"); // ok:构造对象成功 399 400 vector<String> tempVec; 401 tempVec.reserve(10); // 与 example_2的区别 402 tempVec.push_back("kaizen"); // ok:push_back支持隐式转换构造对象 403 tempVec.emplace_back("kaizen"); // ok:emplace_bakc支持隐式转换构造对象 404 } 405 /* 406 constructor: String(const char* source) | this: 0000002CAAB2FC58 407 constructor: String(const char* source) | this: 0000002CAAB2FC88 408 constructor: String(const char* source) | this: 0000002CAAB2FDB8 409 move constructor | src this: 0000002CAAB2FDB8 dst this: 0000015D821D5910 410 call destructor | this: 0000002CAAB2FDB8 411 constructor: String(const char* source) | this: 0000015D821D5920 412 call destructor | this: 0000015D821D5910 413 call destructor | this: 0000015D821D5920 414 call destructor | this: 0000002CAAB2FC88 415 call destructor | this: 0000002CAAB2FC58 416 */ 417 #endif 418 419 // 左值:emplace_back 支持;push_back 支持 420 #ifdef EXAMPLE_4 421 { 422 String str1 = "kaizen1"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 423 String str2("kaizen2"); // ok:构造对象成功 424 425 vector<String> tempVec; 426 tempVec.push_back(str1); // ok 427 tempVec.emplace_back(str2); // ok 428 } 429 /* 430 constructor: String(const char* source) | this: 00000081A014F4A8 431 constructor: String(const char* source) | this: 00000081A014F4D8 432 copy constructor: String(const String& s) | src this: 00000081A014F4A8 dst this: 000002409BE33560 433 copy constructor: String(const String& s) | src this: 00000081A014F4D8 dst this: 000002409BE31F50 434 move constructor | src this: 000002409BE33560 dst this: 000002409BE31F40 435 call destructor | this: 000002409BE33560 436 call destructor | this: 000002409BE31F40 437 call destructor | this: 000002409BE31F50 438 call destructor | this: 00000081A014F4D8 439 call destructor | this: 00000081A014F4A8 440 */ 441 #endif 442 443 // 左值:emplace_back 支持;push_back 支持 预留足够空间,避免移动元素影响 444 #ifdef EXAMPLE_5 445 { 446 String str1 = "kaizen1"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 447 String str2("kaizen2"); // ok:构造对象成功 448 449 vector<String> tempVec; 450 tempVec.reserve(10); // 与 example_4的区别 451 tempVec.push_back(str1); // ok 452 tempVec.emplace_back(str2); // ok 453 } 454 /* 455 constructor: String(const char* source) | this: 000000534CF9F548 456 constructor: String(const char* source) | this: 000000534CF9F578 457 copy constructor: String(const String& s) | src this: 000000534CF9F548 dst this: 000001FF62045660 458 copy constructor: String(const String& s) | src this: 000000534CF9F578 dst this: 000001FF62045670 459 call destructor | this: 000001FF62045660 460 call destructor | this: 000001FF62045670 461 call destructor | this: 000000534CF9F578 462 call destructor | this: 000000534CF9F548 463 */ 464 #endif 465 466 // 右值:emplace_back 支持;push_back 支持 467 #ifdef EXAMPLE_6 468 { 469 vector<String> tempVec; 470 tempVec.push_back(String{ "kaizen" }); // ok 471 tempVec.emplace_back(String{ "kaizen" }); // ok 472 } 473 /* 474 constructor: String(const char* source) | this: 000000DF9DB9F9E8 475 move constructor | src this: 000000DF9DB9F9E8 dst this: 00000258DD4837B0 476 call destructor | this: 000000DF9DB9F9E8 477 constructor: String(const char* source) | this: 000000DF9DB9FA18 478 move constructor | src this: 000000DF9DB9FA18 dst this: 00000258DD481310 479 move constructor | src this: 00000258DD4837B0 dst this: 00000258DD481300 480 call destructor | this: 00000258DD4837B0 481 call destructor | this: 000000DF9DB9FA18 482 call destructor | this: 00000258DD481300 483 call destructor | this: 00000258DD481310 484 */ 485 #endif 486 487 // 右值:emplace_back 支持; push_back 支持; 预留足够空间,避免移动元素影响 488 #ifdef EXAMPLE_7 489 { 490 vector<String> tempVec; 491 tempVec.reserve(10); // 与 example_6的区别 492 tempVec.push_back(String{ "kaizen" }); // ok 493 tempVec.emplace_back(String{ "kaizen" }); // ok 494 } 495 /* 496 constructor: String(const char* source) | this: 0000005FDD56F8F8 497 move constructor | src this: 0000005FDD56F8F8 dst this: 0000018316495660 498 call destructor | this: 0000005FDD56F8F8 499 constructor: String(const char* source) | this: 0000005FDD56F928 500 move constructor | src this: 0000005FDD56F928 dst this: 0000018316495670 501 call destructor | this: 0000005FDD56F928 502 call destructor | this: 0000018316495660 503 call destructor | this: 0000018316495670 504 */ 505 #endif 506 507 // 右值(std::move):emplace_back 支持; push_back 支持; 508 #ifdef EXAMPLE_8 509 { 510 String str1 = "kaizen1"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 511 String str2("kaizen2"); // ok:构造对象成功 512 513 vector<String> tempVec; 514 tempVec.push_back(std::move(str1)); // ok 515 tempVec.emplace_back(std::move(str2)); // ok 516 } 517 /* 518 constructor: String(const char* source) | this: 000000A7B96FFA78 519 constructor: String(const char* source) | this: 000000A7B96FFAA8 520 move constructor | src this: 000000A7B96FFA78 dst this: 000001BBEA4E3990 521 move constructor | src this: 000000A7B96FFAA8 dst this: 000001BBEA4E10D0 522 move constructor | src this: 000001BBEA4E3990 dst this: 000001BBEA4E10C0 523 call destructor | this: 000001BBEA4E3990 524 call destructor | this: 000001BBEA4E10C0 525 call destructor | this: 000001BBEA4E10D0 526 call destructor | this: 000000A7B96FFAA8 527 call destructor | this: 000000A7B96FFA78 528 */ 529 #endif 530 531 // 右值(std::move):emplace_back 支持; push_back 支持; 预留足够空间,避免移动元素影响 532 #ifdef EXAMPLE_9 533 { 534 String str1 = "kaizen1"; // ok:通过隐式转换构造对象成功:因为参数为const char* source的重载构造函数前已去掉关键字explicit显式限定约束 535 String str2("kaizen2"); // ok:构造对象成功 536 537 vector<String> tempVec; 538 tempVec.reserve(10); // 与 example_8 的区别 539 tempVec.push_back(std::move(str1)); // ok 540 tempVec.emplace_back(std::move(str2)); // ok 541 } 542 /* 543 constructor: String(const char* source) | this: 0000000D392FF4B8 544 constructor: String(const char* source) | this: 0000000D392FF4E8 545 move constructor | src this: 0000000D392FF4B8 dst this: 000001B9A1585910 546 move constructor | src this: 0000000D392FF4E8 dst this: 000001B9A1585920 547 call destructor | this: 000001B9A1585910 548 call destructor | this: 000001B9A1585920 549 call destructor | this: 0000000D392FF4E8 550 call destructor | this: 0000000D392FF4B8 551 */ 552 #endif 553 554 #ifdef EXAMPLE_10 555 // emplace_back 使用条件:自定义类型必须要有重载(自定义)构造函数,否则只能使用默认构造函数 556 { 557 struct Person 558 { 559 int id; 560 }; 561 vector<Person> myTempVec; 562 myTempVec.emplace_back(Person{}); 563 //myTempVec.emplace_back(2); //compile error:默认构造函数参数为空;再没有任何重载构造函数,编译器不能利用2这个形参完成构造Person对象的使命! 564 } 565 #endif 566 567 #ifdef EXAMPLE_11 568 // 不论哪种方式,传入左值,执行后,对左值无影响 569 { 570 String str1 = "kaizen1"; 571 String str2 = "kaizen2"; 572 573 vector<String> myTempVec; 574 std::cout << "before emplace str1:value == " << str1.getData() << endl; 575 myTempVec.emplace_back(str1); 576 std::cout << "after emplace str1:value == " << str1.getData() << endl; 577 578 std::cout << "before push str2:value == " << str2.getData() << endl; 579 myTempVec.push_back(str2); 580 std::cout << "after push str2:value == " << str2.getData() << endl; 581 } 582 /* 583 constructor: String(const char* source) | this: 00000070548FF998 584 constructor: String(const char* source) | this: 00000070548FF9C8 585 before emplace str1:value == kaizen1 586 copy constructor: String(const String& s) | src this: 00000070548FF998 dst this: 000001B9B73C3260 587 after emplace str1:value == kaizen1 588 before push str2:value == kaizen2 589 copy constructor: String(const String& s) | src this: 00000070548FF9C8 dst this: 000001B9B73C1BB0 590 move constructor | src this: 000001B9B73C3260 dst this: 000001B9B73C1BA0 591 call destructor | this: 000001B9B73C3260 592 after push str2:value == kaizen2 593 call destructor | this: 000001B9B73C1BA0 594 call destructor | this: 000001B9B73C1BB0 595 call destructor | this: 00000070548FF9C8 596 call destructor | this: 00000070548FF998 597 */ 598 #endif 599 600 #ifdef EXAMPLE_12 601 // emplace_back 支持可变参数模板;push_back 支持对象 602 { 603 struct Person 604 { 605 Person(int id, String name) : m_id(id), m_name(name) 606 { } 607 608 int m_id; 609 String m_name; 610 }; 611 vector<Person> myTempVec; 612 myTempVec.reserve(10); 613 //myTempVec.push_back(610580, "kaizen0"); // compile error:不支持 614 myTempVec.push_back({ 610581, "kaizen1" }); // ok {}列表会先构造临时对象,然后将临时对象传入 615 //myTempVec.emplace_back({ 601582, "kaizen2" }); // compile error:不支持 616 myTempVec.emplace_back(Person{ 601583, "kaizen3" }); // ok 617 myTempVec.emplace_back(601584, "kaizen4"); // ok 因为emplace_back用的是可变参数模板 618 } 619 /* 620 constructor: String(const char* source) | this: 000000A08FAFF6B8 621 copy constructor: String(const String& s) | src this: 000000A08FAFF6B8 dst this: 000000A08FAFF690 622 call destructor | this: 000000A08FAFF6B8 623 move constructor | src this: 000000A08FAFF690 dst this: 0000020EC9594528 624 call destructor | this: 000000A08FAFF690 625 constructor: String(const char* source) | this: 000000A08FAFF738 626 copy constructor: String(const String& s) | src this: 000000A08FAFF738 dst this: 000000A08FAFF710 627 call destructor | this: 000000A08FAFF738 628 move constructor | src this: 000000A08FAFF710 dst this: 0000020EC9594540 629 call destructor | this: 000000A08FAFF710 630 constructor: String(const char* source) | this: 000000A08FAFF1F8 631 copy constructor: String(const String& s) | src this: 000000A08FAFF1F8 dst this: 0000020EC9594558 632 call destructor | this: 000000A08FAFF1F8 633 call destructor | this: 0000020EC9594528 634 call destructor | this: 0000020EC9594540 635 call destructor | this: 0000020EC9594558 636 */ 637 #endif 638 639 #ifdef EXAMPLE_13 640 // 两者效率比较:emplace_back略胜一筹 [当然传参的方式不同,可能会有差异,具体场景特殊分析]: 641 double total_cost_loop_push = 0; 642 double total_cost_loop_emplace = 0; 643 for (int i = 0; i < LOOP_COUNT; ++i) 644 { 645 vector<String> myVec1, myVec2; 646 total_cost_loop_push += FV_ByPushBack(myVec1); 647 total_cost_loop_emplace += FV_ByEmplaceBack(myVec2); 648 std::cout << std::endl; 649 } 650 std::cout << "the average of pushBack : " << total_cost_loop_push / LOOP_COUNT << std::endl; 651 std::cout << "the average of emplaceBack : " << total_cost_loop_emplace / LOOP_COUNT << std::endl; 652 /* 653 FV_ByPushBack :: 245 654 FV_ByEmplaceBack :: 188 655 656 FV_ByPushBack :: 258 657 FV_ByEmplaceBack :: 194 658 659 FV_ByPushBack :: 232 660 FV_ByEmplaceBack :: 181 661 662 FV_ByPushBack :: 215 663 FV_ByEmplaceBack :: 178 664 665 FV_ByPushBack :: 331 666 FV_ByEmplaceBack :: 176 667 668 the average of pushBack : 256.2 669 the average of emplaceBack : 183.4 670 */ 671 #endif 672 673 system("pause"); 674 }
【4】结论:建议更多使用emplace_back
4.1 声明:本地使用VS2019,编译示例应用C++语言标准:ISO C++17标准(std:C++17)
4.2 提示:示例代码中,可通过修改宏EXAMPLE_的最后编号,逐个分析每个示例的场景。
4.2 示例说明:
通过示例1(EXAMPLE_1)情形,说明:
如果重载构造函数使用explicit修饰,想通过直接利用参数增加对象,则只有使用emplace_back才能通过编译,使用push_back会报编译错误,因为无法进行隐式转换。
相比emplace_back,push_back不支持显式构造对象;也可理解原因同353行一致。
通过示例2(EXAMPLE_2)情形,说明:
如果重载构造函数没有使用explicit修饰,想通过直接利用参数增加对象,使用emplace_back或push_back均可。原因与示例1反之。
通过示例3(EXAMPLE_3)情形,说明:
如果提前可明确欲添加元素数量,预留足够空间,可避免容器扩增空间引起移动元素的消耗。
通过示例4(EXAMPLE_4)情形,说明:
如果欲传入左值,使用emplace_back或push_back均可,而且效率相同。
通过示例5(EXAMPLE_5)情形,说明:
如果欲传入左值,且提前可明确欲添加元素数量,预留足够空间,可避免容器扩增空间引起移动元素的消耗。
通过示例6(EXAMPLE_6)情形,说明:
如果欲传入右值,使用emplace_back或push_back均可,而且效率相同。
通过示例7(EXAMPLE_7)情形,说明:
如果欲传入右值,且提前可明确欲添加元素数量,预留足够空间,可避免容器扩增空间引起移动元素的消耗。
通过示例8(EXAMPLE_8)情形,说明:
如果欲传入右值(std::move转换后),使用emplace_back或push_back均可,而且效率相同。
通过示例9(EXAMPLE_9)情形,说明:
如果欲传入右值(std::move转换后),且提前可明确欲添加元素数量,预留足够空间,可避免容器扩增空间引起移动元素的消耗。
通过示例10(EXAMPLE_10)情形,说明:
如果想通过直接利用参数增加对象,emplace_back 的使用条件:自定义类型必须要有重载(自定义)构造函数,否则只能使用默认构造函数。因为总得让编译器知道与参数匹配的构造函数,否则编译器也无能为力。
通过示例11(EXAMPLE_11)情形,说明:
不论使用emplace_back或push_back哪种方式,传入左值,执行后,对左值无影响。
通过示例12(EXAMPLE_12)情形,说明:
emplace_back 支持可变参数模板,比push_back更方便的优势。
通过示例13(EXAMPLE_13)情形,说明:
如果想通过直接利用参数增加对象时,两者效率比较:emplace_back略胜一筹 [当然,传参方式不同,效率可能会相同,具体场景特殊分析,比如左值或右值的示例5和示例7]:
Good Good Study, Day Day Up.
顺序 选择 循环 总结