[英]Malloc on a struct containing a std::vector
情况如下:
我使用 malloc 为结构分配 memory。 该结构包含各种项目,例如指针、字符串变量和向量。
事实上,当我们使用 malloc 时,没有调用任何构造函数。 使用类似于以下代码的代码,我遇到了一些变量有效而其他变量无效的情况。
注意:以下代码无法编译。 它的目的只是为了说明情况。
struct MyStruct
{
MyClass* mFirstClass;
bool mBool;
std::string mString;
std::vector<MyClass> mVector;
};
int main()
{
MyStruct* wMyStructure;
wMyStructure = (MyStruct*) malloc (sizeof(MyStruct));
MyClass wMyClassObject;
wMyStructure->mFirstClass = new MyClass();
wMyStructure->mFirstClass->func();
wMyStructure->mBool = false;
wMyStructure->mString = "aString";
wMyStructure->mVector.push_back(wMyClassObject);
return 0;
}
通过使用指针而不是那些变量 ( std::string* mString
),然后调用 object 构造函数 ( mString = new std::string;
) 不会引发异常。
但是,我遇到过这样的情况:在没有调用构造函数的情况下使用 mString 没有问题,但是当涉及到向量时,应用程序会自动退出。
这给我留下了很多问题:
如果没有使用构造函数,object 什么时候会抛出异常?
在我遇到的情况下,只有矢量引起了问题。 mString 可以保持原样还是我应该称它为构造函数?
使用 malloc 来完成整个操作,最安全的方法是什么?
使用 object 而不构造它必须是未定义的行为。 任何事情都可能随时发生。 如果你这样做,你不能依赖代码的任何部分来顺利运行,因为在这种情况下,语言不能保证任何事情。
您的代码会导致未定义的行为,因为您的wMyStructure
未指向 object,因此您不能在其上使用访问器运算符->
。
object 仅在其构造函数完成后才开始其生命周期。 由于您不调用任何构造函数,因此您没有 object。
(如果您的结构是一个 POD,即仅由原始类型和 POD 组成,那么这没问题,因为 POD 有微不足道的构造函数,它们什么都不做。)
您面临的具体问题是结构的字符串和向量成员没有调用它们的构造函数,因此这些成员不存在,因此整个 object 不存在。
如果要将 memory 管理与 object 构造解耦,可以使用放置语法:
// get some memory
char arena[HUGE_VAL];
void * morespace = malloc(HUGE_VAL);
// construct some objects
MyClass * px = new (arena + 2000) MyClass; // default constructor
YourClass * py = new (morespace + 5000) YourClass(1, -.5, 'x'); // non-default constructor
(你必须手动销毁这些对象, px->~MyClass();
等,当你完成它们时。)
使用未初始化的 object 是未定义的行为。 任何时候都可能抛出异常——或者根本不抛出异常。
1 ) 如果没有使用构造函数,object 什么时候会抛出异常?
如果不调用构造函数,就没有object。 您刚刚分配了一些空间。
2)在我遇到的情况下,只有向量引起了问题。 mString 可以保持原样还是我应该称它为构造函数?
这都是未定义的行为,几乎任何事情都可能发生。 没有规则。
3 ) 使用 malloc 完成整个操作,最安全的方法是什么?
最安全的方法是不使用 malloc,而是使用将调用构造函数的new
进行分配。 就这么简单
MyStruct* wMyStructure = new MyStruct;
其他答案似乎都无法解释编译器在做什么。 我会尽力解释。
当您调用malloc
时,程序会为结构保留一些 memory 空间。 该空间充满了 memory 垃圾(即随机数代替结构字段)。
现在考虑这段代码:
// (tested on g++ 5.1.0 on linux)
#include <iostream>
#include <stdlib.h>
struct S {
int a;
};
int main() {
S* s = (S*)malloc(sizeof(S));
s->a = 10;
*((int*)s) = 20;
std::cout << s->a << std::endl; // 20
}
因此,当访问结构的成员时,您实际上是在访问 memory position,写入它时应该没有意外行为。
但是在 C++ 中,您可以重载运算符。 现在想象一下,如果重载的赋值运算符需要初始化 class 会发生什么,如下面的代码所示:
// (tested on g++ 5.1.0 on linux)
#include <iostream>
#include <stdlib.h>
class C {
public:
int answer;
int n;
C(int n) { this->n = n; this->answer = 42; }
C& operator=(const C& rhs) {
if(answer != 42) throw "ERROR";
this->n = rhs.n; return *this;
}
};
struct S {
int a;
C c;
};
int main() {
S* s = (S*)malloc(sizeof(S));
C c(10);
C c2(20);
c = c2; // OK
std::cout << c.n << std::endl; // 20
s->c = c; // Not OK
// Throw "ERROR"
std::cout << s->c.n << std::endl; // 20
}
当s->c = c
执行时,赋值运算符验证s->c.answer
是否为42
,如果不是,则会抛出错误。
因此,如果您知道 class std::vector
的重载赋值运算符不期望初始化向量,则只能按照您在示例中所做的那样做。 我从来没有读过这个 class 的源代码,但我敢打赌它是预期的。
所以这样做是不可取的,但如果你真的需要,也不是不可能安全地完成。 你只需要确保你知道你正在使用的所有赋值运算符的行为。
在您的示例中,如果您确实需要结构上的std::vector
,则可以使用向量指针:
class MyClass { ... };
struct S {
std::vector<MyClass>* vec;
}
int main() {
S s;
MyClass c;
s.vec = new std::vector<MyClass>();
s.vec->push_back(c);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.