Short answer - don't do it.
Details:
// copy constructor
FeatureValue::FeatureValue(const FeatureValue& other)
{
m_size = other.m_size;
delete[] m_value; // m_value NOT INITIALISED - DON'T DELETE HERE!
m_value = new uint8_t[m_size];
for (int i = 0; i < m_size; i++)
{
m_value[i] = other.m_value[i];
}
}
// assignment operator function
FeatureValue& FeatureValue::operator=(const FeatureValue& other)
{
FeatureValue(other); // error C2082: redefinition of formal parameter
return *this;
}
Notes:
- When the copy constructor is called, it's constructing the new object with reference to the object being copied, but the default constructor does not run before the copy constructor. This means
m_value
has an indeterminate value when the copy constructor starts running - you can assign to it, but to read from it is undefined behaviour, and to delete[]
it considerably worse (if anything can be worse than UD! ;-)). So, just leave out that delete[]
line.
Next, if operator=
tries to leverage the functionality from the copy constructor, it has to first release any existing data m_value
is pointing at or it will be leaked. Most people try to do that as follows (which is broken) - I think this is what you were trying for:
FeatureValue& FeatureValue::operator=(const FeatureValue& other)
{
// WARNING - this code's not exception safe...!
~FeatureValue(); // call own destructor
new (this) FeatureValue(other); // reconstruct object
return *this;
}
The problem with this is that if the creation of FeatureValue fails (e.g. because new
can't get the memory it wants), then the FeatureValue
object is left with an invalid state (e.g. m_value
might be pointing off into space). Later when the destructor runs and does a delete[] m_value
, you have undefined behaviour (your program will probably crash).
You really should approach this more systematically... either writing it out step by step, or perhaps implementing a guaranteed non-throwing swap()
method (easy to do... just std::swap()
m_size
and m_value
, and using it ala:
FeatureValue& FeatureValue::operator=(FeatureValue other)
{
swap(other);
return *this;
}
That's easy and clean, but it has a couple minor performance/efficiency issues:
keeping any existing m_value
array around longer than necessary, increasing peak memory usage... you could call clear()
. In practice, most non-trivial programs wouldn't care about this unless the data structure in question was holding huge amounts of data (e.g. hundreds of megabytes or gigabytes for a PC app).
not even trying to reuse the existing m_value
memory - instead always doing another new
for other
(that can lead to reduced memory usage but isn't always worthwhile).
Ultimately, the reasons there can be distinct copy constructor and operator=
- rather than having the compiler automatically create one from the other - is that optimally efficient implementations can't - in general - leverage each other in the way you'd hoped.