包含std :: vector的结构上的Malloc

时间:2011-08-16 22:17:20

标签: c++ vector constructor memory-management malloc

情况如下:

我使用malloc为结构分配内存。 该结构包含各种项目,如指针,字符串变量和向量。

事实是,当我们使用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),然后调用对象构造函数(mString = new std::string;),不会抛出异常。

但是,我遇到过一种情况,即在没有调用构造函数的情况下使用mString没有问题,但是当它来到向量时,应用程序会自动退出。

这给我留下了很多问题:

  1. 如果没有使用构造函数,对象何时会抛出异常?

  2. 在我遇到的情况下,只有向量引起了问题。 mString可以保持原样,还是应该调用它的构造函数?

  3. 使用malloc做最重要的事情最安全的方法是什么?

5 个答案:

答案 0 :(得分:5)

使用对象而不构造它必须是未定义的行为。任何事情都可能随时发生。如果你这样做,你不能依赖代码的任何部分来顺利运行,因为在这种情况下语言不保证任何东西。

答案 1 :(得分:2)

您的代码会导致未定义的行为,因为您的wMyStructure未指向某个对象,因此您可能无法在其上使用访问者运算符->

对象仅在构造函数完成之后才开始生命。由于您没有调用任何构造函数,因此您没有对象。

(如果您的结构是一个POD,即只包含原始类型和POD,那么这就没问题,因为POD具有无关的构造函数,它们什么都不做。)

您面临的具体问题是结构的字符串和向量成员无法调用其构造函数,因此这些成员不存在,因此整个对象不存在。

如果要将内存管理与对象构造分离,可以使用放置语法:

// 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();等。)

答案 2 :(得分:1)

使用非初始化对象是未定义的行为。可能在任何时候抛出异常 - 或者根本不抛出。

答案 3 :(得分:1)

  

1)如果没有使用构造函数,对象何时会抛出异常?

如果你不调用构造函数,那么 就没有对象。你刚刚分配了一些空间。

  

2)在我遇到的情况下,只有矢量引起了问题。 mString可以原样保留,还是应该调用它的构造函数?

这是所有未定义的行为,只是任何可能发生。没有规则。

  

3)使用malloc做最重要的事情最安全的方法是什么?

最安全的方法是使用malloc,但使用将调用构造函数的new进行分配。它就像这个一样简单

MyStruct* wMyStructure = new MyStruct;

答案 4 :(得分:0)

其他答案似乎都没有解释编译器正在做什么。我会试着解释一下。

当你调用malloc时,程序会为结构保留一些内存空间。该空间充满了内存垃圾(即随机数代替结构字段)。

现在考虑以下代码:

// (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 
}

因此,在访问某个结构的成员时,您实际上正在访问一个内存位置,在写入时应该没有意外的行为。

但是在C ++中你可以重载运算符。现在想象如果重载的赋值运算符需要初始化类会发生什么,如下面的代码所示:

// (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,如果不是,则会抛出错误。

因此,如果您知道类std::vector的重载赋值运算符不期望初始化向量,那么您只能像在示例中那样执行操作。我从来没有读过这个类的源代码,但我打赌它期望。

所以不建议这样做,但如果你真的需要,安全并非不可能完成。您只需要确保知道正在使用的所有赋值运算符的行为。

在您的示例中,如果您确实需要结构上的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);
}