C++复制构造函数和移动构造函数的性能速度对比

一、对比

C++11中引入了移动语义,用在资源频繁拷贝的地方。通过移动构造函数可以减少不必要的复制,带来性能上的提升。下面我们通过代码比较复制(拷贝)构造函数和移动构造函数的性能(速度)。

测试代码如下:

#include 
#include 
#include 
#include 
#include 

using namespace std;


class Person
{
private:
    int age;
    string name;
    int* data;

public:
    Person() : data(new int[1000000]) {}
    ~Person() { delete[] data; }

    // 拷贝构造函数
    Person(const Person& p) :
        age(p.age),
        name(p.name),
        data(new int[1000000]) {
        std::copy(p.data, p.data + 1000000, data);
        //cout << "Copy Constructor" << endl;
    }

    // 拷贝赋值运算符
    Person& operator=(const Person& p) {
        this->age = p.age;
        this->name = p.name;
        this->data = new int[1000000];
        std::copy(p.data, p.data + 1000000, data);
        //cout << "Copy Assign" << endl;
        return *this;
    }

    // 移动构造函数
    Person(Person&& p) :
        age(std::move(p.age)),
        name(std::move(p.name)),
        data(p.data) {
        p.data = nullptr; // 源对象的指针应该置空,以免源对象析构时影响本对象
        //cout << "Move Constructor" << endl;
    }

    // 移动赋值运算符
    Person& operator=(Person&& p) {
        this->age = std::move(p.age);
        this->name = std::move(p.name);
        this->data = p.data;
        p.data = nullptr;
        //cout << "Move Assign" << endl;
        return *this;
    }
};

int main()
{
    auto begin1 = std::chrono::high_resolution_clock::now();
    queue queuePerson1;
    for (int i = 0; i < 100; i++)
    {
        Person person;
        queuePerson1.push(person);
    }
    //printf("入队列完毕\n");
    for (int i=0; i< queuePerson1.size(); i++)
    {
        Person person = queuePerson1.front();
        queuePerson1.pop();
    }
    //printf("出队列完毕\n");
    auto end1 = std::chrono::high_resolution_clock::now();
    auto elapsed1 = std::chrono::duration_cast(end1 - begin1);
    printf("拷贝构造时间: %.3f seconds.\n", elapsed1.count() * 1e-9);

    auto begin2 = std::chrono::high_resolution_clock::now();
    queue queuePerson2;
    for (int i = 0; i < 100; i++)
    {
        Person person;
        queuePerson2.push(std::move(person));
    }
    //printf("入队列完毕\n");
    for (int i = 0; i < queuePerson2.size(); i++)
    {
        Person person = std::move(queuePerson2.front());
        queuePerson2.pop();
    }
    //printf("出队列完毕\n");
    auto end2 = std::chrono::high_resolution_clock::now();
    auto elapsed2 = std::chrono::duration_cast(end2 - begin2);
    printf("移动构造时间: %.3f seconds.\n", elapsed2.count() * 1e-9);

    return 0;
}

Visual Studio中选择release模式,编译执行效果如下:

C++复制构造函数和移动构造函数的性能速度对比_第1张图片

可以看到使用移动构造函数Person(Person&& p) 速度比使用拷贝构造函数Person(const Person& p)快200多倍。所以我们尽可能使用移动构造函数代替拷贝构造函数(这里又会涉及到亡值的问题需要注意),比如生产者消费者模式的入队列、出队列操作,我们可以使用移动构造来提示性能。

二、测量执行时间的方法优化

可以根据我另一篇文章《C++计算打印函数和代码块的执行时间(支持所有类型函数)》中的

measure函数对测量方法进行优化。优化后代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;


template
auto measure(T&& func, Args&&... args)->std::future::type>
{
	using return_type = typename std::result_of::type;
	auto task = std::make_shared>
		(std::bind(std::forward(func), std::forward(args)...));
	std::future res = task->get_future();
	auto begin = std::chrono::high_resolution_clock::now();
	(*task)();
	auto end = std::chrono::high_resolution_clock::now();
	auto elapsed = std::chrono::duration_cast(end - begin);
	printf("执行时间: % .3f seconds.\n", elapsed.count() * 1e-9);
	return res;
}


class Person
{
private:
    int age;
    string name;
    int* data;

public:
    Person() : data(new int[1000000]) {}
    ~Person() { delete[] data; }

    // 拷贝构造函数
    Person(const Person& p) :
        age(p.age),
        name(p.name),
        data(new int[1000000]) {
        std::copy(p.data, p.data + 1000000, data);
        //cout << "Copy Constructor" << endl;
    }

    // 拷贝赋值运算符
    Person& operator=(const Person& p) {
        this->age = p.age;
        this->name = p.name;
        this->data = new int[1000000];
        std::copy(p.data, p.data + 1000000, data);
        //cout << "Copy Assign" << endl;
        return *this;
    }

    // 移动构造函数
    Person(Person&& p) :
        age(std::move(p.age)),
        name(std::move(p.name)),
        data(p.data) {
        p.data = nullptr; // 源对象的指针应该置空,以免源对象析构时影响本对象
        //cout << "Move Constructor" << endl;
    }

    // 移动赋值运算符
    Person& operator=(Person&& p) {
        this->age = std::move(p.age);
        this->name = std::move(p.name);
        this->data = p.data;
        p.data = nullptr;
        //cout << "Move Assign" << endl;
        return *this;
    }
};


int main() {
    
    measure([]{
        queue queuePerson1;
        for (int i = 0; i < 100; i++)
        {
            Person person;
            queuePerson1.push(person);
        }
        //printf("入队列完毕\n");
        for (int i = 0; i < queuePerson1.size(); i++)
        {
            Person person = queuePerson1.front();
            queuePerson1.pop();
        }
        });

    measure([] {
        queue queuePerson2;
        for (int i = 0; i < 100; i++)
        {
            Person person;
            queuePerson2.push(std::move(person));
        }
        //printf("入队列完毕\n");
        for (int i = 0; i < queuePerson2.size(); i++)
        {
            Person person = std::move(queuePerson2.front());
            queuePerson2.pop();
        }
        //printf("出队列完毕\n");
        });

    return 0;

}

参考:《C++笔记 · 右值引用,移动语义,移动构造函数和移动赋值运算符》

你可能感兴趣的:(C++,c++)