簡體   English   中英

重載=運算符和名稱隱藏

[英]Overloading = operator and Name Hiding

我在重載繼承類中的賦值運算符時遇到了一個奇怪的問題。 我的代碼包含以下相關內容

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;

逐步執行代碼,遵循所有虛擬函數,並且當重載=運算符調用Net<double>::Set方法時,實際上pval_net_real指向的值已更新為3.0 問題是當我從調用堆棧返回到*net_real = *net_real2;之后的下一*net_real = *net_real2; ,似乎以某種方式執行了net_real2的淺表副本。 具體來說,指針pval_net_real現在突然有相同的地址指針net_real2

是否以某種方式調用了重載的base =運算符,然后調用了派生類的隱式=運算符? 我沒有定義派生類的任何等式/副本構造函數(但是基類有一個顯式定義的副本構造函數)。

編輯

我在下面粘貼了一段代碼,以顯示正在發生的事情。 我剝離了很多東西,但我希望這可以顯示問題。 我只是在[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]

使用聲明將名稱引入到使用聲明出現在的聲明區域中。

[省略形式語法]

在使用聲明中出現的聲明區域中聲明使用聲明中指定的成員名稱。

using所做的一切就是有效地聲明

operator=(const base &);

以某種方式從派生類中的基類中獲取。

刪除派生的默認賦值運算符將無濟於事。 然后,編譯器將抱怨調用已刪除的運算符。

這里唯一可行的選擇是用一個包裝器替換using聲明,該包裝器從基類中顯式調用賦值運算符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM