gpt4 book ai didi

c++ - copy-and-swap 习语的低效率?

转载 作者:可可西里 更新时间:2023-11-01 15:41:24 35 4
gpt4 key购买 nike

我正在测试一些代码,其中类中有一个 std::vector 数据成员。该类既可复制可移动operator= 的实现方式如here 所述。使用 copy-and-swap 习语

如果有两个vector,比如v1大容量,v2小容量,v2 被复制到v1 (v1 = v2),赋值后保留v1中的大容量;这是有道理的,因为接下来的 v1.push_back() 调用不必强制进行新的重新分配(换句话说:释放已经可用的内存,然后重新分配它以增加 vector 没有多大意义).

但是,如果对以vector 为数据成员的 进行相同的赋值,则行为不同,并且在赋值之后更大的容量是保留。

如果 copy-and-swap 惯用语被使用,复制operator=和移动operator=是分开实现的 ,则行为符合预期(对于普通的非成员 vector)。

这是为什么呢?我们是否应该不遵循 copy-and-swap 习语,而是实现 operator=(const X& other) (copy op=) 和 operator=(X&& other) (move op=) 分开以获得最佳性能?

这是可重现测试的输出使用 copy-and-swap 惯用语(注意在这种情况下,在x1 = x2之后,x1.GetV ().capacity() 是 1,000,而不是 1,000,000:

C:\TEMP\CppTests>cl /EHsc /W4 /nologo /DTEST_COPY_AND_SWAP test.cpp
test.cpp

C:\TEMP\CppTests>test.exe
v1.capacity() = 1000000
v2.capacity() = 1000

After copy v1 = v2:
v1.capacity() = 1000000
v2.capacity() = 1000

[Copy-and-swap]

x1.GetV().capacity() = 1000000
x2.GetV().capacity() = 1000

After x1 = x2:
x1.GetV().capacity() = 1000
x2.GetV().capacity() = 1000

这是输出没有 copy-and-swap 惯用语(请注意在这种情况下 x1.GetV().capacity() = 1000000,如预期的那样):

C:\TEMP\CppTests>cl /EHsc /W4 /nologo test.cpp
test.cpp

C:\TEMP\CppTests>test.exe
v1.capacity() = 1000000
v2.capacity() = 1000

After copy v1 = v2:
v1.capacity() = 1000000
v2.capacity() = 1000

[Copy-op= and move-op=]

x1.GetV().capacity() = 1000000
x2.GetV().capacity() = 1000

After x1 = x2:
x1.GetV().capacity() = 1000000
x2.GetV().capacity() = 1000

可编译示例代码如下(使用 VS2010 SP1/VC10 测试):

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

class X
{
public:
X()
{
}

explicit X(const size_t initialCapacity)
{
m_v.reserve(initialCapacity);
}

X(const X& other)
: m_v(other.m_v)
{
}

X(X&& other)
: m_v(move(other.m_v))
{
}

void SetV(const vector<double>& v)
{
m_v = v;
}

const vector<double>& GetV() const
{
return m_v;
}

#ifdef TEST_COPY_AND_SWAP
//
// Implement a unified op= with copy-and-swap idiom.
//

X& operator=(X other)
{
swap(*this, other);
return *this;
}

friend void swap(X& lhs, X& rhs)
{
using std::swap;

swap(lhs.m_v, rhs.m_v);
}
#else
//
// Implement copy op= and move op= separately.
//

X& operator=(const X& other)
{
if (this != &other)
{
m_v = other.m_v;
}
return *this;
}

X& operator=(X&& other)
{
if (this != &other)
{
m_v = move(other.m_v);
}
return *this;
}
#endif

private:
vector<double> m_v;
};

// Test vector assignment from a small vector to a vector with big capacity.
void Test1()
{
vector<double> v1;
v1.reserve(1000*1000);

vector<double> v2(1000);

cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';

v1 = v2;
cout << "\nAfter copy v1 = v2:\n";
cout << "v1.capacity() = " << v1.capacity() << '\n';
cout << "v2.capacity() = " << v2.capacity() << '\n';
}

// Similar to Test1, but now vector is a data member inside a class.
void Test2()
{
#ifdef TEST_COPY_AND_SWAP
cout << "[Copy-and-swap]\n\n";
#else
cout << "[Copy-op= and move-op=]\n\n";
#endif

X x1(1000*1000);

vector<double> v2(1000);
X x2;
x2.SetV(v2);

cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';

x1 = x2;
cout << "\nAfter x1 = x2:\n";
cout << "x1.GetV().capacity() = " << x1.GetV().capacity() << '\n';
cout << "x2.GetV().capacity() = " << x2.GetV().capacity() << '\n';
}

int main()
{
Test1();
cout << '\n';
Test2();
}

最佳答案

使用 std::vector 进行 copy-and-swap 确实会导致性能损失。这里的主要问题是复制 std::vector 涉及两个不同的阶段:

  1. 分配新的内存部分
  2. 复制内容。

copy-and-swap 可以消除 #2 但不能 #1。考虑在调用 swap() 之前但在输入赋值操作之后会观察到什么。你有三个 vector ——一个即将被覆盖,一个是拷贝,一个是原始参数。

这清楚地表明,如果即将被覆盖的 vector 具有足够或过剩的容量,则在创建中间 vector 时会造成浪费,并且会损失源的额外容量。其他容器也可以这样做。

copy-and-swap 是一个很好的基准,尤其是在涉及到异常安全时,但它不是全局最高性能的解决方案。如果您处于狭窄区域,那么其他更专业的实现可能会更高效 - 但请注意,该区域的异常安全性非常重要,如果不进行 copy-and-swap ,有时是不可能的。

关于c++ - copy-and-swap 习语的低效率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15188156/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com