Our website uses cookies to enhance your browsing experience.
Accept
to the top
close form

Fill out the form in 2 simple steps below:

Your contact information:

Step 1
Congratulations! This is your promo code!

Desired license type:

Step 2
Team license
Enterprise license
** By clicking this button you agree to our Privacy Policy statement
close form
Request our prices
New License
License Renewal
--Select currency--
USD
EUR
* By clicking this button you agree to our Privacy Policy statement

close form
Free PVS‑Studio license for Microsoft MVP specialists
* By clicking this button you agree to our Privacy Policy statement

close form
To get the licence for your open-source project, please fill out this form
* By clicking this button you agree to our Privacy Policy statement

close form
I am interested to try it on the platforms:
* By clicking this button you agree to our Privacy Policy statement

close form
check circle
Message submitted.

Your message has been sent. We will email you at


If you do not see the email in your inbox, please check if it is filtered to one of the following folders:

  • Promotion
  • Updates
  • Spam

Webinar: Parsing C++ - 10.10

>
>
>
V690. The class implements a copy const…
menu mobile close menu
Analyzer diagnostics
General Analysis (C++)
General Analysis (C#)
General Analysis (Java)
Micro-Optimizations (C++)
Diagnosis of 64-bit errors (Viva64, C++)
Customer specific requests (C++)
MISRA errors
AUTOSAR errors
OWASP errors (C#)
Problems related to code analyzer
Additional information
toggle menu Contents

V690. The class implements a copy constructor/operator=, but lacks the operator=/copy constructor.

May 16 2014

The analyzer has detected a class where a copy constructor is implemented but the 'operator =' is not, or vice versa, the 'operator =' is implemented but a copy constructor is not.

Handling such classes is very dangerous. In other words, we are dealing with the violated "Law of The Big Two". We will discuss this law a bit further.

Let's examine an example of a dangerous class. It is pretty long, but the only thing we are concerned with now is that the class has the assignment operator but lacks a copy constructor.

class MyArray
{
  char *m_buf;
  size_t m_size;
  void Clear() { delete [] m_buf; }
public:
  MyArray() : m_buf(0), m_size(0) {}
  ~MyArray() { Clear(); }
  void Allocate(size_t s)
    { Clear(); m_buf = new char[s]; m_size = s; }
  void Copy(const MyArray &a)
    { Allocate(a.m_size);
      memcpy(m_buf, a.m_buf, a.m_size * sizeof(char)); }
  char &operator[](size_t i) { return m_buf[i]; }
  
  MyArray &operator =(const MyArray &a)
    { Copy(a); return *this; }
};

We are not going to discuss how practical and useful this class is; it's just an example and what we care about is that the following code fragment will work well:

{
  MyArray A; 
  A.Allocate(100);
  MyArray B;
  B = A;
}

The assignment operator is successfully copying the array.

The next code fragment will cause undefined behavior: the application will either crash or its operation will be violated otherwise.

{   
  MyArray A; 
  A.Allocate(100);
  MyArray C(A);
}

The point is that the class lacks a copy constructor. When creating the 'C' object, the pointer to the array will be simply copied, which will cause double memory freeing when destroying the objects A and C.

A similar trouble will occur when a copy constructor is present but the assignment operator is absent.

To fix the class, we need to implement a copy constructor:

MyArray &operator =(const MyArray &a)
  { Copy(a); return *this; }
MyArray(const MyArray &a) : m_buf(0), m_size(0)
  { Copy(a); }

If the analyzer generates the V690 warning, please don't be lazy to implement an absent method. Do so even if the code works well currently and you are sure you remember the class' specifics. Some time later, you will forget about the missing operator= or a copy constructor, and you or your colleagues will make a mistake which will be difficult to find. When class fields are copied automatically, it's a usual thing that such classes "almost work". Troubles reveal themselves later in absolutely different places of code.

The Law of The Big Two

As it was said in the beginning, the V690 diagnostic rule detects classes that violate "The Law of The Big Two". Let's examine this in detail. But we should start with "The rule of three" first. Here is an extract from Wikipedia:

The rule of three (also known as the Law of The Big Three or The Big Three) is a rule of thumb in C++ that claims that if a class defines one of the following it should probably explicitly define all three:

  • destructor;
  • copy constructor;
  • copy assignment operator.

These three functions are special member functions. If one of these functions is used without first being declared by the programmer it will be implicitly implemented by the compiler with the default semantics of performing the said operation on all the members of the class. The default semantics are:

  • Destructor - Call the destructors of all the object's class-type members
  • Copy constructor - Construct all the object's members from the corresponding members of the copy constructor's argument, calling the copy constructors of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members
  • Copy assignment operator - Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members.

The Rule of Three claims that if one of these had to be defined by the programmer, it means that the compiler-generated version does not fit the needs of the class in one case and it will probably not fit in the other cases either. The term "Rule of three" was coined by Marshall Cline in 1991.

An amendment to this rule is that if Resource Acquisition Is Initialization (RAII) is used for the class members, the destructor may be left undefined (also known as The Law of The Big Two).

Because implicitly-generated constructors and assignment operators simply copy all class data members, one should define explicit copy constructors and copy assignment operators for classes that encapsulate complex data structures or have external references such as pointers, since only the pointer gets copied, not the object it points to. In the case that this default behavior is actually the intended behavior, an explicit declaration can prevent ambiguity.

"The Law of The Big Two" itself is discussed in detail in the following article: The Law of The Big Two.

As you can see, "The Law of The Big Two" is very important - that's why we have implemented the corresponding diagnostic in our code analyzer.

Starting from C++11, there has been move semantics, so this rule expanded to the "Big Five". A list of methods, which all have to be defined in case if at least one of them is defined:

  • destructor;
  • copy constructor;
  • copy assignment operator;
  • move constructor;
  • move assignment operator;

Therefore, everything that is true for the constructor/copy operator, is true for the move constructor/operator.

Note

Does the V690 diagnostic always reveal genuine errors? No, it doesn't. Sometimes we deal not with an error but just a redundant function. Take a look at the following sample taken from a real application:

struct wdiff {
  int start[2];
  int end[2];
  wdiff(int s1=0, int e1=0, int s2=0, int e2=0)
  {
    if (s1>e1) e1=s1-1;
    if (s2>e2) e2=s2-1;
    start[0] = s1;
    start[1] = s2;
    end[0] = e1;
    end[1] = e2;
  }
  wdiff(const wdiff & src)
  {
    for (int i=0; i<2; ++i)
    {
      start[i] = src.start[i];
      end[i] = src.end[i];
    }
  }
};

This class has a copy constructor but lacks the assignment operator. But it's alright: the arrays 'start' and 'end' consist of simple types 'int' and will be correctly copied by the compiler. To eliminate the V690 warning here, we need to remove the meaningless copy constructor. The compiler will build the code, copying the class members in no way slower, if not even faster.

The fixed code:

struct wdiff {
  int start[2];
  int end[2];
  wdiff(int s1=0, int e1=0, int s2=0, int e2=0)
  {
    if (s1>e1) e1=s1-1;
    if (s2>e2) e2=s2-1;
    start[0] = s1;
    start[1] = s2;
    end[0] = e1;
    end[1] = e2;
  }
};

You can look at examples of errors detected by the V690 diagnostic.