14
votes

I have a class with a point to dynamically allocated array, so I created copy constructor and assignment operator function. Since copy constructor and assignment operator function do the same work, I call copy constructor from the assignment operator function but get "error C2082: redefinition of formal parameter". I am using Visual Studio 2012.

// default constructor
FeatureValue::FeatureValue()
{
    m_value = NULL;
}

// copy constructor 
FeatureValue::FeatureValue(const FeatureValue& other)
{
    m_size = other.m_size;  
    delete[] m_value;
    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;
}
4

4 Answers

22
votes

The offending line isn't what you think it is. It actually declares a variable other of type FeatureValue. This is because constructors to not have names and cannot be called directly.

You can safely invoke the copy assignment operator from the constructor as long as the operator is not declared virtual.

FeatureValue::FeatureValue(const FeatureValue& other)
    : m_value(nullptr), m_size(0)
{
    *this = other;
}

// assignment operator function
FeatureValue& FeatureValue::operator=(const FeatureValue& other)
{
    if(this != &other)
    {
        // copy data first. Use std::unique_ptr if possible
        // avoids destroying our data if an exception occurs
        uint8_t* value = new uint8_t[other.m_size];
        int size = other.m_size;  

        for (int i = 0; i < other.m_size; i++)
        {
            value[i] = other.m_value[i];
        }

        // Assign values
        delete[] m_value;
        m_value = value;
        m_size = size;
    }
    return *this;
}

This will works just dandy or you can use the typical guidelines for the copy & swap idiom suggested in Vaughn Cato's answer

12
votes

You can't directly call a constructor like you would any other method. What you are doing is actually declaring a variable called other of type FeatureValue.

Take a look at the copy-and-swap idiom for a good way to avoid duplication between the assignment operator and the copy constructor: What is the copy-and-swap idiom?

Even better, use a std::vector instead of new and delete. Then you don't need to write your own copy constructor or assignment operator.

5
votes

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.

0
votes

The statement FeatureValue(other); actually invokes the copy constructor to create a new Featurevalue object,which has nothing to do with *this.