c++ - 重载 = 运算符和名称隐藏

标签 c++ c++14 copy-constructor

我在继承类中重载赋值运算符时遇到了一个奇怪的问题。我的代码有以下相关部分

class BaseSignal
{
  ...
  virtual void Get(double* val) const {};
  virtual void Set(double val) {};
  BaseSignal& operator=(const BaseSignal& other);
  BaseSignal(const BaseSignal& orig);
  ...
}

BaseSignal& BaseSignal::operator=(const BaseSignal& other)
{
  double dval;
  other.Get(&dval);
  this->Set(dval);
}


template <class T>
class Net : public BaseSignal
{
  ...
  using BaseSignal::operator=;
  T* pval_;
  ...
  void Set(T val)
  {
    *pval_ = val;
  }
}

我认为派生类是模板这一事实在这里并不重要。

我遇到的问题是当我按以下顺序评估最终相等性时:

Net<double>* net_real = new Net<double>(...);
*net_real = 1.0;
Net<double>* net_real2 = new Net<double>(...);
*net_real2 = 3.0;
*net_real = *net_real2;

单步执行代码,后面跟着所有虚函数,确实是pval_指向的值的 net_real更新为 3.0Net<double>::Set方法由重载 = 调用运算符(operator)。问题是当我从调用堆栈返回到 *net_real = *net_real2; 之后的下一行时, 它看起来像是 net_real2 的浅表拷贝进行了。具体来说,指针pval_net_real现在突然与 net_real2 中的指针具有相同的地址.

以某种方式重载了基础 =运算符调用,然后隐式 =派生类的运算符被调用?我没有定义派生类的任何相等/复制构造函数(但基类有一个显式定义的复制构造函数)。

编辑

我在下面粘贴了一段代码,显示了正在发生的事情。我剥离了很多东西,但我希望这能说明问题所在。我刚刚在 [cpp.sh] 中运行了这个.

// Example program
#include <iostream>
#include <string>

  class BaseSignal
  {
    public:
    /**
     * Constructor and destructor methods
     */
    // Nullor
    BaseSignal() {};
    // Destructor
    virtual ~BaseSignal() {};
    // Copy
    BaseSignal(const BaseSignal& orig);

    // Virtual settors
    virtual void Set(double val) {};

    // Virtual gettors
    virtual void Get(double* val) const {};

    // Equality to another signal
    BaseSignal& operator=(const BaseSignal& other);
    BaseSignal& operator=(const double& rhs);
  }; // class BaseSignal

  BaseSignal& BaseSignal::operator=(const double& rhs)
  {
    this->Set(rhs);
    return *this;
  }

  // Equality to another signal. Check vector widths are equal
  BaseSignal& BaseSignal::operator=(const BaseSignal& other)
  {
    std::cout << "******BaseSignal::operator= was called!******\n";
    double dval;
    other.Get(&dval);
    this->Set(dval);
    return *this;
  }

template <class T>
  class Net : public BaseSignal
  {
      public:
    T* pval_;
    /**
     * Constructors/destructors
     */
    Net() : BaseSignal()
    {
      // Initialize value
      pval_ = new T[1]{0};
    }
    ~Net() {delete[] pval_;}
    /**
     * Operator Overloads
     */
    using BaseSignal::operator=;

    /**
     * Setting with a constant value input just writes to the value at pval_.
     * If the Net is a 2-D vector, this method writes all values in the vector
     * to be the same val.
     */
    void Set(T val)
    {
        pval_[0] = val;
    } // Net<T>::Set

    /**
     * The Get method returns the number of elements based on the col_
     * parameter.
     */
    void Get(T* val) const
    {
        val[0] = pval_[0];
    } // Net<T>::Get
};

int main()
{
    double read_val;
    Net<double>* net_real = new Net<double>();
    *net_real = 1.0;
    net_real->Get(&read_val);
    std::cout << "net_real is equal to: " << read_val << "\n";
    std::cout << "net_real has pval_ pointer: " << net_real->pval_ << "\n";
    Net<double>* net_real2 = new Net<double>();
    *net_real2 = 2.0;
    net_real2->Get(&read_val);
    std::cout << "net_real2 is equal to: " << read_val << "\n";
    std::cout << "net_real2 has pval_ pointer: " << net_real2->pval_ << "\n";
    std::cout << "Now assigning value of net_real2 to net_real\n";
    *net_real = *net_real2;
    net_real->Get(&read_val);
    std::cout << "net_real is now equal to: " << read_val << "\n";
    std::cout << "net_real now has pval_ pointer: " << net_real->pval_ << "\n";
}

最佳答案

这里发生的事情是

 using BaseSignal::operator=;

并不像您认为的那样工作。这里模板的使用有点混淆了这个问题,所以我将使用一个更简单的例子,有两个普通类。

class base {

     // Class declaration
};

class derived: public base {

     //

     using base::operator=;
};

这两个类的默认赋值运算符是:

 base &base::operator=(const base &);

 derived &derived::operator=(const derived &);

这就是默认赋值运算符的作用。

using 声明的作用是有效地将基类的 operator= 带入派生类,就好像还声明了以下运算符一样:

 derived &derived::operator=(const base &);

但是,当然,derived 的默认operator=(const derived &) 仍然存在,没有任何变化。因此,无论如何,您的代码最终会调用派生类中的默认赋值运算符。

C++ 标准规定 using 声明如下:

[namespace.udecl]

A using-declaration introduces a name into the declarative region in which the using-declaration appears.

[formal grammar omitted]

The member name specified in a using-declaration is declared in the declarative region in which the using-declaration appears.

using 所做的就是有效地声明

operator=(const base &);

从派生类中的基类,以某种方式来说。

删除派生的默认赋值运算符无济于事。然后编译器将提示调用已删除的运算符。

这里唯一现实的选择是将 using 声明替换为从基类显式调用赋值运算符的包装器。

关于c++ - 重载 = 运算符和名称隐藏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57563912/

相关文章:

c++ - 在 C++ 中通过套接字发送和接收数组内容

c++ - 我需要 2 个套接字来进行多播发送和单播读取吗?

c++ - 如何在连续内存中多态地存储和访问来自同一继承层次结构的不同类型?

c++ - decltype(auto) 和 decltype(returning expr) 作为返回类型有什么区别?

c++ - 转换构造函数调用 move 但不调用 copy

c++ - 错误 : expected primary-expression before 'float'

C++ 运算符 < 重载

c++ - 我可以让我的 Makefile 自动让 GCC 使用它支持的最新标准吗?

c++ - move 语义和复制构造函数

c++ - POD 类型的填充字节是否被复制?