|
You last visited: Today at 18:22
Advertisement
Realloc in C++?
Discussion on Realloc in C++? within the C/C++ forum part of the Coders Den category.
03/31/2013, 23:10
|
#1
|
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
|
Realloc in C++?
Ich weiß, dass mich gleich 99% der Leute hier steinigen werden, dennoch versuche ich folgendes zum Laufen zu bringen:
Ich will realloc auf std::string anweden.
Ich weiß, dass die Alternative std::vector besser schöner und von der Schnelligkeit her wahrscheinlich keinen Unterschied macht, dennoch möchte ich realloc benutzen.
Code zum assozieren des Speichers:
PHP Code:
void * func_raw; string *functions; unsigned int index=100; func_raw = malloc(sizeof(string)*100); functions= new(func_raw) string[100];
Das funktioniert auch, jetzt aber zu meinem Sorgenkind:
PHP Code:
index+=100; delete[] functions; func_raw = realloc(func_raw,sizeof(string)*index); functions= new(func_raw) string[index];
Der 2. Code wird später im Code aufgerufen und soll eine dynamische Größe bekommen. Da ich functions mit new assoziert habe, muss ich es ja auch mit delete wieder löschen um Memory-Leaks entgegen zu wirken oder etwa nicht?
Mein Programm meldet jedoch ein Segmentationfault wenn ich auf den Bereich des neuen größeren Arrays zugreifen will.
Es ist mit Sicherheit irgendetwas banales wo ich gerade nicht draufkomme, wäre nett wenn ihr mir kurz unter die Arme greifen könntet.
|
|
|
04/01/2013, 00:08
|
#2
|
elite*gold: 297
Join Date: Dec 2010
Posts: 1,129
Received Thanks: 1,687
|
realloc in C++ funktioniert in etwa so:
Code:
template <typename T>
inline T *reallocate(T *data, size_t old_size, size_t new_size)
{
if (new_size == old_size)
return data;
T *tmp = new T[new_size];
std::copy(data, data + std::max(old_size, new_size), tmp);
delete[] data;
return tmp;
}
Mit deinem Code jedoch scheinst du vollkommen verwirrt zu sein. Grundlegend gilt, dass man nie malloc/calloc/realloc mit new/new[] mischen sollte. Wenn man C++ schreibt, benutzt man new und new[].
Code:
func_raw = malloc(sizeof(string)*100);
functions= new(func_raw) string[100];
Hier benutzt du malloc, um den Speicher zu reservieren und benutzt dann ein placement-new um den default-constructor von std::string aufzurufen. Das ist schlechte Praxis, aber funktioniert, wenn malloc nicht nullptr zurückgibt.
Code:
delete[] functions;
func_raw = realloc(func_raw,sizeof(string)*index);
functions= new(func_raw) string[index];
Hier benutzt du delete[], um Speicher freizugeben. Danach benutzt du realloc mit func_raw - einer nicht initialisierten Variable - und erzeugst damit einen Fehler. realloc sollte auf gültige Stellen im Speicher angewendet werden, die mit malloc/calloc/realloc alloziert wurden.
Danach benutzt du wieder das placement-new um den std::string-constructor aufzurufen.
Alles in allem ist dein Code ziemlicher Schwachsinn. Viel einfacher wäre das hier:
Code:
std::string *functions = new std::string[100];
Und passend dazu unter Verwendung der oben genannten Funktion:
Code:
functions = reallocate(functions, 100, 200);
Noch einfacher wäre wohl nur std::vector<std::string>, aber das wolltest du ja nicht.
|
|
|
04/01/2013, 14:22
|
#3
|
elite*gold: 0
Join Date: Feb 2013
Posts: 1,137
Received Thanks: 869
|
Sag mal, was du genau vor hast. Dann kann man dir vielleicht eine andere Möglichkeit bieten.
Ausserdem: Wenn die Objekte schon unbedingt aufm Heap liegen sollen, dann benutze wenigstens smart pointer.
|
|
|
04/01/2013, 15:17
|
#4
|
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
|
Quote:
Originally Posted by Schlüsselbein
Sag mal, was du genau vor hast. Dann kann man dir vielleicht eine andere Möglichkeit bieten.
Ausserdem: Wenn die Objekte schon unbedingt aufm Heap liegen sollen, dann benutze wenigstens smart pointer.
|
@Underscore
Dass ich ein Copy ausführen muss versuchte mir Google auch klar zu machen. Aber genau das wollte ich nicht. Ich will nicht alle Elemente, die eventuell nicht einmal verändert wurden, neu beschreiben.
Meine Idee war wie du beschrieben hast einen Pointer auf den assozierten Speicher zu geben und dann auf basis dieses Speichers die String-Objekte zu erstellen.
Mir geht es also prinzipiell um eine equivalente Methode zur Reassoziierung von Speicher für komplexe Speichertypen.
Mir geht es hier gar nicht um die Anwendbarkeit oder die Geschwindigkeit dieser Methode, sondern nur um die Möglichkeit ohne erneutes Kopieren ein Array zu vergrößern und zwar ohne std::vector.
|
|
|
04/01/2013, 16:26
|
#5
|
elite*gold: 297
Join Date: Dec 2010
Posts: 1,129
Received Thanks: 1,687
|
Quote:
Originally Posted by Schlüsselbein
Wenn die Objekte schon unbedingt aufm Heap liegen sollen, dann benutze wenigstens smart pointer.
|
Da er anscheinend ein Array mit dynamisch veränderbarer Größe haben möchte, sind Smart Pointer hier keine Lösung, sondern lediglich ein unnötiger Overhead - unique_ptr ist nicht CopyConstructible (zwar noexcept MoveConstructible, aber das nicht in der Implementation von VS2012 Nov'12 CTP) und shared_ptr hat einen unnötigen Overhead.
---
Quote:
Originally Posted by Shadow992
Dass ich ein Copy ausführen muss versuchte mir Google auch klar zu machen. Aber genau das wollte ich nicht. Ich will nicht alle Elemente, die eventuell nicht einmal verändert wurden, neu beschreiben.
|
Um das kopieren kommt man nicht drumherum: Man kann weder via malloc noch new[] (und ähnlichen) sagen, dass der reservierte Speicher erweitert werden soll, weil dort ja bereits anderes liegen könnte. Man kann lediglich komplett neu allozieren, das alte kopieren und dann das alte freigeben. Intern macht realloc das gleiche, indem es malloc, memcpy und free benutzt.
In C++ aber sollte man nie memcpy benutzen, wenn man nicht POD-Datentypen (POD = Plain-Old-Data) kopiert. Der Hintergrund ist, dass Klassen einen CopyConstructor definieren können, der bei std::string intern etwas macht, dass mit memcpy nicht erreicht werden kann.
Quote:
Originally Posted by Shadow992
Meine Idee war wie du beschrieben hast einen Pointer auf den assozierten Speicher zu geben und dann auf basis dieses Speichers die String-Objekte zu erstellen.
Mir geht es also prinzipiell um eine equivalente Methode zur Reassoziierung von Speicher für komplexe Speichertypen.
|
Zunächst einmal verwechselst du anscheinend die Bedeutung der Wörter allozieren und assoziieren. Das sind zwei total verschiedene Dinge. Und zum Rest: Das ist Schwachsinn. Die einzige Methode, mit der du um das Kopieren herum kommst, ist eine Linked-List wie etwa std::list, die jedoch beim Zugriff auf die Array-Elemente vom Index abhängig langsamer ist.
Quote:
Originally Posted by Shadow992
Mir geht es hier gar nicht um die Anwendbarkeit oder die Geschwindigkeit dieser Methode, sondern nur um die Möglichkeit ohne erneutes Kopieren ein Array zu vergrößern und zwar ohne std::vector.
|
Ein Array ohne Kopie zu vergrößern ist nicht ohne weiteres machbar. Dennoch kann man mit Tricks zumindest bis zu einem gewissen Limit etwas ähnliches erreichen:
Code:
template <typename T, size_t N>
class storage
{
typedef typename std::aligned_storage<sizeof(T) * N, alignof(T)>::type storage_type;
storage_type data_;
T *end_;
public:
storage() : end_(reinterpret_cast<T *>(std::addressof(data_)))
{
}
~storage()
{
for (T &obj : *this)
obj.~T();
}
T &operator[](size_t index)
{
if (std::addressof(data_) >= end_)
throw std::out_of_range("out of range");
return *(reinterpret_cast<T *>(std::addressof(data_)) + index);
}
template <typename... Args>
void emplace(Args &&... args)
{
if (end_ >= &end_)
throw std::bad_alloc();
new(end_++) T(std::forward<Args>(args)...);
}
T *begin()
{
return reinterpret_cast<T *>(std::addressof(data_));
}
T *end()
{
return end_;
}
};
Das kann in so verwendet werden, dass man z.B. via storage st<std::string, 1000> Platz für 1000 std::string Objekte reserviert. Via st.emplace(...) werden diese dann erzeugt und via st[index] können diese benutzt werden. Das ist übrigens in etwa wie std::vector, nur dass dieser Platz für die Objekte dynamisch reserviert und bei Bedarf mehr alloziert und dann aber auch kopiert. Da du dies ja vermeiden möchtest ist diese Lösung eine der elegantesten, die ich mir vorstellen kann.
|
|
|
04/01/2013, 17:14
|
#6
|
elite*gold: 7110
Join Date: Jun 2009
Posts: 28,902
Received Thanks: 25,407
|
Quote:
|
Intern macht realloc das gleiche, indem es malloc, memcpy und free benutzt.
|
Soweit ich weiß, versucht realloc erst den bereits reservierten Speicher zu erweitern, bevor es einen neuen Block alloziert, sodass du sogar denselben Pointer zurückerhalten kannst. Ein Äquivalent in C++ gibt es nicht.
|
|
|
04/01/2013, 17:22
|
#7
|
elite*gold: 0
Join Date: Feb 2013
Posts: 1,137
Received Thanks: 869
|
Quote:
|
Da er anscheinend ein Array mit dynamisch veränderbarer Größe haben möchte, sind Smart Pointer hier keine Lösung,
|
Das war auf seine std::string-Objekte allgemein bezogen.
Ich frage mich allerdings immernoch, wo der Sinn in der Geschichte hier liegt? Was möchtest du genau erreichen, Shadow? Klar, realloc ohne zu kopieren, aber wozu? Was ist dein eigentliches Ziel?
Btw: std::unique_ptr lassen sich unter VS11 mit November CTP wunderbar durch die Gegend moven.
Gruß
|
|
|
04/01/2013, 17:22
|
#8
|
elite*gold: 297
Join Date: Dec 2010
Posts: 1,129
Received Thanks: 1,687
|
Quote:
Originally Posted by MrSm!th
Soweit ich weiß, versucht realloc erst den bereits reservierten Speicher zu erweitern, bevor es einen neuen Block alloziert, sodass du sogar denselben Pointer zurückerhalten kannst. Ein Äquivalent in C++ gibt es nicht.
|
Laut C11-Standard ist das der Implementation überlassen.
Und außerdem:  beschäftigt sich damit, ist aus dem 2013-01 Mailing und höchstwahrscheinlich in C++14.
Quote:
Originally Posted by Schlüsselbein
Btw: std::unique_ptr lassen sich unter VS11 mit November CTP wunderbar durch die Gegend moven.
|
Anscheinend hat Microsoft hier eine Compilereigene noexcept-Erweiterung drin, weil der move-ctor nur benutzt werden soll, wenn der Objekttyp noexcept MoveConstructible ist. Davon wusste ich nichts, dann hast du natürlich recht.
|
|
|
04/01/2013, 18:38
|
#9
|
elite*gold: 77
Join Date: May 2008
Posts: 5,430
Received Thanks: 5,878
|
Quote:
Originally Posted by Schlüsselbein
Das war auf seine std::string-Objekte allgemein bezogen.
Ich frage mich allerdings immernoch, wo der Sinn in der Geschichte hier liegt? Was möchtest du genau erreichen, Shadow? Klar, realloc ohne zu kopieren, aber wozu? Was ist dein eigentliches Ziel?
Btw: std::unique_ptr lassen sich unter VS11 mit November CTP wunderbar durch die Gegend moven.
Gruß
|
Danke für die Antworten. Ich denke damit ist das Thema soweit durch.
Mein Ziel dahinter war kein bestimmtes. Ich war nur vor dem Post auf der Suche nach etwas derartiges fand std::vector und fand das genau passend für meine Zwecke. Irgendwie hatte ich aber trotzdem ein Gefühl, dass es irgendwie gehen sollte, meine Frage war also reine Neugier deswegen war mir die Umsetzung und Geschwindigkeit auch relativ egal.
|
|
|
All times are GMT +1. The time now is 18:22.
|
|