问题描述
我正在尝试实现我自己的数学库,我从向量开始。这个想法是给类一个指向数字数组的指针,然后复制该数组并将其存储在私有变量指针给出的数据地址中。首先,我使用 alloca
尝试为私有变量释放一些内存
vml.h
namespace vml {
// Vectors
template <typename in_type,const int in_length>
class vec {
public:
vec(in_type* in_data) {
std::cout << data << std::endl;
std::copy(in_data,in_data + in_length,data);
}
vec() {
data = nullptr;
}
in_type& operator()(int index) const {
_ASSERT(0 <= index && index < in_length);
return data[index];
}
private:
in_type* data = alloca(in_length * sizeof(in_type));
};
main.cpp
int main() {
int list[] = { 1,2,3 };
int list2[] = {2,4,6 };
vml::vec<int,3> a(list);
vml::vec<int,3> b(list);
return 0;
}
这不会出错,但是由于某种原因,alloca
在调用两个实例时两次返回相同的地址。我到处搜索,但找不到原因的解释。所以我决定使用数组分配内存。如果你能回答这个问题,那将是非常有帮助的。
谢谢。
解决方法
您必须非常注意alloca
。它在堆栈而不是堆上分配内存。一旦调用 alloca
的函数退出,该内存就会被释放。在这种情况下,它将在构造函数中调用,因此当您调用 operator()
时,内存已经被释放并且您正在处理未定义的行为。
除非您确实需要避免堆分配,并且您确定不会溢出堆栈并且您了解使用 alloca
的所有限制,否则最好避开它。
让我们从基础开始,您的堆栈很可能只有 1 MB,因此在几次向量和递归调用之后,您的程序可能会死掉。
如果你想在堆栈上解决它,你可以使用 std::array
作为 data
警告未经测试的代码
template <typename in_type,const int in_length>
class vec {
public:
vec(in_type* in_data) {
std::cout << data << std::endl;
std::copy(in_data,in_data + in_length,data);
}
vec() = default;
in_type& operator()(int index) const {
_ASSERT(0 <= index && index < in_length);
return data[index];
}
private:
std::array<in_type,in_length> data;
};
或者,如果您想使用 std::array
中的所有好东西
template <typename in_type,const int in_length>
class vec : public std::array<in_type,in_length> {
public:
using std::array::array; // use constructors,might need template param to compile
}
这也意味着,如果您在某个时候只想更改为堆,您只需像其他所有类一样分配您的 vec。
另一种选择是使用 C++17 PMR,使用堆栈上的分配作为存储并使 vec
PMR 感知。
您不能将 alloca
包装在函数中并将其指针返回到外部,因为包装函数的堆栈将被释放。
如果你调用它作为成员初始化器,它实际上是从构造函数调用的,并且可能在构造函数返回时被释放,然后重新使用。