数组大小作为构造函数参数
Array Size as Constructor Parameter
我正在创建一个c++类,它包装了一个浮点2d数组并提供了一些额外的功能。我想把数组的形状作为参数传递给构造函数,参见下面的代码(类块部分)。以注释"//here"结束的行将在编译过程中导致错误,因为那时_nx和_ny是未知的。有两种解决方案(我认为):一种是使用指针(见下面代码中的解决方案1)并动态分配数组;另一种是使用模板(参见下面代码中的解决方案2),但我有几个理由不使用它们:
- 我不想使用指针,只要有一个无指针选择;换句话说,我不想使用new和delete。的这样做的原因是个人对纯c++的偏好。 我不想要使用模板,因为可以有许多不同的块形状-我不希望编译器为每个类创建许多类,
另外,我不想使用stl vector,因为数组的大小在创建后是固定的;我也在做数值计算,所以一个"原始"数组更适合我。
我在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;
}
仅使用std::vector
。一周前我也遇到过同样的决策问题,并在这里问过。
如果您使用reserve()
,这将不会使您的vector
重新分配多次(如果有的话),那么vectors
将不会影响您的项目的性能。换句话说,vector
不太可能成为您的瓶颈。
注意,在C++
中vectors
被广泛使用,因此在release mode
中,对进行的优化是非常有效的。
或者等待std::dynarray
的引入!(不幸的是,不是在C++14
,但在array TS
或C++17
)。来源,归功于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
。:)我很确定你知道如何改变你的代码,你不需要我告诉你(是这样,让我知道当然:))。
你可以尝试其他的时间方法,在我的伪网站上找到。
我认为你在拒绝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
那样容易地转移数组的所有权
std::vector是你的朋友,不需要重建wheel:
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
};
您也可以像在第一个解决方案中那样使用简单的双*。但是,不要忘记在销毁块时删除它。
现在内存很便宜,你的块矩阵非常非常小。
所以,当你不想使用模板,也不想使用动态分配时,我们就使用一个固定大小的数组,足够大,可以容纳最大的块。
就这么简单。
使用
std::auto_ptr
显示的代码有两个主要问题:
std::auto_ptr
在c++ 11中已弃用。std::auto_ptr
总是执行delete p
,当分配的是数组时,会产生未定义行为,如new T[n]
。
顺便说一下,关于使用模板所设想的代码膨胀,如果您测量,您可能会感到惊喜。
顺便说一下,这听起来有点不成熟的优化。使用c++时,始终牢记性能是一个好主意,不要做不必要的变慢或消耗内存的事情。但同时,一个好主意是不要陷入不必要的工作中,而这些工作实际上并不重要,或者如果只是忽略它就不会重要。
因此,您的主要默认选择应该是使用std::vector
作为存储
然后,如果你怀疑它太慢,测量。发布版本。哦,我只说了两次,所以这里是第三个:测量。: -)
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 在按值调用 (c++) 中转发构造函数参数
- 如何使用 swig 修改类构造函数以保留对其中一个构造函数参数的引用?
- 如何在构造函数参数中初始化"std::set"?
- 何时应在构造函数参数中使用 const C++?
- 使用模板化结构作为构造函数参数
- 使用 lambda 作为构造函数参数是否需要C++ 17?
- 如何使输入文本文件成为构造函数参数?c++
- shared_ptr构造函数参数是否应按值传递
- 是否允许使用初始值设定项列表将const数组引用实例化为构造函数参数
- 复制构造函数参数为0
- 用作成员构造函数参数的函数的求值顺序
- 结构中的默认成员值或默认构造函数参数
- C++将引用成员绑定到构造函数参数
- 如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?
- 模板函数指针参数与构造函数参数
- 如何将 std::string 作为构造函数参数传递,并将其保存的 C 字符串存储在 void 指针中?
- 如何基于构造函数参数模板化类成员函数的代码
- 将派生类构造函数参数传递给受保护的成员
- 如何根据构造函数参数使用超类类型初始化成员变量?