簡體   English   中英

使用malloc / realloc用於包含std向量的類/結構數組

[英]Using malloc/realloc for array of classes/structs including std vector

我有一個問題wrt malloc / realloc內存將包含一個包含std向量的class / struct(我嘗試過問題的struct和class)成員數組。 我知道我可以通過使用new和std數組容器類來解決此問題。 但是,我想更好地理解為什么當我使用realloc而不是malloc時,以下的小代碼崩潰(因為在將較大的代碼項目從C轉換為C ++的情況下遇到了此問題)。 似乎我也不一定必須在類/結構中設置向量的初始大小(某些編譯器允許某些..)-那么類中的向量是什么?舒適的指針?

謝謝,凱

#include <stdlib.h>
#include <limits.h>
#include <float.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <vector>
/* mpic++ -O3 -ffast-math -pedantic vec-alloc.cpp -o vec-alloc */

using namespace std;

class float_vector{
public:
  double x;
  double y;
  double z;
  float_vector() : x(0), y(0), z(0) {};
};


class voxel{
public:
  float_vector   x;
  vector<double> y;

  voxel() : x() {};
};

int main(){

  int i;
  double d =1.111;
  voxel v0, *Comp, *Comp2;

  /* dynamically allocate memory */
  Comp= (voxel*)malloc(10*sizeof(voxel));
  for(i=0;i<10;++i) Comp[i] = v0;
  printf("malloc done\n");

  /* dynamically re-allocate memory */
  Comp2= (voxel*)malloc(sizeof(voxel));  
  printf("realloc done\n");
  for(i=0;i<10;++i){
    Comp2 =(voxel*)realloc(&Comp2[0], (i+1)*sizeof(voxel));
    Comp2[i] = v0;
  }  

  printf("realloc done\n");

  for(i=0;i<10;++i) Comp[i].y.push_back(d);
  for(i=0;i<10;++i) printf("%lf\n",Comp[i].y[0]);

  for(i=0;i<10;++i) Comp2[i].y.push_back(d); // this crashes
  for(i=0;i<10;++i) printf("%lf\n",Comp2[i].y[0]);

  return 1;
} 

如果將malloc()與非POD類一起使用,則必須手動調用構造函數(通過放置new )和析構函數。

使用未正確構造的對象會導致未定義的行為 ,這通常在涉及指針時會導致崩潰。

顯然,在沒有適當破壞對象的情況下釋放對象的內存也會導致UB。

您的代碼必須如下所示:

MyClass *arr = (MyClass *) malloc(10 * sizeof (MyClass));

for (int i = 0; i < 10; i++)
    new (arr + i) MyClass; // This line calls constructors

// Do something with the array here

for (int i = 0; i < 10; i++)
    arr[i].~MyClass(); // This line calls destructors.

free(arr);

此要求還意味着您不能將realloc()與非POD類型一起使用,因為它不會為您調用舊數組的析構函數,而為新數組調用析構函數。

手動重新分配代碼可能如下所示:

MyClass *new_ptr = (MyClass *) malloc(new_size * sizeof (MyClass));

for (int i = 0; i < new_size; i++)
    new (new_ptr + i) MyClass((MyClass &&) old_ptr[i]);

for (int i = new_size; i < old_size; i++)
    new (new_ptr + i) MyClass;

for (int i = 0; i < old_size; i++)
    old_ptr[i].~MyClass();

free(old_ptr);

並且請記住,上面的代碼並不是真正的異常安全。 如果構造函數引發異常並捕獲了該異常,那么您要確保正確地破壞了所構造的對象。 謝謝@SteveJessop。

現在,當您了解了為什么在C ++中通常應避免使用malloc() / free() ,我希望您能返回到更安全的new / delete ,它可以為您完成所有構造和銷毀工作。

它可能與realloc無關。 在開始時,您的代碼已經具有未定義的行為:

for(i=0;i<10;++i) Comp[i] = v0;

Comp[0]從未被初始化(因為malloc返回未初始化的內存-它無法知道您打算將其用於什么類型,因此即使它想要也無法對其進行初始化)。 然后,您的代碼嘗試分配給它。 對於諸如vector復雜類型,這是不允許的。

為什么不允許呢? 對於向量,因為當您分配給已經保存數據的向量時,它需要釋放舊數據。 如果沒有什么可以釋放的,那么它將什么也沒有釋放。 但是未初始化的內存可能根本沒有任何值,因此vector很可能看起來似乎應該釋放一些東西,實際上根本不是一個可釋放的指針,更不用說vector應該因此釋放的東西了分配。 如果不進行初始化,則會違反“此指針數據成員始終是空指針,或者是向量負責的某些內存的地址”的類不變式,因此vector代碼無法正常工作。

假設您的代碼以某種方式使其超越了這一點,您仍然無法重新realloc包含vector內存。 從標准的角度來看,這是因為vector<double>不是POD類型,因此按字節復制它(包括通過realloc完成的復制)會導致未定義的行為。

從特定實現的角度來看,我們可能會問自己實現者可能編寫什么代碼,如果向量是逐字節復制的,那將會出錯。 一個假設的答案是,在某些情況下, vector可能包含指向其自身的指針(作為所謂的小向量優化的一部分)[編輯:實際上,我認為在其他標准中,不可能進行小向量優化原因,但我的一般觀點是,由於向量不是POD,因此實現者可以自由使用它們的創造力。 如果重新定位了向量,則此指針不再指向向量自己的主體,因此不滿足類不變式,並且代碼不再起作用。 為了使實現者可以自由地編寫這樣的代碼,您作為類用戶的自由是有限的,並且不允許您通過按字節復制來重定位向量(或通常是任何非POD類型的向量)。

/* dynamically allocate memory */
Comp= (voxel*)malloc(10*sizeof(voxel));

現在, Comp是指向未初始化內存的指針。

for(i=0;i<10;++i) Comp[i] = v0;

這會嘗試調用Comp[i].operator=(v0) ,但是Comp[i]不是有效的初始化對象。 在簡單的測試/調試情況下,我們可能會很幸運,但是在實踐中,我們會得到垃圾,向量將嘗試釋放/使用無效的指針。

這不僅意味着必須calloc()內存,而且不能對初始化對象期望找到的值進行假設。

/* dynamically re-allocate memory */
Comp2= (voxel*)malloc(sizeof(voxel));  
printf("realloc done\n");

現在,Comp2是指向單個體素的指針,並且沒有完成“重新分配”。

for(i=0;i<10;++i){
  Comp2 =(voxel*)realloc(&Comp2[0], (i+1)*sizeof(voxel));
  Comp2[i] = v0;
}  

這真是奇怪。 它從Comp2指向單個體素開始。 然后,由於某種原因,您將采用第一個元素( &Comp2[0] )的地址,而不僅僅是使用第一個元素( Comp2 )的地址,然后將其重新分配為相同的大小。 然后,將v0復制並分配到未初始化的內存中的最后一個位置:

Comp2 = [...uninit...]

for (i  = 0
realloc(i + 1 == 1)

Comp2 = [...uninit...]
              ^-- v0

i++
realloc(i+1 == 2)

Comp2 = [.....v0.....][...uninit...]
                            ^--v0

簡短:您不能將malloccallocrealloc與非Pod對象一起使用。 您可能偶爾會擺脫它,但是您基本上是將一只裝滿shot彈槍的腳對准您的腳。

似乎我也不一定必須在類/結構中設置向量的初始大小

您可以輕松地在類中設置向量的默認大小,並且需要C ++ 11(對於gnu / clang編譯器, -std=c++11或更大,VS2013或更高)

#include <iostream>
#include <vector>

struct A {
    std::vector<int> v = { 1, 2, 3 }; // default population
};

struct B {
    std::vector<int> v;
    B() : v(4) {}
};

int main() {
    A a;
    B b;
    std::cout << a.v.size() << ", " << b.v.size() << "\n";
    std::cout << "\n";
    for (int v : a.v) { std::cout << v << "\n"; }
    std::cout << "\n";
    for (int v : b.v) { std::cout << v << "\n"; }
}

http://ideone.com/KA9fWB

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM