Jakie są zastosowania „nowego miejsca docelowego”?


410

Czy ktoś tutaj kiedykolwiek używał „nowego miejsca docelowego” w C ++? Jeśli tak, to po co? Wydaje mi się, że przydałby się tylko na sprzęcie odwzorowanym w pamięci.


14
To tylko informacje, których szukałem, aby wywołać konstruktory obiektów na zwiększenie puli pamięci przydzielonej. (Mam nadzieję, że te słowa kluczowe ułatwią znalezienie kogoś w przyszłości).
Sideshow Bob

2
Jest on używany w artykule na temat C ++ 11 w Wikipedii w konstruktorze związku.
HelloGoodbye,

@HelloGoodbye, ciekawe! Dlaczego w tym artykule nie można po prostu p = ptużyć operatora przypisania Pointzamiast tego new(&p) Point(pt)? Zastanawiam się nad różnicami między nimi. Czy pierwszy zadzwoniłby operator=do Pointa, a drugi do kopii konstruktora Point? ale wciąż nie jestem do końca pewien, dlaczego jedno jest lepsze od drugiego.
Andrei-Niculae Petre

@ Andrei-NiculaePetre Sam nie użyłem umieszczania nowego, ale myślę, że powinieneś go użyć - wraz z konstruktorem kopiowania - jeśli nie masz obecnie obiektu tej klasy, w przeciwnym razie powinieneś użyć operatora przypisania kopii. Chyba że klasa jest trywialna; to nie ma znaczenia, którego z nich używasz. To samo dotyczy zniszczenia obiektu. Niepoprawne obchodzenie się z tym w przypadku nietrywialnych klas może najprawdopodobniej prowadzić do dziwnego zachowania, aw niektórych sytuacjach może nawet powodować niezdefiniowane zachowanie .
HelloGoodbye,

@ Andrei-NiculaePetre Właściwie uważam ten przykład w artykule na Wikipedii za bardzo zły, ponieważ zakłada się, że nie istnieje żaden wcześniejszy obiekt i że trzeba go skonstruować. Nie dzieje się tak, jeśli U::operator=został właśnie wywołany.
HelloGoodbye,

Odpowiedzi:


364

Umieszczenie nowego pozwala skonstruować obiekt w pamięci, który jest już przydzielony.

Możesz to zrobić w celu optymalizacji, gdy musisz zbudować wiele instancji obiektu, a szybsze jest nie przydzielanie pamięci za każdym razem, gdy potrzebujesz nowej instancji. Zamiast tego bardziej wydajne może być wykonanie pojedynczej alokacji dla części pamięci, która może pomieścić wiele obiektów, nawet jeśli nie chcesz korzystać z nich jednocześnie.

DevX daje dobry przykład :

Standardowy C ++ obsługuje także umieszczanie nowego operatora, który konstruuje obiekt na wstępnie przydzielonym buforze. Jest to przydatne podczas budowania puli pamięci, modułu wyrzucania elementów bezużytecznych lub po prostu, gdy najważniejsze są bezpieczeństwo wydajności i wyjątków (nie ma niebezpieczeństwa niepowodzenia alokacji, ponieważ pamięć została już przydzielona, ​​a budowa obiektu na wstępnie przydzielonym buforze zajmuje mniej czasu) :

char *buf  = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi");    // placement new
string *q = new string("hi");          // ordinary heap allocation

Możesz także mieć pewność, że nie wystąpi błąd alokacji w określonej części kodu krytycznego (na przykład w kodzie wykonywanym przez rozrusznik serca). W takim przypadku należy wcześniej przydzielić pamięć, a następnie użyć umieszczenia nowego w sekcji krytycznej.

Dezalokacja w miejscu docelowym nowość

Nie należy cofać przydziału każdego obiektu używającego bufora pamięci. Zamiast tego powinieneś usunąć [] tylko oryginalny bufor. Będziesz musiał ręcznie wywołać destruktory swoich klas. Dobra sugestia na ten temat znajduje się w FAQ Stroustrup na temat: Czy istnieje „usunięcie miejsca docelowego” ?


54
Nie jest przestarzałe, ponieważ potrzebujesz tej funkcji, aby skutecznie implementować obiekty kontenerowe (takie jak wektor). Jeśli nie budujesz własnego kontenera, nie musisz korzystać z tej funkcji.
Martin York,

26
Bardzo ważne jest również, aby pamiętać #include <memory>, w przeciwnym razie możesz
napotkać

22
Ściśle mówiąc, wywołanie delete[]pierwotnego charbufora jest niezdefiniowane . Korzystanie z umieszczania newzakończyło żywotność oryginalnych charobiektów poprzez ponowne użycie ich przechowywania. Jeśli teraz wywołasz delete[] bufdynamiczny typ wskazanego obiektu (ów), nie będzie on już odpowiadał ich typowi statycznemu, więc zachowałeś niezdefiniowane zachowanie. Bardziej spójne jest użycie operator new/ operator deleteprzydzielenie surowej pamięci przeznaczonej do użycia przez umieszczenie new.
CB Bailey

31
Zdecydowanie pominąłbym korzystanie ze sterty w rozruszniku serca :-)
Eli Bendersky

15
@RamonZarazua Błędny nagłówek, to #include <new>.
bit2shift 26.04.16

63

Używamy go z niestandardowymi pulami pamięci. Tylko szkic:

class Pool {
public:
    Pool() { /* implementation details irrelevant */ };
    virtual ~Pool() { /* ditto */ };

    virtual void *allocate(size_t);
    virtual void deallocate(void *);

    static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};

class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };

// elsewhere...

void *pnew_new(size_t size)
{
   return Pool::misc_pool()->allocate(size);
}

void *pnew_new(size_t size, Pool *pool_p)
{
   if (!pool_p) {
      return Pool::misc_pool()->allocate(size);
   }
   else {
      return pool_p->allocate(size);
   }
}

void pnew_delete(void *p)
{
   Pool *hp = Pool::find_pool(p);
   // note: if p == 0, then Pool::find_pool(p) will return 0.
   if (hp) {
      hp->deallocate(p);
   }
}

// elsewhere...

class Obj {
public:
   // misc ctors, dtors, etc.

   // just a sampling of new/del operators
   void *operator new(size_t s)             { return pnew_new(s); }
   void *operator new(size_t s, Pool *hp)   { return pnew_new(s, hp); }
   void operator delete(void *dp)           { pnew_delete(dp); }
   void operator delete(void *dp, Pool*)    { pnew_delete(dp); }

   void *operator new[](size_t s)           { return pnew_new(s); }
   void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
   void operator delete[](void *dp)         { pnew_delete(dp); }
   void operator delete[](void *dp, Pool*)  { pnew_delete(dp); }
};

// elsewhere...

ClusterPool *cp = new ClusterPool(arg1, arg2, ...);

Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);

Teraz możesz grupować obiekty razem w jednej arenie pamięci, wybierać alokator, który jest bardzo szybki, ale nie dokonuje dealokacji, używa mapowania pamięci i innych semantycznych, które chcesz narzucić, wybierając pulę i przekazując ją jako argument do umiejscowienia obiektu nowy operator.


1
Tak. Jesteśmy dość sprytni, ale pytanie to jest nie na temat.
Don Wakefield

2
@jdkoftinoff Czy masz link do faktycznej próbki kodu? wydaje mi się dość interesująca!
Victor

@DonWakefield Jak poradzisz sobie z wyrównaniem w tej puli? Czy nie powinieneś allocate()gdzieś przekazać wyrównania ?
Mikhail Vasilyev

1
@MikhailVasilyev, w prawdziwej implementacji oczywiście byś sobie z tym poradził. Tylko przykładowy kod.
Don Wakefield,

co jeśli miejsce docelowe jest nieprawidłowym adresem, powiedzmy 0x0?
Charlie

51

Jest to przydatne, jeśli chcesz oddzielić alokację od inicjalizacji. STL używa nowego umieszczania do tworzenia elementów kontenera.


35

Użyłem go w programowaniu w czasie rzeczywistym. Zazwyczaj nie chcemy przeprowadzać żadnego dynamicznego przydzielania (lub zwalniania) po uruchomieniu systemu, ponieważ nie ma gwarancji, jak długo to zajmie.

To, co mogę zrobić, to wstępnie przydzielić dużą część pamięci (wystarczająco dużą, aby pomieścić dowolną ilość tego, czego może wymagać klasa). Następnie, kiedy wymyślę w czasie wykonywania, jak konstruować te rzeczy, nowe umieszczanie może być użyte do konstruowania obiektów dokładnie tam, gdzie chcę. Wiem, że użyłem go w jednej sytuacji, aby pomóc stworzyć heterogeniczny okrągły bufor .

Z pewnością nie jest to dla osób o słabym sercu, ale właśnie dlatego sprawiają, że składnia jest dla tego dość osobliwa.


Cześć TED, czy mógłbyś podzielić się więcej na temat posiadanego rozwiązania? Myślę o wstępnie przydzielonym rozwiązaniu, ale nie osiągnąłem dużych postępów. Z góry dziękuję!
Wietnam

1
Cóż, faktyczny hetrogeniczny cykliczny kod bufora był naprawdę trudny do zrobienia. Nowy wygląd wygląda trochę ponuro, ale dla porównania nie był to żaden problem.
TED

26

Użyłem go do konstruowania obiektów przydzielonych na stosie za pomocą metody przydzielania ().

bezwstydna wtyczka: blogowałem o tym tutaj .


ciekawy artykuł, ale nie jestem pewien, czy rozumiem zaletę korzystania z tego boost::array. Czy możesz to trochę rozwinąć?
GrahamS,

boost :: array wymaga, aby rozmiar tablicy był stałą czasową kompilacji. Nie ma tego ograniczenia.
Ferruccio,

2
@Ferruccio To jest całkiem fajne, zauważyłem, że twoje makro jest nieco niebezpieczne, a mianowicie rozmiar może być ekscesem. Jeśli na przykład zostanie przekazane x + 1, rozwinąłbyś go do sizeof (typ) * x + 1, co byłoby niepoprawne. Musisz zawęzić makro, aby było bezpieczniejsze.
Benj

Używanie z aloką wydaje mi się niebezpieczne, jeśli zostanie zgłoszony wyjątek, ponieważ musisz wywołać destruktory na wszystkich swoich obiektach.
CashCow

14

Head Geek: BINGO! Masz to całkowicie - właśnie do tego jest idealny. W wielu środowiskach osadzonych ograniczenia zewnętrzne i / lub ogólny scenariusz użycia zmusza programistę do oddzielenia alokacji obiektu od jego inicjalizacji. Zgrupowane razem, C ++ nazywa to „wystąpieniem”; ale ilekroć działanie konstruktora musi być jawnie wywoływane BEZ alokacji dynamicznej lub automatycznej, sposobem na to jest umieszczenie nowego. Jest to również doskonały sposób na zlokalizowanie globalnego obiektu C ++, który jest przypięty do adresu komponentu sprzętowego (we / wy mapowane w pamięci) lub dowolnego obiektu statycznego, który z jakiegokolwiek powodu musi znajdować się pod stałym adresem.


12

Użyłem go do stworzenia klasy Variant (tj. Obiektu, który może reprezentować pojedynczą wartość, która może być jedną z wielu różnych typów).

Jeśli wszystkie typy wartości obsługiwane przez klasę Variant są typami POD (np. Int, float, double, bool), wówczas wystarczające jest połączenie oznaczone w stylu C, ale jeśli chcesz, aby niektóre typy wartości były obiektami C ++ ( np. std :: string), funkcja unii C nie zadziała, ponieważ typy danych inne niż POD mogą nie zostać zadeklarowane jako część unii.

Zamiast tego przydzielam tablicę bajtów, która jest wystarczająco duża (np. Sizeof (the_largest_data_type_I_support)) i używam umieszczania new, aby zainicjować odpowiedni obiekt C ++ w tym obszarze, gdy Variant jest ustawiony na przechowywanie wartości tego typu. (I miejsce docelowe usuwa się wcześniej przy zmianie innego typu danych niż POD), oczywiście)


Emm, typy danych inne niż POD mogą być deklarowane w ramach unii, o ile podasz ctor związkowy - i hej - ten ctor prawdopodobnie użyłby umiejscowienianew do zainicjowania swojej podklasy innej niż POD. Ref: stackoverflow.com/a/33289972/2757035 Ponowne wynalezienie tego koła za pomocą arbitralnie dużej tablicy bajtów jest imponującą akrobacją, ale wydaje się całkowicie niepotrzebne, więc co przegapiłem? :)
underscore_d

6
Przegapiłeś wszystkie wersje C ++ przed C ++ 11, które w wielu przypadkach nadal muszą być obsługiwane. :)
Jeremy Friesner

10

Nowe umieszczanie jest również bardzo przydatne podczas szeregowania (powiedzmy przy pomocy boost :: seriization). W ciągu 10 lat c ++ to dopiero drugi przypadek, dla którego potrzebowałem nowego miejsca (trzeci, jeśli uwzględnisz wywiady :)).


9

Jest to również przydatne, gdy chcesz ponownie zainicjować struktury globalne lub statycznie przydzielone.

Stary sposób memset()języka C służył do ustawiania wszystkich elementów na 0. Nie można tego zrobić w C ++ z powodu vtables i niestandardowych konstruktorów obiektów.

Czasami używam następujących

 static Mystruct m;

 for(...)  {
     // re-initialize the structure. Note the use of placement new
     // and the extra parenthesis after Mystruct to force initialization.
     new (&m) Mystruct();

     // do-some work that modifies m's content.
 }

1
Czy nie musiałbyś dokonać odpowiedniego zniszczenia przed ponownym zainicjowaniem go w ten sposób?
Head Geek

[Edytowane pod kątem pisowni] Zwykle - robisz. Ale czasami, gdy wiesz, że klasa nie przydziela pamięci ani innych zasobów (lub zwolniłeś je na zewnątrz - na przykład, gdy korzystasz z pul pamięci), możesz użyć tej techniki. Gwarantuje to, że wskaźniki v-table nie zostaną nadpisane. - nimrodm 16 godzin temu
nimrodm

1
Nawet w C użycie ustawienia wszystkich bitów na 0 jest gwarantowane tylko w celu uzyskania reprezentacji 0 dla typów całkowych, a nie innych typów (wskaźnik zerowy może mieć niezerową reprezentację).
ciekawy,

@curiousguy - dla typów prymitywnych masz rację (sprawi, że program będzie przewidywalny, co jest zaletą, jeśli chodzi o debugowanie). Jednak typy danych C ++ będą miały uruchomionego konstruktora (w miejscu) i zostaną poprawnie zainicjowane.
nimrodm

9

W rzeczywistości jest to rodzaj implementacji jakiejkolwiek struktury danych, która przydziela więcej pamięci niż jest minimalnie wymagana dla liczby wstawianych elementów (tj. Wszystko inne niż struktura połączona, która przydziela jeden węzeł na raz).

Przystawki pojemniki podoba unordered_map, vectoralbo deque. Wszystkie one alokują więcej pamięci, niż jest to wymagane minimalnie dla elementów, które dotychczas wstawiłeś, aby uniknąć konieczności alokacji sterty dla każdego pojedynczego wstawienia. Użyjmy vectorjako najprostszego przykładu.

Kiedy to zrobisz:

vector<Foo> vec;

// Allocate memory for a thousand Foos:
vec.reserve(1000);

... to nie konstruuje tysiąc Foos. Po prostu alokuje / rezerwuje dla nich pamięć. Gdyby vectornie używał nowego umieszczania tutaj, byłoby to domyślne budowanie w Fooscałym miejscu, a także zmuszanie do wywoływania ich destrukterów nawet dla elementów, których nigdy nawet nie wstawiłeś.

Alokacja! = Budowa, uwolnienie! = Zniszczenie

Mówiąc ogólnie, aby zaimplementować wiele struktur danych takich jak powyżej, nie można traktować przydzielania pamięci i konstruowania elementów jako jednej niepodzielnej rzeczy, podobnie jak nie można traktować zwalniania pamięci i niszczenia elementów jako jednej niepodzielnej rzeczy.

Pomiędzy tymi pomysłami musi istnieć rozdział, aby uniknąć niepotrzebnego wywoływania konstruktorów i destruktorów niepotrzebnie w lewo i w prawo, i dlatego standardowa biblioteka oddziela ideę std::allocator (która nie konstruuje ani nie niszczy elementów, gdy alokuje / zwalnia pamięć *) z dala od kontenery, które go używają, które ręcznie konstruują elementy za pomocą umieszczania nowych i ręcznie niszczą elementy za pomocą jawnych wywołań destruktorów.

  • Nienawidzę projektu std::allocator ale to inny temat, o którym będę się nie drażnić. :-RE

Tak czy inaczej, często go używam, ponieważ napisałem wiele ogólnie dostępnych kontenerów C ++ zgodnych ze standardami, których nie można zbudować w stosunku do istniejących. Wśród nich jest mała implementacja wektorowa, którą zbudowałem kilkadziesiąt lat temu, aby uniknąć alokacji sterty w typowych przypadkach, oraz trie wydajne pod względem pamięci (nie alokuje jednego węzła na raz). W obu przypadkach nie mogłem tak naprawdę zaimplementować ich przy użyciu istniejących kontenerów, więc musiałem użyć, placement newaby uniknąć zbędnego wywoływania konstruktorów i destruktorów na rzeczach niepotrzebnych z lewej i prawej strony.

Oczywiście, jeśli kiedykolwiek pracujesz z niestandardowymi alokatorami, aby alokować obiekty indywidualnie, jak na przykład bezpłatna lista, to na ogół powinieneś również użyć placement newtakiego, jak ten (podstawowy przykład, który nie przeszkadza w bezpieczeństwie wyjątków lub RAII):

Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);

8

Przydaje się, jeśli budujesz jądro - gdzie umieścisz kod jądra, który czytasz z dysku lub z pagetable? Musisz wiedzieć, gdzie skoczyć.

Lub w innych, bardzo rzadkich okolicznościach, na przykład gdy masz dużo przydzielonego pokoju i chcesz umieścić kilka struktur za sobą. Można je w ten sposób spakować bez potrzeby użycia operatora offsetof (). Istnieją jednak inne sztuczki.

Wierzę również, że niektóre implementacje STL wykorzystują nowe umieszczanie, takie jak std :: vector. W ten sposób przydzielają miejsce na 2 elementy i nie muszą zawsze zmieniać położenia.


Zmniejszenie przydziałów pamięci jest jednym z głównych powodów, aby z niej korzystać, a także „sztuczki”, takie jak ładowanie obiektów z dysku
lefticus

Nie znam żadnych jąder napisanych w C ++; większość jąder jest napisanych prosto C.
Adam Rosenfield,

8
System operacyjny, z którym poznałem podstawy systemu operacyjnego, jest napisany w C ++: sweb.sourceforge.net
mstrobl

8

Myślę, że nie została to podkreślona żadna odpowiedź, ale kolejny dobry przykład i zastosowanie nowego miejsca docelowego jest zmniejszenie fragmentacji pamięci (poprzez użycie pul pamięci). Jest to szczególnie przydatne w systemach wbudowanych io wysokiej dostępności. W tym ostatnim przypadku jest to szczególnie ważne, ponieważ dla systemu, który musi działać 24/365 dni, bardzo ważne jest, aby nie było fragmentacji. Ten problem nie ma nic wspólnego z wyciekiem pamięci.

Nawet jeśli używana jest bardzo dobra implementacja malloc (lub podobna funkcja zarządzania pamięcią), bardzo trudno jest poradzić sobie z fragmentacją przez długi czas. W pewnym momencie, jeśli nie będziesz sprytnie zarządzać wywołaniami rezerwacji / zwalniania pamięci, możesz skończyć z wieloma małymi lukami, które są trudne do ponownego wykorzystania (przypisz nowe rezerwacje). Tak więc jednym z rozwiązań zastosowanych w tym przypadku jest użycie puli pamięci do przydzielenia pamięci podręcznej dla obiektów aplikacji. Za każdym razem, gdy potrzebujesz pamięci dla jakiegoś obiektu, po prostu używasz nowego położenia, aby utworzyć nowy obiekt w już zarezerwowanej pamięci.

W ten sposób po uruchomieniu aplikacji masz już zarezerwowaną całą potrzebną pamięć. Wszystkie nowe rezerwacje / wydania pamięci trafiają do przydzielonych pul (możesz mieć kilka pul, po jednej dla każdej innej klasy obiektu). W tym przypadku nie dochodzi do fragmentacji pamięci, ponieważ nie będzie braków, a system może działać bardzo długo (lata) bez cierpienia z powodu fragmentacji.

Widziałem to w praktyce specjalnie dla VxWorks RTOS, ponieważ jego domyślny system alokacji pamięci bardzo cierpi z powodu fragmentacji. Tak więc alokacja pamięci za pomocą standardowej metody new / malloc była zasadniczo zabroniona w projekcie. Wszystkie rezerwacje pamięci powinny być kierowane do dedykowanej puli pamięci.


8

Jest używany przez, std::vector<>ponieważ std::vector<>zwykle przydziela więcej pamięci niż objectsw vector<>.


7

Użyłem go do przechowywania obiektów z plikami mapowanymi w pamięci.
Konkretnym przykładem była baza danych obrazów, która przetwarzała bardzo dużą liczbę dużych obrazów (więcej niż mogło zmieścić się w pamięci).


7

Widziałem to jako niewielki hack wydajnościowy dla wskaźnika „typu dynamicznego” (w sekcji „Pod maską”):

Ale oto podstępna sztuczka, której użyłem, aby uzyskać szybką wydajność dla małych typów: jeśli utrzymywana wartość może zmieścić się w pustce *, tak naprawdę nie zawracam sobie głowy przydzielaniem nowego obiektu, zmuszam go do samego wskaźnika za pomocą umieszczania nowego .


Co oznacza , że utrzymywana wartość może zmieścić się w pustce * ? Zawsze możliwe jest przypisanie dowolnego typu wskaźnika do void *. Czy możesz nam podać przykład?
anurag86,

@ anurag86: Na moim 64-bitowym komputerze void*zajmuje 8 bajtów. Trochę głupio jest wskazywać ośmiobajtowy void*bajt bool. Ale całkowicie możliwe jest nałożenie boolna void*, podobnie jak union { bool b; void* v }. Potrzebujesz sposobu, aby wiedzieć, że rzecz, którą nazywasz a, void*to tak naprawdę bool(lub a short, lub a float, itp.). Artykuł, do którego podłączyłem, opisuje, jak to zrobić. Aby odpowiedzieć na pierwotne pytanie, umiejscowienie newjest funkcją używaną do tworzenia bool(lub innego typu), gdzie void*oczekiwany jest (rzutowania są używane do późniejszego pobrania / modyfikacji wartości).
Max Lybbert,

@ anurag86: To nie to samo, ale możesz być zainteresowany otagowanymi wskaźnikami ( en.wikipedia.org/wiki/Tagged_pointer ).
Max Lybbert,

6

Użyłem go do tworzenia obiektów opartych na pamięci zawierającej wiadomości otrzymane z sieci.


5

Ogólnie rzecz biorąc, nowe miejsce docelowe służy do pozbycia się kosztów alokacji „normalnego nowego”.

Innym scenariuszem, w którym go użyłem, jest miejsce, w którym chciałem mieć dostęp do wskaźnika do obiektu, który jeszcze nie został zbudowany, aby wdrożyć singleton na dokument.



4

Jedyne miejsce, na które natknąłem się na to, to pojemniki, które przydzielają ciągły bufor, a następnie wypełniają go odpowiednio do potrzeb obiektami. Jak wspomniano, std :: vector może to zrobić i wiem, że niektóre wersje MFC CArray i / lub CList to zrobiły (ponieważ to tam po raz pierwszy go uruchomiłem). Metoda nadmiernej alokacji buforów jest bardzo przydatną optymalizacją, a nowe umieszczanie jest prawie jedynym sposobem konstruowania obiektów w tym scenariuszu. Czasami jest również używany do konstruowania obiektów w blokach pamięci przydzielonych poza bezpośrednim kodem.

Użyłem go w podobnej pojemności, chociaż nie pojawia się często. Jest to jednak przydatne narzędzie do zestawu narzędzi C ++.


4

Silniki skryptów mogą używać go w interfejsie natywnym do przydzielania obiektów natywnych ze skryptów. Przykłady: patrz Angelscript (www.angelcode.com/angelscript).


3

Zobacz plik fp.h w projekcie xll na stronie http://xll.codeplex.com Rozwiązuje problem „nieuzasadnionej chumminess z kompilatorem” dla tablic, które lubią nosić ze sobą swoje wymiary.

typedef struct _FP
{
    unsigned short int rows;
    unsigned short int columns;
    double array[1];        /* Actually, array[rows][columns] */
} FP;

2

Oto użycie zabójcy dla konstruktora lokalnego C ++: wyrównanie do linii pamięci podręcznej, a także innych potęg 2 granic. Oto mój ultraszybki algorytm wyrównywania wskaźnika do dowolnej potęgi 2 granic z 5 lub mniej instrukcjami pojedynczego cyklu :

/* Quickly aligns the given pointer to a power of two boundary IN BYTES.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param boundary_byte_count The boundary byte count that must be an even
power of 2.
@warning Function does not check if the boundary is a power of 2! */
template <typename T = char>
inline T* AlignUp(void* pointer, uintptr_t boundary_byte_count) {
  uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
  value += (((~value) + 1) & (boundary_byte_count - 1));
  return reinterpret_cast<T*>(value);
}

struct Foo { Foo () {} };
char buffer[sizeof (Foo) + 64];
Foo* foo = new (AlignUp<Foo> (buffer, 64)) Foo ();

Czy to nie tylko wywołuje uśmiech na twojej twarzy (:-). I ♥♥♥ C ++ 1x

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.