File: unhandled-self-assignment.rst

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (124 lines) | stat: -rw-r--r-- 3,123 bytes parent folder | download | duplicates (15)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
.. title:: clang-tidy - bugprone-unhandled-self-assignment

bugprone-unhandled-self-assignment
==================================

`cert-oop54-cpp` redirects here as an alias for this check. For the CERT alias,
the `WarnOnlyIfThisHasSuspiciousField` option is set to `false`.

Finds user-defined copy assignment operators which do not protect the code
against self-assignment either by checking self-assignment explicitly or
using the copy-and-swap or the copy-and-move method.

By default, this check searches only those classes which have any pointer or C array field
to avoid false positives. In case of a pointer or a C array, it's likely that self-copy
assignment breaks the object if the copy assignment operator was not written with care.

See also:
`OOP54-CPP. Gracefully handle self-copy assignment
<https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP54-CPP.+Gracefully+handle+self-copy+assignment>`_

A copy assignment operator must prevent that self-copy assignment ruins the
object state. A typical use case is when the class has a pointer field
and the copy assignment operator first releases the pointed object and
then tries to assign it:

.. code-block:: c++

  class T {
  int* p;

  public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
      delete p;
      p = new int(*rhs.p);
      return *this;
    }
  };

There are two common C++ patterns to avoid this problem. The first is
the self-assignment check:

.. code-block:: c++

  class T {
  int* p;

  public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
      if(this == &rhs)
        return *this;

      delete p;
      p = new int(*rhs.p);
      return *this;
    }
  };

The second one is the copy-and-swap method when we create a temporary copy
(using the copy constructor) and then swap this temporary object with ``this``:

.. code-block:: c++

  class T {
  int* p;

  public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    void swap(T &rhs) {
      using std::swap;
      swap(p, rhs.p);
    }

    T& operator=(const T &rhs) {
      T(rhs).swap(*this);
      return *this;
    }
  };

There is a third pattern which is less common. Let's call it the copy-and-move method
when we create a temporary copy (using the copy constructor) and then move this
temporary object into ``this`` (needs a move assignment operator):

.. code-block:: c++

  class T {
  int* p;

  public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
      T t = rhs;
      *this = std::move(t);
      return *this;
    }

    T& operator=(T &&rhs) {
      p = rhs.p;
      rhs.p = nullptr;
      return *this;
    }
  };

.. option:: WarnOnlyIfThisHasSuspiciousField

  When `true`, the check will warn only if the container class of the copy assignment operator
  has any suspicious fields (pointer or C array). This option is set to `true` by default.