c++ - C++ 中简单多项式类的标量乘法重载的值不正确
问题描述
简化:我正在尝试编写一个简单的Polynomial
类,当我尝试重载乘法运算符时得到不正确的值:
#include <iostream>
class Polynomial {
public:
unsigned int degree;
int *coefficients;
Polynomial(unsigned int deg, int* arr) {
degree = deg;
coefficients = arr;
}
~Polynomial() {}
int& operator[](int index) const {
return coefficients[index];
}
};
std::ostream& operator<<(std::ostream &os, const Polynomial& P){
for (unsigned int i=0; i < P.degree; i++) os << P[i] << ",";
os << P.coefficients[P.degree];
return os;
}
Polynomial operator*(const Polynomial &P, const int &x) {
int arr[P.degree];
for (unsigned int i=0; i <= P.degree; i++) arr[i] = P[i];
Polynomial p(P.degree, arr);
std::cout << p << std:: endl; // just for debugging
return p;
}
我正在测试我的代码main
:
int g[] = {-1, 0, 1, 1, 0, 1, 0, 0, -1, 0, -1};
Polynomial P_g = Polynomial(10, g);
std::cout << P_g << std::endl;
Polynomial P_g_3 = P_g*3;
std::cout << P_g_3[0] << std::endl;
std::cout << P_g_3[1] << std::endl;
std::cout << P_g_3[2] << std::endl;
std::cout << P_g_3[3] << std::endl;
std::cout << P_g_3[4] << std::endl;
std::cout << P_g_3[5] << std::endl;
std::cout << P_g_3[6] << std::endl;
std::cout << P_g_3[7] << std::endl;
std::cout << P_g_3[8] << std::endl;
std::cout << P_g_3[9] << std::endl;
std::cout << P_g_3[10] << std::endl;
std::cout << P_g_3 << std::endl;
但是控制台中的输出与我所期望的完全不同:
-1,0,1,1,0,1,0,0,-1,0,-1
-1,0,1,1,0,1,0,0,-1,0,-1
-1
0
0
0
0
0
-2002329776
32767
-516177072
32766
539561192
0,32766,539581697,32767,-2002329776,32767,1,0,243560063,1,-2002329776
尽管请注意cout
重载运算符中的内部语句返回正确的多项式。只有当程序退出该函数时,系数才会被搞砸……而且这两种打印策略甚至与它们本身不一致。到底是怎么回事?
解决方案
分析
您的Polynomial
班级很不寻常,因为它不拥有自己的数据。虽然这本身并不是错误的,但它几乎总是一种不明智的做法。
作为说明,请查看 main 函数中的变量。多项式的数据P_g
存储在数组中g
。该变量g
除了存储该数据之外没有其他用途,因此它的名称是混乱的。这里还有一个一致性问题,因为如果有人改变 的元素g
,那么P_g
也会改变。更糟糕的是,如果g
在它还在的时候不存在P_g
,你将拥有一个无法访问其数据的多项式!
好在局部变量是按照创建的相反顺序销毁的,所以P_g
会在之前销毁g
。但是,当您调用operator*
. 在该运算符中,多项式p
将其数据存储在局部变量arr
中。到目前为止,情况与中相同main()
。直到你回来p
。此时,p
被复制/移动到调用范围,并且该副本使用与原始数据相同的数据。数组arr
被销毁,但返回的对象仍试图访问arr
其数据。返回的对象P_g_3
有一个悬空指针。
当您尝试访问 的数据时P_g_3
,会发生未定义的行为。在某些情况下,您可能会看到预期的行为。在这种情况下,产生了垃圾值。从一个角度来看,您的结果是更理想的结果,因为您能够检测到问题的存在,这使您可以尝试修复它。更阴险的是未定义的行为,它在您运行程序时按您预期的方式执行,但在其他人执行时却没有。
解决方案
通常的方法是让对象拥有自己的数据。通常,这是独占所有权,因此数据在不通过对象的情况下无法更改。
第一个改进是将数据保存数组移动到对象中。主要障碍是您事先不知道数组有多大。这需要动态内存分配。您可以在不更改类的数据成员的情况下开始这条路;第一步可能是初始化分配给它coefficients
的内存new
而不是分配它arr
。这导致需要遵循三法则。不幸的是,这需要一些工作才能正确完成,特别是因为您选择跟踪多项式的次数而不是数组的大小。
幸运的是,存储未知长度的数据是一个常见的问题,标准库提供了自动化大多数细节的工具。一种这样的工具是std::vector
. 如果您将coefficients
from的类型更改int*
为std::vector<int>
,那么Polynomial
对象将能够拥有自己的数据,加上三规则变成零规则。(也就是说,编译器生成的析构函数和复制方法就足够了。)您operator*
可以简单地制作传入的副本Polynomial
,然后遍历副本的向量以进行更改。
作为一个额外的好处,您不再需要degree
手动跟踪,因为非零多项式的次数将为coefficients.size() - 1
. (好吧,如果前导系数为零,则会出现复杂情况,但这对于您的原始实现来说也是一个未处理的问题。)这说明了一个类经常保留其数据成员的原因private
。如果您已将数据成员设为私有,而是定义了一个degree()
方法,则可以更改确定度数的方式,而无需修改任何使用Polynomial
.
注意:
如果您对向量使用基于范围的for
循环,则示例代码无需查看多项式的次数。您将为类外部代码提供成员函数。
推荐阅读
- excel - VBA 格式化以基于复选框突出显示文本
- simulation - AFU 模拟环境(ASE 环境)设置问题;无法将 / 复制到 /target-directory Traceback(最近的调用):
- numpy - torch.where() 可以以等效的广播形式使用吗?
- php - 无法使用 php 或 nodejs 读取串口
- python - Assign class label based on parent folder name when using Tf.data
- reactjs - 使用 Flask Socket Io 和 Reactjs 进行连续数据传输
- file - (Godot Engine) Creating folders on network drives, UNC paths
- vue.js - 是否可以集成一个可点击的单元格来验证 v-data-table?
- python - How to track progress with nlp.pipe function of spacy?
- rust - 从 RefCell 获取引用