这是一个需要进行实际测量的情况。
我正在查看原帖中的复制赋值运算符,并发现存在低效率问题:
A& operator=(A const& other)
{A temp = other
如果
*this
和
other
具有相同的
s
,会发生什么?
在我看来,如果
s == other.s
,更智能的复制赋值可以避免访问堆。它只需要进行复制即可:
A& operator=(A const& other)
{
if (this != &other)
{
if (s != other.s)
{
delete [] p;
p = nullptr;
s = 0;
p = new int[other.s];
s = other.s;
}
std::copy(other.p, other.p + s, this->p);
}
return *this;
}
如果您只需要在复制赋值(例如std :: string,std :: vector等)上实现基本的异常安全性而不是强异常安全性,则以上内容可能会带来潜在的性能提升。具体提升多少?请进行测试。
我以三种方式编写了这个类:
设计1:使用上述复制赋值运算符和OP的移动赋值运算符#1。
设计2:使用上述复制赋值运算符和OP的移动赋值运算符#2。
设计3:DeadMG的复制赋值运算符用于复制和移动赋值。
以下是我用于测试的代码:
#include <cstddef>
#include <algorithm>
#include <chrono>
#include <iostream>
struct A
{
std::size_t s;
int* p;
A(std::size_t s) : s(s), p(new int[s]){}
~A(){delete [] p;}
A(A const& other) : s(other.s), p(new int[other.s])
{std::copy(other.p, other.p + s, this->p);}
A(A&& other) : s(other.s), p(other.p)
{other.s = 0; other.p = nullptr;}
void swap(A& other)
{std::swap(s, other.s); std::swap(p, other.p);}
#if DESIGN != 3
A& operator=(A const& other)
{
if (this != &other)
{
if (s != other.s)
{
delete [] p;
p = nullptr;
s = 0;
p = new int[other.s];
s = other.s;
}
std::copy(other.p, other.p + s, this->p);
}
return *this;
}
#endif
#if DESIGN == 1
A& operator=(A&& other)
{
swap(other);
return *this;
}
#elif DESIGN == 2
A& operator=(A&& other)
{
delete [] p;
s = other.s;
p = other.p;
other.s = 0;
other.p = nullptr;
return *this;
}
#elif DESIGN == 3
A& operator=(A other)
{
swap(other);
return *this;
}
#endif
};
int main()
{
typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<float, std::nano> NS;
A a1(10);
A a2(10);
auto t0 = Clock::now();
a2 = a1;
auto t1 = Clock::now();
std::cout << "copy takes " << NS(t1-t0).count() << "ns\n";
t0 = Clock::now();
a2 = std::move(a1);
t1 = Clock::now();
std::cout << "move takes " << NS(t1-t0).count() << "ns\n";
}
以下是我得到的输出:
$ clang++ -std=c++11 -stdlib=libc++ -O3 -DDESIGN=1 test.cpp
$ a.out
copy takes 55ns
move takes 44ns
$ a.out
copy takes 56ns
move takes 24ns
$ a.out
copy takes 53ns
move takes 25ns
$ clang++ -std=c++11 -stdlib=libc++ -O3 -DDESIGN=2 test.cpp
$ a.out
copy takes 74ns
move takes 538ns
$ a.out
copy takes 59ns
move takes 491ns
$ a.out
copy takes 61ns
move takes 510ns
$ clang++ -std=c++11 -stdlib=libc++ -O3 -DDESIGN=3 test.cpp
$ a.out
copy takes 666ns
move takes 304ns
$ a.out
copy takes 603ns
move takes 446ns
$ a.out
copy takes 619ns
move takes 317ns
DESIGN 1
对我来说看起来不错。
注意:如果类有需要快速释放的资源,如互斥锁所有权或文件打开状态的所有权,则从正确性的角度来看,设计2的移动赋值运算符可能更好。但是,当资源只是内存时,尽可能延迟释放它通常是有优势的(如OP的用例所示)。
注意2:如果您有其他重要的用例,请测量它们。您可能会得出与我在这里的结论不同的结论。
注意:我的价值观是性能高于“DRY”。所有这里的代码都将被封装在一个类(struct A
)中。让struct A
变得尽可能好。如果您做得足够高质量,那么struct A
的客户端(可能是您自己)就不会被诱惑“RIA”(重新发明)。我更喜欢在一个类中重复一些代码,而不是一遍又一遍地重复整个类的实现。
std::unique_ptr<int>
成员(而不是int*
),并让相关运算符自动生成或=default
? - Walterstd::vector
。此外,在编写时,MSVC还没有实现“default”。 - Jesse GoodMSVC
不在标签中。 - Walter