- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在测试一些代码,其中类中有一个 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
涉及两个不同的阶段:
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/
场景如下: 我将我的应用程序运行所需的几个 .xml(某种配置)文件捆绑在一个 .jar 文件中。 jar 文件具有以下结构: settings-1.0.0.jar ˪ resources/ ˪
我是一名优秀的程序员,十分优秀!