QPen: add move constructor

After this change, the relation between copy/move constructor calls in QtGui is
something like 40/16.

A moved-from QPen can only be copied, assigned-to or else destroyed. This required
adding a nullptr check to the copy ctor and the dtor and rewriting copy assignment
(which used non-nullptr-safe qAtomicAssign) in terms of copy construction and
swapping.

Extensive tests included. They are implemented such that they work in C++98 as well
as C++11 mode, but they naturally test move semantics only in a C++11 build.

Change-Id: If68f37c10b8eeefb2478cbae386cd2e38b4f6e19
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Marc Mutz 2014-02-25 23:07:46 +01:00 committed by The Qt Project
parent 98d7d4c1e5
commit db8167c224
3 changed files with 70 additions and 3 deletions

View File

@ -327,17 +327,29 @@ QPen::QPen(const QBrush &brush, qreal width, Qt::PenStyle s, Qt::PenCapStyle c,
QPen::QPen(const QPen &p)
{
d = p.d;
d->ref.ref();
if (d)
d->ref.ref();
}
/*!
\fn QPen::QPen(QPen &&pen)
\since 5.4
Constructs a pen that is moved from the given \a pen.
The moved-from pen can only be assigned to, copied, or
destroyed. Any other operation (prior to assignment) leads to
undefined behavior.
*/
/*!
Destroys the pen.
*/
QPen::~QPen()
{
if (!d->ref.deref())
if (d && !d->ref.deref())
delete d;
}
@ -373,7 +385,7 @@ void QPen::detach()
QPen &QPen::operator=(const QPen &p)
{
qAtomicAssign(d, p.d);
QPen(p).swap(*this);
return *this;
}

View File

@ -72,6 +72,8 @@ public:
QPen &operator=(const QPen &pen);
#ifdef Q_COMPILER_RVALUE_REFS
inline QPen(QPen &&other)
: d(other.d) { other.d = 0; }
inline QPen &operator=(QPen &&other)
{ qSwap(d, other.d); return *this; }
#endif

View File

@ -57,6 +57,8 @@ public:
private slots:
void getSetCheck();
void swap();
void move();
void move_assign();
void operator_eq_eq();
void operator_eq_eq_data();
@ -101,6 +103,57 @@ void tst_QPen::swap()
QCOMPARE(p2.color(), QColor(Qt::black));
}
void tst_QPen::move()
{
QPen p1(Qt::black);
// check that moving does the right thing:
QPen p2 = qMove(p1); // could be move or copy construction, so don't check p1's state
QCOMPARE(p2.color(), QColor(Qt::black));
// this, executed ehre, would crash:
// QVERIFY(p1.style() != Qt::NoPen);
// check that moved-from QPen p1 can still be safely copied:
const QPen p3 = p1;
// check that moved-from QPen p1 can still be safely assigned to:
const QPen p4(Qt::yellow);
p1 = p4;
QCOMPARE(p1.color(), QColor(Qt::yellow));
// check that moved-from QPens p2, p3 can still be safely destroyed:
QPen p5 = qMove(p2);
// intentionally no more statements beyond this point
}
void tst_QPen::move_assign()
{
QPen p1(Qt::black), p2(Qt::white);
// check that moving does the right thing:
p2 = qMove(p1); // could be move or copy assignment, so don't check p1's state
QCOMPARE(p2.color(), QColor(Qt::black));
// check that move-assigned-from QPen p1 can still be used, albeit
// with undocumented state (it's p2's original state):
QVERIFY(p1.style() != Qt::NoPen);
// check that moved-from QPen p1 can still be safely copied:
const QPen p3 = p1;
// check that moved-from QPen p1 can still be safely assigned to:
const QPen p4(Qt::yellow);
p1 = p4;
QCOMPARE(p1.color(), QColor(Qt::yellow));
// check that moved-from QPens p2, p3 can still be safely destroyed:
QPen p5;
p5 = qMove(p2);
// intentionally no more statements beyond this point
}
tst_QPen::tst_QPen()