{"id":244,"date":"2011-11-05T21:10:46","date_gmt":"2011-11-05T21:10:46","guid":{"rendered":"http:\/\/piatkosia.k4be.pl\/wordpress\/?p=244"},"modified":"2011-12-30T02:13:57","modified_gmt":"2011-12-30T02:13:57","slug":"operator-new-kilka-slow-na-jego-temat","status":"publish","type":"post","link":"https:\/\/piatkosia.k4be.pl\/wordpress\/2011\/11\/05\/operator-new-kilka-slow-na-jego-temat\/","title":{"rendered":"Operator new &#8211; kilka s\u0142\u00f3w na jego temat"},"content":{"rendered":"<div>\n<p><strong>Operator new &#8211; kilka s\u0142\u00f3w na jego temat<\/strong><\/p>\n<p>Operator new, jak zapewne wszyscy wiedz\u0105 s\u0142u\u017cy w j\u0119zykach obiektowych\/\u00a0zorientowanych obiektowo do kreacji nowych obiekt\u00f3w. W przypadku j\u0119zyka C++ u\u017cywamy go r\u00f3wnie\u017c do tworzenia zmiennych dynamicznych typ\u00f3w wbudowanych, na kt\u00f3re pokazuje wska\u017anik. Zmienna dynamiczna bowiem sama w sobie nazwy niestety nie ma. Ale ma adres kt\u00f3ry mo\u017cna przypisa\u0107 do wska\u017anika, a to nam w zupe\u0142no\u015bci wystarczy do pos\u0142ugiwania si\u0119 ni\u0105. Zmienna dynamiczna nie podlega prawom zakresu wa\u017cno\u015bci (co innego wska\u017anik, kt\u00f3ry na t\u0105 zmienn\u0105 pokazuje). Je\u017celi w danym zakresie wa\u017cno\u015bci mamy dost\u0119p do przynajmniej jednego wska\u017anika na zaalokowany obszar pami\u0119ci, b\u0119dzie od dost\u0119pny. Tracimy zmienn\u0105 dopiero po u\u017cyciu bli\u017aniaczego operatora delete (kt\u00f3rym nie b\u0119dziemy si\u0119 zajmowa\u0107 w tym artykule).\u00a0 Wybaczcie mi to mas\u0142o ma\u015blane.<br \/>\nZmienne tworzone dynamicznie w j\u0119zyku c++ nie s\u0105 zerowane, nale\u017cy o tym pami\u0119ta\u0107.<br \/>\nArtyku\u0142 ten stanowi vademecum pos\u0142ugiwania si\u0119 tym operatorem na (prawie) wszystkie mo\u017cliwe sposoby. Ograniczymy si\u0119 przy tym do j\u0119zyka c++. Ma on tak\u017ce za zadanie rozbudzi\u0107 wasz\u0105 ciekawo\u015b\u0107 do zbadania tego, co nieznane.<br \/>\n<!--more--><br \/>\nTak ten operator wygl\u0105da w deklaracji visual studio (skopiowa\u0142am z msdn)<\/p>\n<pre class=\"brush:cpp\">void* operator new(\r\n   std::size_t _Count\r\n) throw(bad_alloc);\r\nvoid* operator new(\r\n   std::size_t _Count,\r\n   const std::nothrow_t&amp;\r\n) throw( );\r\nvoid* operator new(\r\n   std::size_t _Count,\r\n   void* _Ptr\r\n) throw( );<\/pre>\n<p>TIP: Aby korzysta\u0107 z innej formy ni\u017c podstawowa (co\u015b* wsk = new co\u015b) potrzebujemy doda\u0107 do naszego pliku #include &lt;new&gt; .<br \/>\nJak nietrudno zauwa\u017cy\u0107, funkcja zwraca wska\u017anik na void. Nie nie nie, nie wska\u017anik na nic, ale wska\u017anik, jaki mo\u017cna przypisa\u0107 do dowolnego typu.<\/p>\n<p>Przyjmowane parametry:<br \/>\n_Count: ile bajt\u00f3w ma zaalokowa\u0107 (pobierze go z typu prawego operandu. Jak dasz new int, we\u017amie sizeof(int). Przecie\u017c to zwraca size_t, nie?)<br \/>\n_Ptr: wska\u017anik, jaki ma zwr\u00f3ci\u0107 (przy realokacji)<br \/>\nMo\u017ce te\u017c nie przyjmowa\u0107 \u017cadnych parametr\u00f3w (w nawiasie) a wtedy domy\u015blnie zaalokuje tyle bajt\u00f3w, ile ma typ zmiennej, oraz umie\u015bci j\u0105 w wybranym przez siebie miejscu pami\u0119ci.<\/p>\n<p><strong>Przyk\u0142ad u\u017cycia tych form<\/strong><\/p>\n<p>1 forma:<\/p>\n<pre class=\"brush:cpp\">JakasKlasa* wskObj = new Jakas Klasa;<\/pre>\n<p>Jest to forma, jak\u0105 znamy od dawna i u\u017cywamy cz\u0119sto. Do w\u0142a\u015bnie zadeklarowanego wska\u017anika przypisany jest adres nowego, w\u0142a\u015bnie przypisanego obiektu. No ok, ale we\u017amy sobie tak\u0105 sytuacj\u0119. Umieszczamy t\u0105 instrukcj\u0119 w jakiej\u015b p\u0119tli niesko\u0144czonej (lub sko\u0144czonej, ale obiekty klasy JakasKlasa s\u0105 spore) i w kt\u00f3rym\u015b miejscu zabrak\u0142o pami\u0119ci. Oczywi\u015bcie, do wska\u017anika przypisuje si\u0119 warto\u015b\u0107 null, ALE to nie wszystko co si\u0119 stanie. Zosta\u0142 r\u00f3wnie\u017c wyrzucony wyj\u0105tek bad_alloc, a co za tym idzie, aplikacja si\u0119 wykrzaczy\u0142a, jak to zwykle bywa, gdy zostanie wyrzucony wyj\u0105tek, kt\u00f3rego p\u00f3\u017aniej nie obs\u0142u\u017cyli\u015bmy. Co zrobi\u0107, je\u015bli chcemy, by ten wyj\u0105tek nie zosta\u0142 wyrzucony? Ano u\u017cywamy drugiej formy operatora. W naszym przypadku b\u0119dzie to<\/p>\n<pre class=\"brush:cpp\">JakasKlasa* wskObj = new( nothrow ) Jakas Klasa;<\/pre>\n<p>(Je\u017celi nie dali\u015bmy<\/p>\n<pre class=\"brush:cpp\">using namespace std;<\/pre>\n<p>to musimy napisa\u0107<\/p>\n<pre class=\"brush:cpp\">std::nothrow<\/pre>\n<p>) I ju\u017c \u017caden wyj\u0105tek nie zostanie wyrzucony. W przypadku b\u0142\u0119du alokacji po prostu wska\u017anik b\u0119dzie wskazywa\u0142 na nulla. I nic wi\u0119cej si\u0119 nie stanie. Nale\u017cy jednak w kodzie dalej sprawdzi\u0107, czy wska\u017anik wskazuje na null i odpowiednio zareagowa\u0107 na t\u0105 informacj\u0119.<br \/>\n<strong>A gdzie si\u0119 podzia\u0142 realloc?<\/strong><br \/>\nPewnego dnia m\u00f3j znajomy (Tak Mariusz, Ty z p\u00f3\u0142tora roku temu) powiedzia\u0142, \u017ce tr\u00f3jca \u015bwi\u0119ta malloc, free, realloc znana z j\u0119zyka C jest lepsza, ni\u017c para new, delete. Uargumentowa\u0142 to:<br \/>\na) brakiem mo\u017cliwo\u015bci alokacji pami\u0119ci o rozmiarze jakim on sobie wymy\u015bli (mo\u017cna oszuka\u0107 tworz\u0105c tablic\u0119 bajt\u00f3w o liczbie element\u00f3w odpowiadaj\u0105cej ilo\u015bci pami\u0119ci i zrzutowa\u0107)<br \/>\nb) brakiem mo\u017cliwo\u015bci realokacji pami\u0119ci.<br \/>\nCo prawda nie ma trzeciej funkcji, jak realloc, ale nie jest ona potrzebna. Tutaj mo\u017cemy r\u00f3wnie\u017c u\u017cy\u0107 operatora new. Przyk\u0142ad takiej realokacji zaczerpni\u0119ty z MSDNa:<\/p>\n<pre class=\"brush:cpp\">char x[sizeof( MyClass )];\r\n   MyClass* fPtr2 = new( &amp;x[0] ) MyClass;<\/pre>\n<p>Czyli po skr\u00f3cie wska\u017anik = new(gdzie) typ;<\/p>\n<p>Oczywi\u015bcie gdzie to adres gdzie ma alokowa\u0107;) Jako void*, bo takimi adresami operuje new. Je\u015bli u\u017cywamy operatora pobrania adresu, zwr\u00f3ci nam jak trzeba. Jak wida\u0107 realokowany zas\u00f3b musi by\u0107 wcze\u015bniej zaalokowany jakkolwiek. W przeciwnym wypadku program si\u0119 wysypie z b\u0142\u0119dem segmentacji lub co gorsza, b\u0119dzie pisa\u0142 po innych danych programu. Tak\u017ce u\u017cywa\u0107 tego na prawd\u0119 z g\u0142ow\u0105. Kiedy to jest przydatne? Gdy chcemy zaalokowa\u0107 raz kawa\u0142 pami\u0119ci, a potem realokowa\u0107 jego fragment.<\/p>\n<p>TIP: je\u017celi nie u\u017cywamy referencji do jakiej\u015b zmiennej, a wska\u017anik chcemy poda\u0107 \u017cywcem (czy np. sta\u0142\u0105 dos\u0142own\u0105 adres podajemy) a program nie przyjmuje, zrzutujmy go na void* i przyjmie.<\/p>\n<p><strong>Nadawanie pocz\u0105tkowych warto\u015bci tworzonym obiektom<\/strong><\/p>\n<p>Tak, jest to mo\u017cliwe, ale wy\u0142\u0105cznie w przypadku prostych obiekt\u00f3w (np. nie przejdzie w tablicach, bo to typ pochodny). Ale tak\u017ce dla np. naszych klas. U\u017cywamy tego tak:<\/p>\n<pre class=\"brush:cpp\">int *wskliczba = new int(32);<\/pre>\n<p>Kto\u015b m\u00f3g\u0142by zada\u0107 pytanie, czy nie pomyli z argumentem realokacji. A niby czemu mia\u0142by tak zrobi\u0107? Przecie\u017c liczba w nawiasie jest przy int, a nie przy new. Czy to&#8230;. Tak, wywo\u0142anie yyy nazwijmy to sobie &#8222;konstruktora zmiennej typu wbudowanego&#8221;, jakkolwiek by to nie zabrzmia\u0142o.<\/p>\n<p>Oczywi\u015bcie dzia\u0142a bajer r\u00f3wnie\u017c dla pojedynczych, niestablicowanych obiekt\u00f3w naszych w\u0142asnych klas. We\u017amy sobie jak\u0105\u015b prost\u0105 klas\u0119<\/p>\n<pre class=\"brush:cpp\">class rekord{\r\n\tpublic:\r\n\t\tint lp;\r\n\t\tstring imie;\r\n\t\tstring nazwisko;\r\n\t\trekord(int numer, string im, string naz){  \/\/konstruktor\r\n\t\t\tlp = numer; imie = im; nazwisko = naz;\r\n\t}\r\n};<\/pre>\n<p>Klasa ta jest prymitywna, (taaa, \u0142amie wszystkie zasady enkapsulacji i w og\u00f3le) ale wystarczy do pokazania o co mi chodzi. \u017beby to zadzia\u0142a\u0142o, musimy mie\u0107 ofc publiczny konstruktor.<br \/>\nObiekt dynamiczny tworzymy sobie w taki oto spos\u00f3b<\/p>\n<pre class=\"brush:cpp\">rekord *wskrekord = new rekord(1,\"Jan\",\"Kowalski\");<\/pre>\n<p>podaj\u0105c sobie pola po kolei, jak w konstruktorze zadeklarowali\u015bmy.<br \/>\nCo do omini\u0119cia problem\u00f3w z tablicami, mo\u017cemy przecie\u017c po new co\u015btam da\u0107 ={konstruktor(), konstruktor(),&#8230;} i b\u0119dzie ok, bo na li\u015bcie inicjalizator\u00f3w b\u0119d\u0105 ju\u017c tworzone pojedyncze elementy tablicy.<br \/>\n<strong>New a tablice<\/strong><br \/>\nW literaturze cz\u0119sto znajduje si\u0119 oznaczenie, \u017ce operatorem do tworzenia dynamicznych tablic jest<\/p>\n<pre class=\"brush:cpp\">new[]<\/pre>\n<p>. Mimo \u017ce zapisuje si\u0119 nadal jako new. Jednak w parze za nim idzie ju\u017c jawnie zapisany<\/p>\n<pre class=\"brush:cpp\">delete[] zmienna;<\/pre>\n<p>i pewnie by u\u0142atwi\u0107 ludziom pami\u0119tanie o [] tak pisz\u0105. (jakby\u015bcie deletn\u0119li bez nawiasu kwadratowego, usuniecie tylko 0 element tablicy i b\u0119dziecie mieli leaka wielko\u015bci sizeof(typ) * ilosc_elementow-1).<\/p>\n<p>Przyk\u0142ad stworzenia dynamicznej tablicy typu double<\/p>\n<pre class=\"brush:cpp\">double *wsk = new double[1024];<\/pre>\n<p>co jest oczywi\u015bcie r\u00f3wnoznaczne parze instrukcji:<\/p>\n<pre class=\"brush:cpp\">double *wsk;\r\nwsk = new double[1024];<\/pre>\n<p>Rozmiaru nie musimy podawa\u0107 w ten spos\u00f3b. Nie musi to by\u0107 litera\u0142. Mo\u017ce to by\u0107 r\u00f3wnie dobrze sta\u0142a (const, czy nawet #define).\u00a0 Je\u017celi alokujemy wiele wymiar\u00f3w, to pierwszy z prawej mo\u017ce by\u0107 zmienn\u0105, lub mo\u017ce nie istnie\u0107.<\/p>\n<p><strong>Wy\u0142apanie wyj\u0105tku<\/strong><\/p>\n<p>Wr\u00f3\u0107my, do sytuacji, gdzie tworzyli\u015bmy sobie sobie co\u015b sporego w bloku try<\/p>\n<pre class=\"brush:cpp\">float* wsk;\r\nint licznik;\r\ntry{\r\n\t\/\/jaki\u015b kod\r\n\twhile(true){\r\n\t\twsk = new float[10000000000];\r\n\t\tlicznik++;\r\n\t\t\/\/mo\u017ce robi\u0107 jeszcze co\u015btam\r\n\t}\r\n}<\/pre>\n<p>To nam wykrzaczy aplikacj\u0119, pr\u0119dzej czy p\u00f3\u017aniej. \u017beby jednak b\u0142\u0105d alokacji nie przerwa\u0142 nam dzia\u0142ania programu i wy\u015bwietlenia okienka, (w przypadku windows), \u017ce &#8222;system windows napotka\u0142 b\u0142\u0105d&#8221; a w szczeg\u00f3\u0142ach da\u0142 informacj\u0119 o nieobs\u0142u\u017conym wyj\u0105tku, musimy go z\u0142apa\u0107 i obs\u0142u\u017cy\u0107. Na przyk\u0142ad pisz\u0105c gdzie\u015b dalej w kodzie tak:<\/p>\n<pre class=\"brush:cpp\">catch (std::bad_alloc){\r\n\r\n cout&lt;&lt;\"\\nStworzy\u0142em ci ju\u017c \"&lt;&lt;licznik&lt;&lt;\"tablic. Wi\u0119cej nie upchn\u0119. Dokup ramu.\";\r\n    }<\/pre>\n<p>No c\u00f3\u017c, nic nie wykombinujemy wi\u0119cej:)<\/p>\n<p><strong>Prze\u0142adowanie operatora new- czyli tworzymy obiekty po swojemu<\/strong><\/p>\n<p>Pierwsza rzecz: operator new musi by\u0107 statycznym sk\u0142adnikiem klasy. Wiadomo, jest u\u017cywany, gdy obiektu jeszcze nie ma:). Nie mo\u017ce si\u0119 kumplowa\u0107. Musi by\u0107 sk\u0142adnikiem. Je\u017celi prze\u0142adowujemy globalny operator new (mo\u017cna), musimy pami\u0119ta\u0107, \u017ce nie odwo\u0142amy si\u0119 do globalnego new, kt\u00f3ry by\u0142 wcze\u015bniej. Nie ma tu jak operatora zakresu u\u017cy\u0107. Strumieni standardowych te\u017c sobie nie pou\u017cywamy, bo by newa u\u017cy\u0142y (\u017cegnajcie cin, cout, cerr, strumienie plikowe itd ;( ). Osobi\u015bcie opr\u00f3cz laborek z c++ i egzaminu nigdy nie musia\u0142am ich u\u017cywa\u0107 (podobno u\u017cywaj\u0105 ich przy optymalizacji alokowania), wi\u0119c przepisz\u0119 kod z symfonii c++<\/p>\n<pre class=\"brush:cpp\">#include &lt;stdio.h&gt;\r\nvoid* operator new(size_t  rozmiar){\r\n\tvoid *wsk = malloc(rozmiar);\r\n\tprintf(\"Globalnym kreuj\u0119 pojedynczy obiekt %x\\n\", wsk);\r\n\treturn wsk;\r\n}<\/pre>\n<p>Pami\u0119ta\u0107 nale\u017cy, \u017ce jak dali\u015bmy new, musimy te\u017c stworzy\u0107 new[], delete oraz delete[].<br \/>\nDla klasy jak chcemy operator prze\u0142adowa\u0107, to tak samo je\u015bli jeste\u015bmy jeszcze w klasie, a je\u017celi ju\u017c poza klas\u0105, piszemy<\/p>\n<pre class=\"brush:cpp\">void* klasa::operator new(size_t rozmiar){\r\n\t\/\/jaki\u015b kod, w tym u\u017cycie new dla p\u00f3l lub malloca\r\n\treturn wsk; \/\/lub return (new obiekt_jaki\u015btam);\r\n}<\/pre>\n<p>Czyli og\u00f3lnie rzecz bior\u0105c, pami\u0119ta\u0107 nale\u017cy, \u017ce new ZAWSZE musi zwraca\u0107 void*, a pobiera\u0107 zmienn\u0105 typu size_t.<\/p>\n<p><strong>Podsumowanie<\/strong><\/p>\n<p>Wow, my\u015bla\u0142am \u017ce b\u0119dzie troch\u0119 kr\u00f3tszy:). Zebra\u0142am do kupy wszystko, co wiem o tym operatorze i co dokumentacja o nim m\u00f3wi w jednym miejscu, by nie trzeba by\u0142o skaka\u0107. Mam nadziej\u0119 \u017ce uda\u0142o mi si\u0119 zaciekawi\u0107 czytelnik\u00f3w tym operatorem, oraz, \u017ce zach\u0119ci\u0142am tych bardziej ciekawskich do poeksperymentowania z kodem.<\/p>\n<p><em><strong>Dla tych, kt\u00f3rym ju\u017c \u015bwie\u017cbi\u0105 r\u0119ce do kombinowania:<\/strong><\/em><\/p>\n<ul>\n<li><em>Sprawd\u017acie jak si\u0119 zachowa, jak spr\u00f3bujecie reallocowa\u0107 zerowy adres, adres poza osi\u0105giem aplikacji, adres sta\u0142y u\u017cywany przez OS, nulla etc i podzielcie si\u0119 spostrze\u017ceniami.<\/em><\/li>\n<li><em>Pami\u0119taj\u0105c o tym, \u017ce zmienne, kt\u00f3re tworzycie operatorem new nie czyszcz\u0105 pami\u0119ci, zastan\u00f3wcie si\u0119, czy mo\u017cna tym sposobem odczytywa\u0107 fragmenty nieczyszczonej pami\u0119ci RAM (nie nadaj\u0105c pocz\u0105tkowych warto\u015bci) i np. zrzuca\u0107 tak\u0105 zawarto\u015b\u0107 do pliku, kt\u00f3ry potem podejrzycie hexdumpem :p &lt;evil&gt; TIP: zaalokuj bardzo du\u017c\u0105 tablic\u0119 char\u00f3w i zrzu\u0107 j\u0105 do pliku tekstowego, lub binarnego jak wolisz:))<\/em><\/li>\n<li><em>Jak si\u0119 zachowa kompilator, w momencie w kt\u00f3rym spr\u00f3bujecie stworzy\u0107 tablic\u0119 o indeksie ze zwyk\u0142ej sta\u0142ej (const), ale pomi\u0119dzy jej deklaracj\u0105 (z przypisaniem warto\u015bci ofc) a utworzeniem tablicy zmie\u0144cie warto\u015b\u0107 tego, co u\u017cyjecie w indeksie na wi\u0119ksz\u0105 o kilka\/kilkana\u015bcie oczek(rzutuj\u0105c metod\u0105 const cast). Po czym spr\u00f3bujcie odwo\u0142a\u0107 si\u0119 do indeksu poprzednia_warto\u015b\u0107+2<br \/>\nTIP: nie pr\u00f3bujcie robi\u0107 tego w 1 (od lewej) wymiarze tablicy wielowymiarowej, tam mo\u017ce sta\u0107 zar\u00f3wno zmienna, jak i nawet mo\u017ce nic nie sta\u0107<\/em><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Bibliografia:<\/p>\n<ul>\n<li>ms-help:\/\/MS.MSDNQTR.v90.en\/dv_vcstdlib\/html\/2476d0f9-59df-485c-981e-ba9f7ee83507.htm (wersja offline MSDN Library for Visual Studio 2008 SP1)<\/li>\n<li>Symfonia c++ standard, Jerzy Gr\u0119bosz, Krak\u00f3w 2008<\/li>\n<li>W\u0142asne eksperymenty:)<\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Operator new &#8211; kilka s\u0142\u00f3w na jego temat Operator new, jak zapewne wszyscy wiedz\u0105 s\u0142u\u017cy w j\u0119zykach obiektowych\/\u00a0zorientowanych obiektowo do kreacji nowych obiekt\u00f3w. W przypadku<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/piatkosia.k4be.pl\/wordpress\/2011\/11\/05\/operator-new-kilka-slow-na-jego-temat\/\">Lecim dalej<span class=\"screen-reader-text\">Operator new &#8211; kilka s\u0142\u00f3w na jego temat<\/span> <i class=\"fas fa-angle-right\"><\/i><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,50,49,24],"tags":[92,211,93,90,91],"class_list":["post-244","post","type-post","status-publish","format-standard","hentry","category-bez-kategorii","category-c","category-programowanie","category-techniczne","tag-alokacja","tag-c","tag-ciekawostki","tag-new","tag-operatory","entry"],"_links":{"self":[{"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/posts\/244","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/comments?post=244"}],"version-history":[{"count":20,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/posts\/244\/revisions"}],"predecessor-version":[{"id":328,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/posts\/244\/revisions\/328"}],"wp:attachment":[{"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/media?parent=244"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/categories?post=244"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/piatkosia.k4be.pl\/wordpress\/wp-json\/wp\/v2\/tags?post=244"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}