005
23.05.2011, 21:34 Uhr
0xdeadbeef
Gott (Operator)
|
Wenn es nur um Arrays geht, sollte man in C99 VLAs benutzen. Unglücklicherweise beherrschen nicht alle gängigen C-Compiler C99 - insbesondere MSVC tanzt hier aus der Reihe. Microsoft hat bislang auch keine Pläne, C99 zu implementieren.
Dynamischer Speicher ist nicht als solches eine Schlechte Sache™, aber man muss vorsichtig sein, dass man keinen Speicher leckt. In C++ gilt RAII, in C muss man halt höllisch aufpassen. Generell sollte man mit dynamischem Speicher aber sparsam umgehen - die Allokation ist wesentlich teurer, als einfach etwas auf den Stack zu klatschen, gleiches gilt für die Deallokation, und in stark nebenläufigen Szenarien kann Heap-Contention leicht zu einem echten Performance-Problem werden.
Gleichwohl: Ein Stack hat begrenzte Größe, und viele Sachverhalte lassen sich davon unabhängig nicht (oder nur sehr umständlich) auf dem Stack erschlagen. Ich denke da in Richtung Laufzeitpolymorphie, Bäume, Graphen etc. Es kann in speziellen Zusammenhängen (beispielsweise stark nebenläufigen) Sinn machen, den Extraaufwand zu betreiben, und ich habe derartige Dinge auch schon getan, aber es ist eine Scheißarbeit, und es will sehr gründlich getestet werden. Wenn man keine guten Gründe dafür hat, sollte man sich das sparen.
Um zu verdeutlichen, worüber ich rede, ein Beispiel für Laufzeitpolymorphie auf dem Stack:
C++: |
///////////////////////////////////////////////////////// #include <iostream> #include <stdexcept>
#include <boost/aligned_storage.hpp> #include <boost/mpl/max_element.hpp> #include <boost/mpl/sizeof.hpp> #include <boost/mpl/transform_view.hpp> #include <boost/mpl/vector.hpp>
struct foo_base { virtual void foo() const = 0; virtual ~foo_base() { } };
struct foo_1 : foo_base { virtual void foo() const { std::cout << "foo_1" << std::endl; } };
struct foo_2 : foo_base { virtual void foo() const { std::cout << "foo_2" << std::endl; } };
class foo_factory : public foo_base { public: foo_factory(int foo_number) : be_(0) { switch(foo_number) { case 1: new (storage()) foo_1(); be_ = static_cast<foo_1*>(storage()); break; case 2: new (storage()) foo_2(); be_ = static_cast<foo_1*>(storage()); break; default: throw std::invalid_argument("Foo-Typ unbekannt"); } }
~foo_factory() { be_->~foo_base(); }
virtual void foo() const { be_->foo(); }
private: typedef boost::mpl::vector<foo_1, foo_2> foo_types_t; typedef boost::mpl::max_element<boost::mpl::transform_view<foo_types_t, boost::mpl::sizeof_<boost::mpl::_1> > >::type foo_types_iterator;
inline void *storage() { return static_cast<void *>(storage_.address()); } inline void const *storage() const { return static_cast<void const *>(storage_.address()); }
boost::aligned_storage<sizeof(boost::mpl::deref<foo_types_iterator::base>::type)> storage_; foo_base *be_; };
int main() { foo_factory f1(1); foo_factory f2(2);
f1.foo(); f2.foo(); }
|
edit (ao): Tonnen von ///// gelöscht und Layout gerettet -- Einfachheit ist Voraussetzung für Zuverlässigkeit. -- Edsger Wybe Dijkstra Dieser Post wurde am 23.05.2011 um 21:55 Uhr von ao editiert. |