本文所讲对移动函数使用noexcept
修饰时带来的效率提升只针对std::vector
。而对std::deque
来说没有功效。
1. 针对std::vector
1.1 move functions with noexcept
当移动构造函数有noexcept
修饰时,在对std::vector
进行push_back
扩充致使vector
的size等于capacity时需要将原有数据搬运到新的更大的空间时会调用移动构造函数将vector
旧空间的数据复用给新申请的更大的空间。
图1 std::vector大小由1变2,扩容时对原有数据调用移动构造
图2 std::vector大小由2变4,扩容时对原有数据调用移动构造
#include <iostream>
#include <vector>
#include <string>
namespace test_noexcept
{
class MyString
{
public:
MyString():len_(0), data_(nullptr)
{
++sDCtor_;
}
MyString(const char* cs) : len_(strlen(cs))
{
init_data(cs);
++sCtor_;
//std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
}
~MyString() noexcept // 析构函数的noexcept是默认的,写不写都可以
{
len_ = 0;
delete[]data_; data_ = nullptr;
++sDtor_;
std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
MyString(const MyString& mstr) : len_(mstr.len_)
{
init_data(mstr.data_);
++sCopyCtor_;
//std::cout << "CopyCtor called " << sCopyCtor_ << " time(s)." << std::endl;
}
MyString& operator=(const MyString& mstr)
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
len_ = mstr.len_;
init_data(mstr.data_);
}
++sCopyAsg_;
//std::cout << "CopyAsg called " << sCopyAsg_ << " time(s)." << std::endl;
return *this;
}
MyString(MyString&& mstr) noexcept
{
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
++sMoveCtor_;
//std::cout << "MoveCtor called " << sMoveCtor_ << " time(s)." << std::endl;
}
MyString& operator=(MyString&& mstr) noexcept
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
}
++sMoveAsg_;
//std::cout << "MoveAsg called " << sMoveAsg_ << " time(s)." << std::endl;
return *this;
}
// overload 'operator<' and 'operator==' for set
bool operator<(const MyString& mstr) const
{
return std::string(data_) < std::string( mstr.data_);
}
bool operator==(const MyString& mstr) const
{
return std::string(data_) == std::string(mstr.data_);
}
void print() const
{
if(data_)
{
std::cout << data_ << std::endl;
}
}
private:
void init_data(const char* cs)
{
data_ = new char[len_+1]; // 虽然实际占用内存len_,但为了串尾的'\0'需要申请len_+1
//memset(data_, 0, sizeof(char)*(len_+1));
data_[len_] = '\0';
memcpy(data_, cs, len_*sizeof(char));
}
public:
static void statistics()
{
std::cout << "default-ctor called " << sDCtor_ << " time(s)." << std::endl;
std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
std::cout << "copy-ctor called " << sCopyCtor_ << " time(s)." << std::endl;
std::cout << "move-ctor called " << sMoveCtor_ << " time(s)." << std::endl;
std::cout << "copy-assign called " << sCopyAsg_ << " time(s)." << std::endl;
std::cout << "move-assign called " << sMoveAsg_ << " time(s)." << std::endl;
//std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
static void reset_statistics()
{
sDCtor_ = sCtor_ = sDtor_ = sCopyCtor_ = sCopyAsg_ = sMoveCtor_ = sMoveAsg_ = 0;
}
private:
static int sDCtor_;
static int sCtor_;
static int sDtor_;
static int sCopyCtor_;
static int sCopyAsg_;
static int sMoveCtor_;
static int sMoveAsg_;
private:
char* data_;
unsigned int len_;
};
int MyString::sDCtor_ = 0;
int MyString::sCtor_ = 0;
int MyString::sDtor_ = 0;
int MyString::sCopyCtor_ = 0;
int MyString::sCopyAsg_ = 0;
int MyString::sMoveCtor_ = 0;
int MyString::sMoveAsg_ = 0;
auto main() -> int
{
std::cout << "testing noexcept......" << std::endl;
std::vector<MyString> vec;
vec.push_back(MyString("abc"));
vec.push_back(MyString("123"));
vec.push_back(MyString(",./"));
MyString::statistics();
//MyString::reset_statistics();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
testing noexcept......
dtor called 1 time(s).
dtor called 2 time(s).
dtor called 3 time(s).
dtor called 4 time(s).
dtor called 5 time(s).
dtor called 6 time(s).
default-ctor called 0 time(s).
ctor called 3 time(s).
copy-ctor called 0 time(s).
move-ctor called 6 time(s).
copy-assign called 0 time(s).
move-assign called 0 time(s).
------------------------------
dtor called 7 time(s).
dtor called 8 time(s).
dtor called 9 time(s).
1.2 move functions without noexcept
当移动构造函数无noexcept
修饰时,在对std::vector
进行push_back
扩充致使vector
需要将原有数据搬运到新的更大的空间时会调用拷贝构造函数将vector
旧空间的数据拷贝到新申请的更大的空间。
图3 std::vector大小由1变2,扩容时调用拷贝构造
图4 std::vector大小由2变4,扩容时调用拷贝构造
#include <iostream>
#include <vector>
#include <string>
namespace test_noexcept
{
class MyString
{
public:
MyString():len_(0), data_(nullptr)
{
++sDCtor_;
}
MyString(const char* cs) : len_(strlen(cs))
{
init_data(cs);
++sCtor_;
//std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
}
~MyString() noexcept // 析构函数的noexcept是默认的,写不写都可以
{
len_ = 0;
delete[]data_; data_ = nullptr;
++sDtor_;
std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
MyString(const MyString& mstr) : len_(mstr.len_)
{
init_data(mstr.data_);
++sCopyCtor_;
//std::cout << "CopyCtor called " << sCopyCtor_ << " time(s)." << std::endl;
}
MyString& operator=(const MyString& mstr)
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
len_ = mstr.len_;
init_data(mstr.data_);
}
++sCopyAsg_;
//std::cout << "CopyAsg called " << sCopyAsg_ << " time(s)." << std::endl;
return *this;
}
MyString(MyString&& mstr) /*noexcept*/
{
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
++sMoveCtor_;
//std::cout << "MoveCtor called " << sMoveCtor_ << " time(s)." << std::endl;
}
MyString& operator=(MyString&& mstr) /*noexcept*/
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
}
++sMoveAsg_;
//std::cout << "MoveAsg called " << sMoveAsg_ << " time(s)." << std::endl;
return *this;
}
// overload 'operator<' and 'operator==' for set
bool operator<(const MyString& mstr) const
{
return std::string(data_) < std::string( mstr.data_);
}
bool operator==(const MyString& mstr) const
{
return std::string(data_) == std::string(mstr.data_);
}
void print() const
{
if(data_)
{
std::cout << data_ << std::endl;
}
}
private:
void init_data(const char* cs)
{
data_ = new char[len_+1]; // 虽然实际占用内存len_,但为了串尾的'\0'需要申请len_+1
//memset(data_, 0, sizeof(char)*(len_+1));
data_[len_] = '\0';
memcpy(data_, cs, len_*sizeof(char));
}
public:
static void statistics()
{
std::cout << "default-ctor called " << sDCtor_ << " time(s)." << std::endl;
std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
std::cout << "copy-ctor called " << sCopyCtor_ << " time(s)." << std::endl;
std::cout << "move-ctor called " << sMoveCtor_ << " time(s)." << std::endl;
std::cout << "copy-assign called " << sCopyAsg_ << " time(s)." << std::endl;
std::cout << "move-assign called " << sMoveAsg_ << " time(s)." << std::endl;
//std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
static void reset_statistics()
{
sDCtor_ = sCtor_ = sDtor_ = sCopyCtor_ = sCopyAsg_ = sMoveCtor_ = sMoveAsg_ = 0;
}
private:
static int sDCtor_;
static int sCtor_;
static int sDtor_;
static int sCopyCtor_;
static int sCopyAsg_;
static int sMoveCtor_;
static int sMoveAsg_;
private:
char* data_;
unsigned int len_;
};
int MyString::sDCtor_ = 0;
int MyString::sCtor_ = 0;
int MyString::sDtor_ = 0;
int MyString::sCopyCtor_ = 0;
int MyString::sCopyAsg_ = 0;
int MyString::sMoveCtor_ = 0;
int MyString::sMoveAsg_ = 0;
auto main() -> int
{
std::cout << "testing noexcept......" << std::endl;
std::vector<MyString> vec;
vec.push_back(MyString("abc"));
vec.push_back(MyString("123"));
vec.push_back(MyString(",./"));
MyString::statistics();
//MyString::reset_statistics();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
testing noexcept......
dtor called 1 time(s).
dtor called 2 time(s).
dtor called 3 time(s).
dtor called 4 time(s).
dtor called 5 time(s).
dtor called 6 time(s).
default-ctor called 0 time(s).
ctor called 3 time(s).
copy-ctor called 3 time(s).
move-ctor called 3 time(s).
copy-assign called 0 time(s).
move-assign called 0 time(s).
------------------------------
dtor called 7 time(s).
dtor called 8 time(s).
dtor called 9 time(s).
2. 针对std::deque
由2.1和2.2章节的示例可知,对于std::deque
来说,移动函数加不加noexcept
,在std::deque
进行增长的时候在搬运已有数据时都是调用的移动构造函数。
2.1 move functions with noexcept
#include <iostream>
#include <vector>
#include <deque>
#include <string>
namespace test_noexcept
{
class MyString
{
public:
MyString():len_(0), data_(nullptr)
{
++sDCtor_;
}
MyString(const char* cs) : len_(strlen(cs))
{
init_data(cs);
++sCtor_;
//std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
}
~MyString() noexcept
{
len_ = 0;
delete[]data_; data_ = nullptr;
++sDtor_;
std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
MyString(const MyString& mstr) : len_(mstr.len_)
{
init_data(mstr.data_);
++sCopyCtor_;
//std::cout << "CopyCtor called " << sCopyCtor_ << " time(s)." << std::endl;
}
MyString& operator=(const MyString& mstr)
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
len_ = mstr.len_;
init_data(mstr.data_);
}
++sCopyAsg_;
//std::cout << "CopyAsg called " << sCopyAsg_ << " time(s)." << std::endl;
return *this;
}
MyString(MyString&& mstr) noexcept/* */
{
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
++sMoveCtor_;
//std::cout << "MoveCtor called " << sMoveCtor_ << " time(s)." << std::endl;
}
MyString& operator=(MyString&& mstr) noexcept/* */
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
}
++sMoveAsg_;
//std::cout << "MoveAsg called " << sMoveAsg_ << " time(s)." << std::endl;
return *this;
}
// overload 'operator<' and 'operator==' for set
bool operator<(const MyString& mstr) const
{
return std::string(data_) < std::string( mstr.data_);
}
bool operator==(const MyString& mstr) const
{
return std::string(data_) == std::string(mstr.data_);
}
void print() const
{
if(data_)
{
std::cout << data_ << std::endl;
}
}
private:
void init_data(const char* cs)
{
data_ = new char[len_+1]; // 虽然实际占用内存len_,但为了串尾的'\0'需要申请len_+1
//memset(data_, 0, sizeof(char)*(len_+1));
data_[len_] = '\0';
memcpy(data_, cs, len_*sizeof(char));
}
public:
static void statistics()
{
std::cout << "default-ctor called " << sDCtor_ << " time(s)." << std::endl;
std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
std::cout << "copy-ctor called " << sCopyCtor_ << " time(s)." << std::endl;
std::cout << "move-ctor called " << sMoveCtor_ << " time(s)." << std::endl;
std::cout << "copy-assign called " << sCopyAsg_ << " time(s)." << std::endl;
std::cout << "move-assign called " << sMoveAsg_ << " time(s)." << std::endl;
//std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
static void reset_statistics()
{
sDCtor_ = sCtor_ = sDtor_ = sCopyCtor_ = sCopyAsg_ = sMoveCtor_ = sMoveAsg_ = 0;
}
private:
static int sDCtor_;
static int sCtor_;
static int sDtor_;
static int sCopyCtor_;
static int sCopyAsg_;
static int sMoveCtor_;
static int sMoveAsg_;
private:
char* data_;
unsigned int len_;
};
int MyString::sDCtor_ = 0;
int MyString::sCtor_ = 0;
int MyString::sDtor_ = 0;
int MyString::sCopyCtor_ = 0;
int MyString::sCopyAsg_ = 0;
int MyString::sMoveCtor_ = 0;
int MyString::sMoveAsg_ = 0;
auto main() -> int
{
std::cout << "testing noexcept......" << std::endl;
#if 0
std::vector<MyString> vec;
vec.push_back(MyString("abc"));
vec.push_back(MyString("123"));
vec.push_back(MyString(",./"));
#endif
std::deque<MyString> deq;
deq.emplace_back(MyString("abc"));
deq.emplace_back(MyString("123"));
deq.emplace_back(MyString(",./"));
MyString::statistics();
//MyString::reset_statistics();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
testing noexcept......
dtor called 1 time(s).
dtor called 2 time(s).
dtor called 3 time(s).
default-ctor called 0 time(s).
ctor called 3 time(s).
copy-ctor called 0 time(s).
move-ctor called 3 time(s).
copy-assign called 0 time(s).
move-assign called 0 time(s).
------------------------------
dtor called 4 time(s).
dtor called 5 time(s).
dtor called 6 time(s).
2.2 move functions without noexcept
#include <iostream>
#include <deque>
#include <string>
namespace test_noexcept
{
class MyString
{
public:
MyString():len_(0), data_(nullptr)
{
++sDCtor_;
}
MyString(const char* cs) : len_(strlen(cs))
{
init_data(cs);
++sCtor_;
//std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
}
~MyString() noexcept
{
len_ = 0;
delete[]data_; data_ = nullptr;
++sDtor_;
std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
MyString(const MyString& mstr) : len_(mstr.len_)
{
init_data(mstr.data_);
++sCopyCtor_;
//std::cout << "CopyCtor called " << sCopyCtor_ << " time(s)." << std::endl;
}
MyString& operator=(const MyString& mstr)
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
len_ = mstr.len_;
init_data(mstr.data_);
}
++sCopyAsg_;
//std::cout << "CopyAsg called " << sCopyAsg_ << " time(s)." << std::endl;
return *this;
}
MyString(MyString&& mstr) /*noexcept*/
{
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
++sMoveCtor_;
//std::cout << "MoveCtor called " << sMoveCtor_ << " time(s)." << std::endl;
}
MyString& operator=(MyString&& mstr) /*noexcept*/
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
}
++sMoveAsg_;
//std::cout << "MoveAsg called " << sMoveAsg_ << " time(s)." << std::endl;
return *this;
}
// overload 'operator<' and 'operator==' for set
bool operator<(const MyString& mstr) const
{
return std::string(data_) < std::string( mstr.data_);
}
bool operator==(const MyString& mstr) const
{
return std::string(data_) == std::string(mstr.data_);
}
void print() const
{
if(data_)
{
std::cout << data_ << std::endl;
}
}
private:
void init_data(const char* cs)
{
data_ = new char[len_+1]; // 虽然实际占用内存len_,但为了串尾的'\0'需要申请len_+1
//memset(data_, 0, sizeof(char)*(len_+1));
data_[len_] = '\0';
memcpy(data_, cs, len_*sizeof(char));
}
public:
static void statistics()
{
std::cout << "default-ctor called " << sDCtor_ << " time(s)." << std::endl;
std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
std::cout << "copy-ctor called " << sCopyCtor_ << " time(s)." << std::endl;
std::cout << "move-ctor called " << sMoveCtor_ << " time(s)." << std::endl;
std::cout << "copy-assign called " << sCopyAsg_ << " time(s)." << std::endl;
std::cout << "move-assign called " << sMoveAsg_ << " time(s)." << std::endl;
//std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
static void reset_statistics()
{
sDCtor_ = sCtor_ = sDtor_ = sCopyCtor_ = sCopyAsg_ = sMoveCtor_ = sMoveAsg_ = 0;
}
private:
static int sDCtor_;
static int sCtor_;
static int sDtor_;
static int sCopyCtor_;
static int sCopyAsg_;
static int sMoveCtor_;
static int sMoveAsg_;
private:
char* data_;
unsigned int len_;
};
int MyString::sDCtor_ = 0;
int MyString::sCtor_ = 0;
int MyString::sDtor_ = 0;
int MyString::sCopyCtor_ = 0;
int MyString::sCopyAsg_ = 0;
int MyString::sMoveCtor_ = 0;
int MyString::sMoveAsg_ = 0;
auto main() -> int
{
std::cout << "testing noexcept......" << std::endl;
std::deque<MyString> deq;
deq.emplace_back(MyString("abc"));
deq.emplace_back(MyString("123"));
deq.emplace_back(MyString(",./"));
MyString::statistics();
//MyString::reset_statistics();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
输出:
testing noexcept......
dtor called 1 time(s).
dtor called 2 time(s).
dtor called 3 time(s).
default-ctor called 0 time(s).
ctor called 3 time(s).
copy-ctor called 0 time(s).
move-ctor called 3 time(s).
copy-assign called 0 time(s).
move-assign called 0 time(s).
------------------------------
dtor called 4 time(s).
dtor called 5 time(s).
dtor called 6 time(s).
3. 如果移动函数写成default
会发生什么?大概是崩溃吧
#include <iostream>
#include <vector>
#include <deque>
#include <string>
namespace test_noexcept
{
class MyString
{
public:
MyString():len_(0), data_(nullptr)
{
++sDCtor_;
}
MyString(const char* cs) : len_(strlen(cs))
{
init_data(cs);
++sCtor_;
//std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
}
~MyString() noexcept
{
len_ = 0;
delete[]data_; data_ = nullptr;
++sDtor_;
std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
MyString(const MyString& mstr) : len_(mstr.len_)
{
init_data(mstr.data_);
++sCopyCtor_;
//std::cout << "CopyCtor called " << sCopyCtor_ << " time(s)." << std::endl;
}
MyString& operator=(const MyString& mstr)
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
len_ = mstr.len_;
init_data(mstr.data_);
}
++sCopyAsg_;
//std::cout << "CopyAsg called " << sCopyAsg_ << " time(s)." << std::endl;
return *this;
}
#if 0
MyString(MyString&& mstr) noexcept/* */
{
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
++sMoveCtor_;
//std::cout << "MoveCtor called " << sMoveCtor_ << " time(s)." << std::endl;
}
MyString& operator=(MyString&& mstr) noexcept/* */
{
if(this != &mstr)
{
if(data_)
{
delete [] data_; data_ = nullptr;
len_ = 0;
}
data_ = mstr.data_;
len_ = mstr.len_;
mstr.data_ = nullptr;
mstr.len_ = 0;
}
++sMoveAsg_;
//std::cout << "MoveAsg called " << sMoveAsg_ << " time(s)." << std::endl;
return *this;
}
#endif
MyString(MyString&&)noexcept = default;
MyString&operator=(MyString&&)noexcept = default;
// overload 'operator<' and 'operator==' for set
bool operator<(const MyString& mstr) const
{
return std::string(data_) < std::string( mstr.data_);
}
bool operator==(const MyString& mstr) const
{
return std::string(data_) == std::string(mstr.data_);
}
void print() const
{
if(data_)
{
std::cout << data_ << std::endl;
}
}
private:
void init_data(const char* cs)
{
data_ = new char[len_+1]; // 虽然实际占用内存len_,但为了串尾的'\0'需要申请len_+1
//memset(data_, 0, sizeof(char)*(len_+1));
data_[len_] = '\0';
memcpy(data_, cs, len_*sizeof(char));
}
public:
static void statistics()
{
std::cout << "default-ctor called " << sDCtor_ << " time(s)." << std::endl;
std::cout << "ctor called " << sCtor_ << " time(s)." << std::endl;
std::cout << "copy-ctor called " << sCopyCtor_ << " time(s)." << std::endl;
std::cout << "move-ctor called " << sMoveCtor_ << " time(s)." << std::endl;
std::cout << "copy-assign called " << sCopyAsg_ << " time(s)." << std::endl;
std::cout << "move-assign called " << sMoveAsg_ << " time(s)." << std::endl;
//std::cout << "dtor called " << sDtor_ << " time(s)." << std::endl;
}
static void reset_statistics()
{
sDCtor_ = sCtor_ = sDtor_ = sCopyCtor_ = sCopyAsg_ = sMoveCtor_ = sMoveAsg_ = 0;
}
private:
static int sDCtor_;
static int sCtor_;
static int sDtor_;
static int sCopyCtor_;
static int sCopyAsg_;
static int sMoveCtor_;
static int sMoveAsg_;
private:
char* data_;
unsigned int len_;
};
int MyString::sDCtor_ = 0;
int MyString::sCtor_ = 0;
int MyString::sDtor_ = 0;
int MyString::sCopyCtor_ = 0;
int MyString::sCopyAsg_ = 0;
int MyString::sMoveCtor_ = 0;
int MyString::sMoveAsg_ = 0;
auto main() -> int
{
std::cout << "testing noexcept......" << std::endl;
std::vector<MyString> vec;
vec.push_back(MyString("abc"));
vec.push_back(MyString("123"));
vec.push_back(MyString(",./"));
#if 0
std::deque<MyString> deq;
deq.emplace_back(MyString("abc"));
deq.emplace_back(MyString("123"));
deq.emplace_back(MyString(",./"));
#endif
MyString::statistics();
//MyString::reset_statistics();
std::cout << "------------------------------" << std::endl;
return 0;
}
}
编译后,运行如下(cmake -G "Visual Studio 15 2017" -A x64 ..
):
Reference
文中附图源自侯捷,感谢!