重载 = 运算符和名称隐藏
Overloading = operator and Name Hiding
我在继承 class 中重载赋值运算符时遇到了一个 st运行ge 问题。我的代码有以下相关部分
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;
}
}
我认为派生的 class 是模板这一事实在这里并不重要。
我遇到的问题是当我按以下顺序计算最终相等性时:
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_real
的 pval_
指向的值在 Net<double>::Set
时更新为 3.0
方法由重载的 =
运算符调用。问题是,当我 return 从调用堆栈到 *net_real = *net_real2;
之后的下一行时,它看起来像是执行了 net_real2
的浅拷贝。具体来说,net_real
中的指针 pval_
现在突然与 net_real2
中的指针具有相同的地址。
是否以某种方式调用了重载基 =
运算符,然后调用了派生 class 的隐式 =
运算符?我没有定义派生 class 的任何 equality/copy 构造函数(但是有一个显式定义的基 class 的复制构造函数)。
编辑
我在下面粘贴了一段代码,它显示了正在发生的事情。我剥离了很多东西,但我希望这能说明问题所在。我只是 运行 这个在 [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=;
并不像您认为的那样工作。这里模板的使用有点混淆了这个问题,所以我将使用一个更简单的例子,有两个普通的 classes.
class base {
// Class declaration
};
class derived: public base {
//
using base::operator=;
};
这两个 classes 的默认赋值运算符是:
base &base::operator=(const base &);
derived &derived::operator=(const derived &);
这就是默认赋值运算符的作用。
using
声明的作用是有效地将基 class 的 operator=
带入派生的 class,就好像还声明了以下运算符一样:
derived &derived::operator=(const base &);
但是,当然,derived
的默认 operator=(const derived &)
仍然存在,没有任何变化。因此,无论如何,您的代码最终会在派生 class 中调用默认赋值运算符。
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 &);
从基础class派生class,从某种意义上说。
删除派生的默认赋值运算符无济于事。编译器随后会抱怨调用已删除的运算符。
这里唯一现实的选择是用包装器替换 using
声明,该包装器显式调用基 class.
的赋值运算符
我在继承 class 中重载赋值运算符时遇到了一个 st运行ge 问题。我的代码有以下相关部分
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;
}
}
我认为派生的 class 是模板这一事实在这里并不重要。
我遇到的问题是当我按以下顺序计算最终相等性时:
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_real
的 pval_
指向的值在 Net<double>::Set
时更新为 3.0
方法由重载的 =
运算符调用。问题是,当我 return 从调用堆栈到 *net_real = *net_real2;
之后的下一行时,它看起来像是执行了 net_real2
的浅拷贝。具体来说,net_real
中的指针 pval_
现在突然与 net_real2
中的指针具有相同的地址。
是否以某种方式调用了重载基 =
运算符,然后调用了派生 class 的隐式 =
运算符?我没有定义派生 class 的任何 equality/copy 构造函数(但是有一个显式定义的基 class 的复制构造函数)。
编辑
我在下面粘贴了一段代码,它显示了正在发生的事情。我剥离了很多东西,但我希望这能说明问题所在。我只是 运行 这个在 [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=;
并不像您认为的那样工作。这里模板的使用有点混淆了这个问题,所以我将使用一个更简单的例子,有两个普通的 classes.
class base {
// Class declaration
};
class derived: public base {
//
using base::operator=;
};
这两个 classes 的默认赋值运算符是:
base &base::operator=(const base &);
derived &derived::operator=(const derived &);
这就是默认赋值运算符的作用。
using
声明的作用是有效地将基 class 的 operator=
带入派生的 class,就好像还声明了以下运算符一样:
derived &derived::operator=(const base &);
但是,当然,derived
的默认 operator=(const derived &)
仍然存在,没有任何变化。因此,无论如何,您的代码最终会在派生 class 中调用默认赋值运算符。
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 &);
从基础class派生class,从某种意义上说。
删除派生的默认赋值运算符无济于事。编译器随后会抱怨调用已删除的运算符。
这里唯一现实的选择是用包装器替换 using
声明,该包装器显式调用基 class.