假设我有这样的课程:
struct A{
std::string a;
std::string b;
std::string c;
std::string d;
};
如果我使用std::swap
,它可能会执行以下操作:
// pseudo-code:
void std::swap(A &a, A &b){
A tmp = std::move(a);
a = std::move(b);
b = std::move(tmp);
}
它将使用默认的c-tor构建“空”对象tmp
- 通常是廉价的操作。然后它有望移动3次,除非在疯狂的情况下移动衰变复制。
但是,如果我自己做交换:
void swap(A &a, A &b){
std::swap(a.a, b.a);
std::swap(a.b, b.b);
std::swap(a.c, b.c);
std::swap(a.d, b.d);
}
它肯定会使用更少的内存,但它仍然需要构造空std::string
- 4次!!!
我可以疯狂,用单std::string
制作它。
在所有情况下,它看起来都不是很大的改进。
我能想到的唯一正确的情况是,默认的c-tor是非常昂贵的。我是对的吗?
答案 0 :(得分:2)
嗯,你不能说你需要或者永远不应该自己制作swap
,这完全取决于背景。在你的例子中,它很可能是不必要的,但你的班级'逻辑可能更复杂。
假设您有一个类,其中包含指向某个数据结构的指针,以及与该结构相关的许多指标,每个指标都需要很长时间才能计算,并且需要大量空间来存储,这些指标在对数据进行一些计算时用作临时值(我知道下面的例子可能是一个糟糕的设计类,但它应该说明这个想法):
class MyClass
{
public:
void doSomeWork()
{
//uses _metricsOneValue and _metricsTwoValue as temporaries,
//calls other functions that use them as temporaries too, etc.
}
private:
//used only in doSomeWork and functions called by it.
//Value overwritten each call.
SomeBigClass _metricsOneValue;
SomeBigClass _metricsTwoValue;
<...>
SomeOtherClass * _data;
}
现在假设你需要swap()
这个类的两个实例。最直接的实现将复制包括旧指标在内的所有内容,实际上目前不需要这些指标,因为它们将在您下次调用doSomeWork()
时被覆盖。因此,在这种情况下,您可以通过将指针交换到数据结构来优化swap
。
答案 1 :(得分:2)
我希望理智的std::swap(std::string&, std::string&)
实现在没有任何临时性的情况下进行交换。它应该只能交换指针,不过我承认小字符串优化可能会在某些实现的特定工作中抛出一个扳手。
您始终可以使用std::string::swap
成员函数I'd expect even more to make use of such a delightful "optimisation"。我的意思是,否则,它有什么意义呢? :)
void swap(A &a, A &b)
{
a.a.swap(b.a);
a.b.swap(b.b);
a.c.swap(b.c);
a.d.swap(b.d);
}
你将需要实现这个 ,即使它只是你的移动构造函数的一部分,否则默认的std::swap(A&, A&)
实现不能做任何事情。
总之,是的,您应该编写自己的swap
函数,是的,您应该使用它来调用标准交换功能。因为你需要两者,所以没有绘制性能比较。
答案 2 :(得分:0)
通过使用赋值运算符来实现您自己的swap
1 函数的一个好处是管理/传输动态分配的内存复制和交换习语。
如果您有以下课程:
#include <algorithm> // std::copy
#include <cstddef> // std::size_t
class MyArray{
public:
// (default) constructor
MyArray(std::size_t size = 0)
: mSize(size), mArray(mSize ? new int[mSize]() : 0)
{ }
// copy-constructor
MyArray(const MyArray& other)
: mSize(other.mSize), mArray(mSize ? new int[mSize] : 0),
{ std::copy(other.mArray, other.mArray + mSize, mArray); }
// destructor
~MyArray(){ delete [] mArray; }
private:
std::size_t mSize;
int* mArray;
};
而不是:
MyArray& operator=(const MyArray& other){
if (this != &other){
// get the new data ready before we replace the old
std::size_t newSize = other.mSize;
int* newArray = newSize ? new int[newSize]() : 0;
std::copy(other.mArray, other.mArray + newSize, newArray);
// replace the old data
delete [] mArray;
mSize = newSize;
mArray = newArray;
}
return *this;
}
你可以这样做:
MyArray& operator=(MyArray other){
swap(*this, other);
return *this;
}
friend void swap(MyArray& first, MyArray& second){
using std::swap;
// by swapping the members of two classes,
// the two classes are effectively swapped
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
注意:此答案中的见解来自this。
1交换函数是一个非抛出函数,它交换类的两个对象,成员的成员。我们可能会试图使用std::swap
而不是提供我们自己的,但这是不可能的; std::swap
在其实现中使用了copy-constructor和copy-assignment运算符,我们最终会尝试根据自身定义赋值运算符!