14

我有一个指向动态分配数组的类,所以我创建了复制构造函数和赋值运算符函数。由于复制构造函数和赋值运算符函数做同样的工作,我从赋值运算符函数调用复制构造函数,但 get "error C2082: redefinition of formal parameter"。我正在使用 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 回答 4

22

有问题的线不是你想的那样。它实际上声明了一个other类型为 的变量FeatureValue。这是因为构造函数没有名称并且不能直接调用。

只要运算符未声明为虚拟,您就可以安全地从构造函数调用复制赋值运算符。

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;
}

这将只是花花公子,或者您可以使用Vaughn Cato 的回答中建议的复制和交换习语的典型指南

于 2013-07-05T02:50:47.237 回答
12

您不能像使用任何其他方法一样直接调用构造函数。您正在做的实际上是声明一个名为othertype的变量FeatureValue

看看copy-and-swap idiom,以了解避免赋值运算符和复制构造函数之间重复的好方法:什么是copy-and-swap idiom?

更好的是,使用 astd::vector而不是newand delete。然后你不需要编写自己的复制构造函数或赋值运算符。

于 2013-07-05T02:50:27.317 回答
5

简短的回答 - 不要这样做。

细节:

// 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;
}

笔记:

  • 当调用复制构造函数时,它会参考被复制的对象来构造新对象,但默认构造函数不会在复制构造函数之前运行。这意味着m_value当复制构造函数开始运行时具有不确定的值 - 您可以分配给它,但从中读取是未定义的行为,并且delete[]对它来说更糟(如果有任何事情可能比 UD 更糟!;-))。因此,只需省略该delete[]行。

接下来,如果operator=试图利用复制构造函数的功能,它必须首先释放任何m_value指向的现有数据,否则它将被泄露。大多数人尝试按以下方式执行此操作(已损坏)-我认为这就是您要尝试的:

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;
}

这样做的问题是,如果 FeatureValue 的创建失败(例如,因为new无法获得它想要的内存),那么FeatureValue对象就会处于无效状态(例如,m_value可能指向空间)。稍后当析构函数运行并执行 adelete[] m_value时,您有未定义的行为(您的程序可能会崩溃)。

你真的应该更系统地处理这个......要么一步一步写出来,要么实现一个有保证的不抛出swap()方法(很容易做到......只是std::swap() m_sizem_value,并使用它ala:

FeatureValue& FeatureValue::operator=(FeatureValue other)
{
    swap(other);
    return *this;
}

这既简单又干净,但它有几个小的性能/效率问题:

  • 使任何现有m_value数组保持比必要更长的时间,增加峰值内存使用量......你可以调用clear(). 在实践中,大多数重要程序不会关心这一点,除非所讨论的数据结构包含大量数据(例如,PC 应用程序为数百兆字节或千兆字节)。

  • 甚至不尝试重用现有m_value内存——而是总是做另一个newfor other(这可能会导致内存使用量减少,但并不总是值得的)。

最终,可能存在不同的复制构造函数的原因operator=- 而不是让编译器自动从另一个创建一个 - 是最佳高效的实现不能 - 通常 - 以您希望的方式相互利用。

于 2013-07-05T03:05:45.637 回答
0

该语句FeatureValue(other);实际上调用了复制构造函数来创建一个新的 Featurevalue 对象,与*this.

于 2013-07-05T03:24:39.407 回答