move functions with VS without noexcept
创始人
2024-04-26 12:07:40
0

本文所讲对移动函数使用noexcept修饰时带来的效率提升只针对std::vector。而对std::deque来说没有功效。

1. 针对std::vector

1.1 move functions with noexcept

当移动构造函数有noexcept修饰时,在对std::vector进行push_back扩充致使vector的size等于capacity时需要将原有数据搬运到新的更大的空间时会调用移动构造函数vector旧空间的数据复用给新申请的更大的空间。

ellipse 图1 std::vector大小由1变2,扩容时对原有数据调用移动构造 ellipse 图2 std::vector大小由2变4,扩容时对原有数据调用移动构造
#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).

1.2 move functions without noexcept

当移动构造函数无noexcept修饰时,在对std::vector进行push_back扩充致使vector需要将原有数据搬运到新的更大的空间时会调用拷贝构造函数vector旧空间的数据拷贝到新申请的更大的空间。

ellipse 图3 std::vector大小由1变2,扩容时调用拷贝构造 ellipse 图4 std::vector大小由2变4,扩容时调用拷贝构造
#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).

2. 针对std::deque

由2.1和2.2章节的示例可知,对于std::deque来说,移动函数加不加noexcept,在std::deque进行增长的时候在搬运已有数据时都是调用的移动构造函数。

2.1 move functions with 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).

2.2 move functions without 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).

3. 如果移动函数写成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 ..):
weird

Reference

文中附图源自侯捷,感谢!

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...