我在继承类中重载赋值运算符时遇到了一个奇怪的问题。我的代码有以下相关部分
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.0
当Net<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/