我正在创建一个C ++类,它包装浮点2d数组并提供一些额外的功能。我想将数组的形状作为参数传递给构造函数,请参阅下面的代码(类Block部分)。该行以评论结束" //此处"编译时会导致错误,因为当时不知道_nx和_ny。 有两个解决方案(我认为)围绕这个:一个是使用指针(参见下面的代码中的解决方案1)并动态分配数组;另一个是使用模板(参见下面代码中的解决方案2),但我有几个理由不使用它们:
另外,我不想使用stl向量,因为数组大小在创建后是固定的;我也在进行数值计算,所以“原始”和“阵列更适合我。
我搜索过SO并且有五六个问题提出了类似的问题,但是没有结论哪一个更好,而且没有一个是从数字上的立场点来的,所以矢量或新的/剔除是他们的好答案 - 但不适合我。我发布这个问题的另一个原因是我想知道我是否在使用c ++功能方面过于严格。由于我将广泛使用c ++,因此了解c ++的限制并停止对某些不存在的功能进行过多的搜索/搜索非常重要。
#include <iostream>
#include <memory>
using namespace std;
class Block
{
public:
Block(int nx, int ny):_nx(nx),_ny(ny){}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx, _ny;
double _data[_nx][_ny]; // here
};
/// Solution 1, using auto_ptr
class BlockAuto
{
public:
BlockAuto(int nx, int ny):_nx(nx),_ny(ny),_data(new double[_nx*_ny]){}
void Report(void)
{
cout << "BlockAuto With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx;
const int _ny;
const auto_ptr<double> _data;
};
/// Solution 2, using template
template<unsigned int nx, unsigned int ny>
class BlockTpl
{
public:
BlockTpl():_nx(nx),_ny(ny){}
void Report(void)
{
cout << "BlockTpl With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx;
const int _ny;
double _data[nx][ny]; // uncomfortable here, can't use _nx, _ny
};
int main(int argc, const char *argv[])
{
Block b(3,3);
b.Report();
BlockAuto ba(3,3);
ba.Report();
BlockTpl<3,4> bt;
bt.Report();
return 0;
}
答案 0 :(得分:4)
只需使用std::vector
即可。我在一周之前遇到了同样的决定问题,并问了here。
如果您使用reserve()
,这不会使您的vector
多次重新分配(如果有的话),那么vectors
将不会影响您项目的效果。换句话说,vector
不太可能是您的瓶颈。
请注意,C++
vectors
广泛使用,因此在release mode
中,对其进行的优化非常有效。
或等待std::dynarray
被引入! (遗憾的是,不是C++14
,而是array TS
或C++17
)。 Source,归功于manlio。
永远不要忘记:过早优化是邪恶的源头。 - Knuth。
不相信我?你不应该!试验自己并找出答案!
这是我的实验,让我确信当我遇到与你完全相同的问题时。
实验代码:
#include <iostream>
#include <vector>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;
int main() {
const int N = 100000;
cout << "Creating, filling and accessing an array of " << N << " elements.\n";
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
int array[N];
for(int i = 0; i < N; ++i)
array[i] = i;
for(int i = 0; i < N; ++i)
array[i] += 5;
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
cout << "Creating, filling and accessing an vector of " << N << " elements.\n";
t1 = high_resolution_clock::now();
vector<int> v;
v.reserve(N);
for(int i = 0; i < N; ++i)
v.emplace_back(i);
for(int i = 0; i < N; ++i)
v[i] += 5;
t2 = high_resolution_clock::now();
time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
结果(注意-o2
编译器标志):
samaras@samaras-A15:~$ g++ -std=gnu++0x -o2 px.cpp
samaras@samaras-A15:~$ ./a.out
Creating, filling and accessing an array of 100000 elements.
It took me 0.002978 seconds.
Creating, filling and accessing an vector of 100000 elements.
It took me 0.002264 seconds.
所以,只需std::vector
。 :)我很确定你知道如何更改你的代码,你不需要我告诉你(是的,当然让我知道:))。
您可以尝试使用我的pseudo-site中的其他时间方法。
答案 1 :(得分:0)
这些天内存很便宜,你的块矩阵非常小。
因此,当您不想使用模板,并且不想使用动态分配时,只需使用足够大的固定大小的数组来容纳最大的块。
就这么简单。
您使用std::auto_ptr
显示的代码有两个主要问题:
std::auto_ptr
在C ++ 11中已弃用。
std::auto_ptr
始终执行delete p
,当分配属于数组时会产生未定义的行为,例如new T[n]
。
顺便说一下,关于模板的预想代码膨胀,如果你衡量,你可能会感到惊喜。
同样顺便说一句,这有点过早优化。使用C ++,始终牢记性能是一个好主意,而不是不必要地放慢速度或消耗内存。但是,一个好主意是不要陷入不必要地解决一些真正无关紧要的感知性能问题,或者如果它被忽略就不会有重要意义。
因此,您的主要默认选择应该是使用 std::vector
进行存储。
然后,如果您怀疑它太慢,测量。发布版本。哦,我只说了两次,所以这里是第三次:衡量。 ; - )
答案 2 :(得分:0)
std :: vector是你的朋友,无需重建轮子:
class Block
{
public:
BlockAuto(int p_rows, int p_cols):m_rows(nx),m_cols(ny)
{
m_vector.resize(m_rows*m_cols);
}
double at(uint p_x, uint p_y)
{
//Some check that p_x and p_y aren't over limit is advised
return m_vector[p_x + p_y*m_rows];
}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int m_rows;
const int m_cols;
std::vector<double> m_vector;
//or double* m_data
};
您也可以在第一个解决方案中使用简单的双*。不要忘记在破坏块时删除它。
答案 3 :(得分:0)
我认为你因为可退款问题拒绝std::vector
时过于谨慎。当然,您的程序可以容纳sizeof(Block)
一些大于原始指针解决方案的指针大小。如果使用单个vector
而不是向量向量,那么用于维护矩阵的vector
应该与性能指针解决方案没有区别。
使用vector
也会让你更不可能搞砸了。例如,您的auto_ptr
解决方案具有未定义的行为,因为auto_ptr
将导致delete
,而不是delete[]
,析构函数中的数组。此外,除非定义复制构造函数和赋值运算符,否则很可能无法获得预期的行为。
现在,如果您必须避开vector
,我建议您使用unique_ptr
代替auto_ptr
。
class Block
{
public:
Block(int nx, int ny):_nx(nx),_ny(ny), _data(new double[_nx*_ny])
{}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx, _ny;
std::unique_ptr<double[]> _data; // here
};
这将正确调用delete[]
,并且不会像auto_ptr
一样轻松转移数组的所有权。