move functions with VS without noexcept

news2024/11/24 17:19:30

本文所讲对移动函数使用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 <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旧空间的数据拷贝到新申请的更大的空间。

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

Reference

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/99445.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

26. GPU以及 没有gpu的情况下使用colab

在PyTorch中&#xff0c;CPU和GPU可以用torch.device(‘cpu’) 和torch.device(‘cuda’)表示。 应该注意的是&#xff0c;cpu设备意味着所有物理CPU和内存&#xff0c; 这意味着PyTorch的计算将尝试使用所有CPU核心。 然而&#xff0c;gpu设备只代表一个卡和相应的显存。 如果…

【大数据技术Hadoop+Spark】Spark SQL、DataFrame、Dataset的讲解及操作演示(图文解释)

一、Spark SQL简介 park SQL是spark的一个模块&#xff0c;主要用于进行结构化数据的SQL查询引擎&#xff0c;开发人员能够通过使用SQL语句&#xff0c;实现对结构化数据的处理&#xff0c;开发人员可以不了解Scala语言和Spark常用API&#xff0c;通过spark SQL&#xff0c;可…

数据挖掘Java——Kmeans算法的实现

一、K-means算法的前置知识 k-means算法&#xff0c;也被称为k-平均或k-均值&#xff0c;是一种得到最广泛使用的聚类算法。相似度的计算根据一个簇中对象的平均值来进行。算法首先随机地选择k个对象&#xff0c;每个对象初始地代表了一个簇的平均值或中心。对剩余的每个对象根…

给 VitePress 添加 algolia 搜索

大家好&#xff0c;我是 Chocolate。 最近在折腾 VitePress&#xff0c;搭建了一个文档项目&#xff1a;ChoDocs&#xff0c;不过文档还不支持搜索功能&#xff0c;虽然目前内容不多&#xff0c;但待我同步完之后&#xff0c;搜索就很有必要了。 之前看 VitePress 官网发现没有…

pikachu靶场暴力破解绕过token防护详解

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是pikachu靶场暴力破解绕过token防护详解。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#x…

基于改进的多目标粒子群算法的微电网多目标调度(三个目标函数)(matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

中央重磅文件明确互联网医疗服务可用医保支付!

文章目录中央重磅文件明确互联网医疗服务可用医保支付&#xff01;中央重磅文件明确互联网医疗服务可用医保支付&#xff01; 当下&#xff0c;互联网医疗机构已加入到新冠防治的“主战场”&#xff0c;在分流线下诊疗发挥了很大作用。国家层面也在进一步鼓励互联网医疗行业发…

基于多尺度形态学梯度进行边缘检测(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

C++中的继承

把握住自己能把握住的点滴&#xff0c;把它做到极致&#xff0c;加油&#xff01; 本节目标1.继承的概念及定义1.1继承的概念1.2 继承定义1.2.1 定义格式1.2.2 继承方式和访问限定符1.2.3 继承基类成员访问方式的变化2.继承中的作用域练习3.基类和派生类对象赋值转换4.派生类的…

Java+SSM网上订餐系统点餐餐厅系统(含源码+论文+答辩PPT等)

项目功能简介: 该项目采用的技术实现如下 后台框架&#xff1a;Spring、SpringMVC、MyBatis UI界面&#xff1a;BootStrap、H-ui 、JSP 数据库&#xff1a;MySQL 系统功能 系统分为前台订餐和后台管理&#xff1a; 1.前台订餐 用户注册、用户登录、我的购物车、我的订单 商品列…

Linux 常用的命令

前言 Linux 的学习对于一个程序员的重要性是不言而喻的。前端开发相比后端开发&#xff0c;接触 Linux 机会相对较少&#xff0c;因此往往容易忽视它。但是学好它却是程序员必备修养之一。 作者使用的是阿里云服务器 ECS &#xff08;最便宜的那种&#xff09; CentOS 7.7 64…

快速了解JSON及JSON的使用

文章目录JSON简介JSON语法JSON 名称/值对JSON对象数组JSON的简单使用JSON简介 JSON&#xff08;JavaScriptObjectNotation&#xff0c;JS对象简谱&#xff09;是一种轻量级的数据交换格式 JS对象简谱&#xff0c;那么JSON如何转换为JS对象&#xff1a; JSON文本格式在语法上与…

多弹协同攻击时的无源定位

题目 采用被动接收方式的无源探测定位技术具有作用距离远、隐蔽接 收、不易被敌方发觉等优点&#xff0c;能有效提高探测系统在电子战环境下的 生存能力和作战能力。 在无源定位的研究中&#xff0c;测向定位技术&#xff08;Direction of Arrival&#xff0c;DOA&#xff09; …

SpringBoot操作Mongo

文章目录引入依赖yaml实体类集合操作创建删除相关注解文档操作添加实验 数据查询添加更新删除引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId> </dependency><de…

Jmeter配置不同业务请求比例,应对综合场景压测

背景 在进行综合场景压测时&#xff0c;遇到了如何实现不同的请求所占比例不同的问题。 有人说将这些请求分别放到单独的线程组下&#xff0c;然后将线程组的线程数按照比例进行配置。 这种方法不是很好&#xff0c;因为服务器对不同的请求处理能力不同&#xff0c;有的处理快…

C规范编辑笔记(八)

往期文章&#xff1a; C规范编辑笔记(一) C规范编辑笔记(二) C规范编辑笔记(三) C规范编辑笔记(四) C规范编辑笔记(五) C规范编辑笔记(六) C规范编辑笔记(七) 正文&#xff1a; 今天来给大家分享我们的第八篇C规范编辑笔记&#xff0c;话不多说&#xff0c;我们直接来看&…

计算机毕设Python+Vue新闻类网站(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于微信小程序的灯具商城系统-计算机毕业设计

项目介绍 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&a…

Java中IO体系

File File类 File类 : 表示计算机中所有的文件和文件夹; [计算机硬盘上除了文件就是文件夹]如何创建File对象 :File(String pathname) : 传入文件路径[String],创建File对象并指向这个路径的文件/文件夹File(String parent, String child) :传入文件路径[String],创建File对象…

【机器学习---02】机器学习相关名词解释

文章目录1. 损失函数、期望风险、经验风险2. 经验风险最小化和结构风险最小化2.1 结构风险&#xff08;正则化&#xff09;2.2 两者的定义3. 训练误差 与 测试误差4. 过拟合 与 欠拟合4.1 过拟合及解决方法4.2 交叉验证4.3 欠拟合5. 泛化误差 与 泛化误差上界5.1 泛化误差5.2 泛…