可变长度数组:如何在C++中创建具有可变大小的缓冲区
Variable Length Arrays: How to create a buffer with variable size in C++
我目前正在编写一个移动平均类。
目标是能够在创建类 Running_Average 的新对象时将缓冲区大小指定为构造函数的一部分。
#include <iostream>
#include "Complex.h"
#include <cmath>
#include<stdio.h>
#include<stdlib.h>
#include <windows.h>
using namespace std;
class Running_Average
{
public:
double sum = 0;
double average = 0;
int i;
double Average(void); // Member functions declaration
void AddSample(double);
Running_Average(int);
};
Running_Average::Running_Average(int size) {
int buffersize = size;
double buffer[buffersize] = { 0 };
}
void Running_Average::AddSample(double val) //Add new values to buffer
{
for (i = 9; i>0; i--)
{
buffer[i] = buffer[i-1];
}
buffer[0] = val;
}
double Running_Average::Average(void) //Calculate Average of current values in buffer
{
for (i = 0; i<buffersize; i++)
{
cout << buffer[i] << endl;
sum += buffer[i];
}
average = sum / buffersize;
sum = 0;
return average;
}
int main()
{
double value;
int i;
int f = 0;
Running_Average test;
for (i = (40); i < (50); i++)
{
test.AddSample(i);
}
while (1)
{
i = rand() % 100;
test.AddSample(i);
value = test.Average();
cout << endl;
cout << value << endl;
cout << endl;
Sleep(1000);
}
}
但是,构造函数让我感到悲伤:
Running_Average::Running_Average(int size) {
int buffersize = size;
double buffer[buffersize] = { 0 };
}
具体说来:
buffer[buffersize]
在Visual Studio中抛出一个错误,指出:
表达式必须具有常量大小。
我希望用户通过在创建新对象时指定他们想要使用的缓冲区大小,方法是将其值传递给constructor
。
如何在不引发错误的情况下完成这项工作?
感谢您的帮助!
编辑:解决了!谢谢大家的帮助!我设法通过使用 std::vector 定义一个可变大小的数组来让函数工作。
有很多方法可以做到这一点。从最好到最坏想到的是:
1使用std::vector
int buffersize = size;
std::vector<double> buffer(buffersize);
2内置唯一指针或共享指针(取决于使用情况(
int buffersize = size;
auto buffer = make_unique<double[]>(buffersize) // C++14
int buffersize = size;
auto buffer = make_shared<double[]>(buffersize) // C++14
3手动分配
int buffersize = size;
double *buffer = new double[buffersize];
// delete [] buffer, must be called later
4在堆栈上分配(不建议,取决于平台(
int buffersize = size;
double *buffer = alloca(buffersize * sizeof(*buffer));
请注意,在所有这些情况下,您可以像数组一样索引缓冲区。
标准C++没有可变长度数组。(为什么?数组的大小必须是常量表达式。某些编译器具有允许 VLA 的非标准扩展,但不应依赖它们。当您需要一个可以具有可变长度并且可以调整大小的数组时,请使用std::vector
。
可变长度数组在 C 中有效,但在C++中无效。C++最好使用vector
集合,因为这可以让您更好地表示意图,即不同的数组大小,而不必单独维护当前大小。
以下完整程序提供了要使用的基线,包括测试工具代码:
#include <iostream>
#include <vector>
class RunningValues {
public:
RunningValues(size_t size = 50);
void Add(double val);
double Sum();
double Average();
private:
std::vector<double> dataBuffer;
size_t sizeLimit;
double sum;
};
// Constructor: store limit and zero sum (vector is already empty).
RunningValues::RunningValues(size_t size): sizeLimit(size), sum(0.0) {}
// Add a sample.
void RunningValues::Add(double val) {
// Zero size, disallow adds.
if (sizeLimit == 0) return;
// If we would exceed limit, remove earliest.
if (dataBuffer.size() == sizeLimit) {
sum -= dataBuffer[0];
dataBuffer.erase(dataBuffer.begin());
}
// Add value to end.
sum += val;
dataBuffer.push_back(val);
}
// Get the average (zero if nothing yet added) or sum.
double RunningValues::Average() {
if (dataBuffer.size() == 0) return 0.0;
return sum / dataBuffer.size();
}
double RunningValues::Sum() {
return sum;
}
// Test harness.
int main() {
RunningValues test(10);
std::cout << "Ave = " << test.Average() << ", sum = " << test.Sum() << 'n';
for (int i = 40; i < 50; ++i)
{
test.Add(i);
std:: cout << "Add " << i << ", ave = " << test.Average() << ", sum=" << test.Sum() << 'n';
}
for (int i = 0; i < 20; ++i)
{
int val = rand() % 100;
test.Add(val);
std:: cout << "Add " << val << ", ave = " << test.Average() << ", sum=" << test.Sum() << 'n';
}
}
下面显示了一个示例运行,其中显示了各个点的平均值和总和:
Ave = 0, sum = 0
Add 40, ave = 40, sum=40
Add 41, ave = 40.5, sum=81
Add 42, ave = 41, sum=123
Add 43, ave = 41.5, sum=166
Add 44, ave = 42, sum=210
Add 45, ave = 42.5, sum=255
Add 46, ave = 43, sum=301
Add 47, ave = 43.5, sum=348
Add 48, ave = 44, sum=396
Add 49, ave = 44.5, sum=445
Add 83, ave = 48.8, sum=488
Add 86, ave = 53.3, sum=533
Add 77, ave = 56.8, sum=568
Add 15, ave = 54, sum=540
Add 93, ave = 58.9, sum=589
Add 35, ave = 57.9, sum=579
Add 86, ave = 61.9, sum=619
Add 92, ave = 66.4, sum=664
Add 49, ave = 66.5, sum=665
Add 21, ave = 63.7, sum=637
Add 62, ave = 61.6, sum=616
Add 27, ave = 55.7, sum=557
Add 90, ave = 57, sum=570
Add 59, ave = 61.4, sum=614
Add 63, ave = 58.4, sum=584
Add 26, ave = 57.5, sum=575
Add 40, ave = 52.9, sum=529
Add 26, ave = 46.3, sum=463
Add 72, ave = 48.6, sum=486
Add 36, ave = 50.1, sum=501
如果您希望有一个避免vector
的解决方案(当向量从大小 0 变为N
然后停留在那里时,拥有所有这些额外的功能有点浪费(,您可以只使用堆上的裸数组作为循环缓冲区。
其代码略有变化(没有main
,因为它没有改变(:
#include <iostream>
class RunningValues {
public:
RunningValues(size_t size = 50);
~RunningValues();
void Add(double val);
double Sum();
double Average();
private:
size_t count, next, limit;
double sum, *data;
};
RunningValues::RunningValues(size_t size)
: count(0), next(0), limit(size)
, sum(0.0), data(new double[size]) {}
RunningValues::~RunningValues() {
delete[] data;
}
void RunningValues::Add(double val) {
// Zero size, disallow adds.
if (limit == 0) return;
// If we would exceed limit, remove earliest.
if (count == limit) {
sum -= data[next];
--count;
}
// Add value to end.
data[next] = val;
sum += val;
++count;
next = (next + 1) % limit;
}
// Get the average (zero if nothing yet added) or sum.
double RunningValues::Average() {
if (count == 0) return 0.0;
return sum / count;
}
double RunningValues::Sum() {
return sum;
}
基于矢量的解决方案的变化相当小:
- 构造函数不再有向量(显然(。相反,它有一个固定大小的数组用作循环缓冲区,以及用于管理它的
count
和next
变量。 - 现在需要一个析构函数来清理缓冲区(在向量管理自身之前(。
- 项目添加现在使用
count
和next
(而不是向量(来弄清楚如何调整总和并保持相关数据的计数。 - 平均值的计算现在使用
count
而不是矢量大小。
除此之外,它实际上与上面基于矢量的代码非常相似。
根据您使用buffer
的方式,我建议使用它std::list<double>
。
将此添加到Running_Average
开头:
class Running_Average
{
private:
list<double> buffer;
const size_t MaxBufferSize;
public:
...
构造函数:
Running_Average::Running_Average(size_t size)
: MaxBufferSize(size)
{
}
AddSample()
和Average()
:
void Running_Average::AddSample(double val)
{
if (buffer.size() == MaxBufferSize)
{
buffer.pop_back();
}
buffer.push_front(val);
}
double Running_Average::Average()
{
double sum = 0;
for (auto a : buffer)
{
cout << a << endl;
sum += a;
}
return sum / buffer.size();
}
我还会删除sum
、average
和i
成员变量,而是在使用它们的地方声明它们(如果需要(。
表达式必须具有常量大小
double buffer[buffersize] = { 0 };
首先,buffersize
不是constexpr
。它是一个在运行时更改的变量。
指定数组的大小时,根据array
的标准定义:
constant expression
指定(元素数(的边界 数组。如果constant expression
的值为 N,则数组具有 编号为 0 到 N-1 的 N 个元素,
包含 5 个类型double
元素的数组的示例声明应如下所示:
double buffer[5]; // 5 is a literal
constexpr int size = 5;
double buffer[size]; // size is constexpr
其次,buffer
是可变长度数组 (VLA(。
VLA 在某些编译器中作为扩展部分受支持。
如何在不引发错误的情况下完成这项工作?
如果需要长度是可变的,请使用std::vector
并在构造函数中初始化它:
class Running_Average {
Running_Average(int size): buffer(size, 0) {}
std::vector<double> buffer;
}
- 在共享缓冲区内存中创建 ::std::string 对象
- openCL-创建子缓冲区返回错误代码13
- 创建一个结构的关联数组,以创建一个缓冲区,供键快速访问
- 创建一个简单的前向迭代器,该迭代器在循环缓冲区的"end"处自动换行
- 使用协议缓冲区创建通用反序列化程序
- 可变长度数组:如何在C++中创建具有可变大小的缓冲区
- 使用(非对象)API 时改变表数组C++而不重新创建整个平面缓冲区
- 如何创建缓冲区并制作Syncronize流
- 从浮点数组创建缓冲区
- 如何使用模板函数从缓冲区(T* 数据数组)创建 cv::Mat
- 如何有效地从标准:d创建D3D缓冲区
- 在内存(C 和/或 C++)中创建和管理字节缓冲区,该缓冲区可以根据需要自动调整大小
- 从gstreamer缓冲区创建输入张量
- 如何在缓冲区周围创建向量
- 确实调用glbufferdata创建/发布缓冲区
- 如何从顶点/索引缓冲区创建 CGAL Nef_polyhedron_3
- SDL OpenGL:创建缓冲区时访问违规
- 创建缓冲区以加载数据时出错
- DirectX - 使用顶点数组作为参数创建缓冲区
- 如何在(视频)内存中创建缓冲区以使用OpenGL绘制