[英]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
簡短:您不能將malloc
或calloc
或realloc
與非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"; }
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.