本文所讲对移动函数使用noexcept
修饰时带来的效率提升只针对std::vector
。而对std::deque
来说没有功效。
std::vector
noexcept
当移动构造函数有noexcept
修饰时,在对std::vector
进行push_back
扩充致使vector
的size等于capacity时需要将原有数据搬运到新的更大的空间时会调用移动构造函数将vector
旧空间的数据复用给新申请的更大的空间。
#include
#include
#include 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 setbool 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 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).
noexcept
当移动构造函数无noexcept
修饰时,在对std::vector
进行push_back
扩充致使vector
需要将原有数据搬运到新的更大的空间时会调用拷贝构造函数将vector
旧空间的数据拷贝到新申请的更大的空间。
#include
#include
#include 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 setbool 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 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).
std::deque
由2.1和2.2章节的示例可知,对于std::deque
来说,移动函数加不加noexcept
,在std::deque
进行增长的时候在搬运已有数据时都是调用的移动构造函数。
noexcept
#include
#include
#include
#include 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 setbool 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 0std::vector vec;vec.push_back(MyString("abc"));vec.push_back(MyString("123"));vec.push_back(MyString(",./"));
#endifstd::deque 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).
noexcept
#include
#include
#include 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 setbool 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 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).
default
会发生什么?大概是崩溃吧#include
#include
#include
#include 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 0MyString(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;}
#endifMyString(MyString&&)noexcept = default;MyString&operator=(MyString&&)noexcept = default;// overload 'operator<' and 'operator==' for setbool 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 vec;vec.push_back(MyString("abc"));vec.push_back(MyString("123"));vec.push_back(MyString(",./"));#if 0std::deque deq;deq.emplace_back(MyString("abc"));deq.emplace_back(MyString("123"));deq.emplace_back(MyString(",./"));
#endifMyString::statistics();//MyString::reset_statistics();std::cout << "------------------------------" << std::endl;return 0;}
}
编译后,运行如下(cmake -G "Visual Studio 15 2017" -A x64 ..
):
文中附图源自侯捷,感谢!