模板多项式类的重载运算符 []
Overloading operator[] for a template Polynom class
我正在Polynom<T>
类编写一个模板,其中T
是其系数的数字类型。
多项式的系数存储在一个std::vector<T> coefficients
中,其中coefficients[i]
对应于实多项式中的x^i
。(所以 X 的幂是按递增顺序排列的(。
保证coefficients
向量始终包含至少一个元素。 - 对于零多项式,它是 T()
.
我想重载operator[]
执行以下操作:
- 传递给运算符 [] 的索引对应于我们要修改/读取其系数的 X 的幂。
- 如果用户只想读取系数,它应该抛出负索引,返回
coefficients.at(i)
存储范围内的索引 - 并合理地为所有其他索引返回 0,而不是抛出。 - 如果用户想要修改系数,它应该抛出负索引,但允许用户自由修改所有其他索引,即使指定的索引大于或等于
coefficients.size()
。所以我们想以某种方式调整向量的大小。
我遇到的主要问题如下:
1.
如何区分读写和写写?有一个人没有解释就离开了我,但说写了两个版本:
const T& operator[] (int index) const;
T& operator[] (int index);
是不够的。但是,我认为编译器会更喜欢读取情况下的 const 版本,不是吗?
阿拉伯数字。
我想确保coefficients
向量中没有尾随零。所以我必须以某种方式提前知道,"之前"我返回系数的可变T&
,用户想要分配什么值。我知道operator[]
不会收到第二个论点。
显然,如果这个值不为零(不是 T(((,那么我必须调整向量的大小,并将适当的系数设置为传递的值。
但是我不能提前这样做(在从operator[]
返回T&
之前(,因为如果要分配的值是T((,那么,如果我提前调整系数向量的大小,它最终会有很多尾随的"零"。
当然,我可以检查类的所有其他函数中的尾随零,并在这种情况下删除它们。对我来说似乎是一个非常奇怪的决定,我希望每个函数都开始工作,假设如果向量的大小> 1,则向量末尾没有零。
你能告诉我尽可能具体的解决方案吗?我听说过一些关于编写一个隐式转换为带有重载operator=
T&
的内部类,但我缺乏细节。
提前非常感谢!
尝试的一个选项(我还没有测试过(:
template<typename T>
class MyRef{
private:
int index;
Polynom<T>*p;
public:
MyRef(int index, Polynom<T>*p) : index(index), p(p) { }
MyRef<T>& operator=(T const&t); //and define these appropriately
T operator T() const;
};
并定义:
MyRef<T> operator[](int index){
return MyRef<T>(index, this);
}
这样,当您为"引用"赋值时,它应该可以访问多项式中所有需要的数据,并采取适当的操作。
我对你的实现不够熟悉,所以我将给出一个非常简单的动态数组的例子,其工作原理如下:
- 您可以毫无顾虑地从任何
int index
读取; 以前未写入的元素应读出0
; - 当您写入超过当前分配数组末尾的元素时,该元素将被重新分配,新分配的元素将初始化为
0
。
#include <cstdlib>
#include <iostream>
using namespace std;
template<typename T>
class my_array{
private:
T* _data;
int _size;
class my_ref{
private:
int index;
T*& obj;
int&size;
public:
my_ref(T*& obj, int&size, int index)
: index(index), obj(obj), size(size){}
my_ref& operator=(T const& t){
if (index>=size){
obj = (T*)realloc(obj, sizeof(T)*(index+1) );
while (size<=index)
obj[size++]=0;
}
obj[index] = t;
return *this;
}
//edit:this one should allow writing, say, v[1]=v[2]=v[3]=4;
my_ref& operator=(const my_ref&r){
operator=( (T) r);
return *this;
}
operator T() const{
return (index>=size)?0:obj[index];
}
};
public:
my_array() : _data(NULL), _size(0) {}
my_ref operator[](int index){
return my_ref(_data,_size,index);
}
int size() const{ return _size; }
};
int main(){
my_array<int> v;
v[0] = 42;
v[1] = 51;
v[5] = 5; v[5]=6;
v[30] = 18;
v[2] = v[1]+v[5];
v[4] = v[8]+v[1048576]+v[5]+1000;
cout << "allocated elements: " << v.size() << endl;
for (int i=0;i<31;i++)
cout << v[i] << " " << endl;
return 0;
}
这是一个非常简单的例子,以目前的形式不是很有效,但它应该证明这一点。
最终,您可能希望重载operator&
以允许*(&v[0] + 5) = 42;
之类的功能正常工作。对于此示例,您可以让该operator&
给出一个my_pointer
,该定义operator+
在其index
字段上进行算术运算并返回新my_pointer
。最后,您可以重载operator*()
以返回到my_ref
。
解决方案是代理类(未经测试的代码如下(:
template<typename T> class Polynom
{
public:
class IndexProxy;
friend class IndexProxy;
IndexProxy operator[](int);
T operator[](int) const;
// ...
private:
std::vector<T> coefficients;
};
template<typename T> class Polynom<T>::IndexProxy
{
public:
friend class Polynom<T>;
// contrary to convention this assignment does not return an lvalue,
// in order to be able to avoid extending the vector on assignment of 0.0
T operator=(T const& t)
{
if (theIndex >= thePolynom.coefficients.size())
thePolynom.coefficients.resize(theIndex+1);
thePolynom.coefficients[theIndex] = t;
// the assignment might have made the polynom shorter
// by assigning 0 to the top-most coefficient
while (thePolynom.coefficients.back() == T())
thePolynom.coefficients.pop_back();
return t;
}
operator T() const
{
if (theIndex >= thePolynom.coefficients.size())
return 0;
return thePolynom.coefficients[theIndex];
}
private:
IndexProxy(Polynom<T>& p, int i): thePolynom(p), theIndex(i) {}
Polynom<T>& thePolynom;
int theIndex;
}
template<typename T>
Polynom<T>::IndexProxy operator[](int i)
{
if (i < 0) throw whatever;
return IndexProxy(*this, i);
}
template<typename T>
T operator[](int i)
{
if (i<0) throw whatever;
if (i >= coefficients.size()) return T();
return coefficients[i];
}
显然上面的代码没有优化(尤其是赋值运算符显然有优化的空间(。
您无法区分使用运算符重载的读取和写入。 您能做的最好的事情是区分const
设置和非const
设置中的用法,这是您的代码片段所做的。 所以:
Polynomial &poly = ...;
poly[i] = 10; // Calls non-const version
int x = poly[i]; // Calls non-const version
const Polynomial &poly = ...;
poly[i] = 10; // Compiler error!
int x = poly[i] // Calls const version
因此,听起来您的两个问题的答案都是具有单独的set
和get
功能。
我看到您的问题的两种解决方案:
-
与其将系数存储在
std::vector<T>
不如将它们存储在std::map<unsigned int, T>
中。这样,您将永远只存储非零系数。您可以创建自己的基于std::map
的容器,该容器将使用存储在其中的零。这样,您还可以为具有大 n 的 x^n 形式的多项式节省一些存储空间。 -
添加一个将存储索引(幂(和系数值的内部类。您将从
operator[]
返回对此内部类的实例的引用。内部类将覆盖operator=
。在被覆盖的operator=
中,您将获取存储在内部类实例中的索引(幂(和系数,并将它们刷新到存储系数的std::vector
。
是不可能的。我能想到的唯一方法是提供一个特殊的成员函数来添加新系数。
编译器通过查看Polynom
的类型来决定const
版本和非const
版本,而不是通过检查对返回值执行的操作类型。
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- <T> 通过模板化运算符重载将 std::complex 乘以双倍
- C++20概念:需要运算符重载
- 使用赋值运算符重载从类中返回jobject
- 在运算符重载定义中使用成员函数(const错误)
- 字节到位运算符重载C++
- 为什么在运算符重载时需要参考?
- 类中 c++ 的运算符 + 重载
- 算术复合运算符重载为非成员
- 运算符重载 (+),用于添加两个具有 C++ 的数组
- 交换运算符 + 重载会导致无限递归
- 如何理解新的运算符重载?
- 向量保持复数的运算符重载
- 如何创建运算符重载?
- 链接列表运算符重载没有打印出我想要的内容
- C++:需要帮助了解运算符重载错误
- 使用模板化运算符重载 XOR 运算符失败
- 如何确保接受的C++模板类型使运算符重载?
- 运算符重载使用运算符+添加类模板