Portál AbcLinuxu, 30. dubna 2025 21:23
char& foo() { static char inner = 'a'; return a; }
Má tam být return inner;
foo() = 'b'; // přiřadí do proměnné inner 'b' cout << foo(); // vytiskne béčko Čili máme nádherný getter - setter, o kterém se žádnému javistovi ani nesníTo se od C liší jen ve hvězdičce před foo(), the C++ way by byla třída foo s operátorem=, operátorem<< pro ostream, konverzním operátorem na char a tak dál.
OK, neumím C, jaký je tedy rozdíl mezi polem a ukazatelem?
$ cat array.c #include <stdio.h> void f(char**p) { *p += 1; } int main() { char a[5] = "ahoj"; char *p = a; printf("a (%p): \"%s\"; p (%p): \"%s\"\n", a, a, p, p); f(&p); printf("a (%p): \"%s\"; p (%p): \"%s\"\n", a, a, p, p); f(&a); printf("a (%p): \"%s\"; p (%p): \"%s\"\n", a, a, p, p); return 0; } $ gcc -Wall array.c array.c: In function ‘main’: array.c:17: warning: passing argument 1 of ‘f’ from incompatible pointer type $ ./a.out a (0xbffc1d43): "ahoj"; p (0xbffc1d43): "ahoj" a (0xbffc1d43): "ahoj"; p (0xbffc1d44): "hoj" a (0xbffc1d43): "bhoj"; p (0xbffc1d44): "hoj"
char a[] = "Hello, world!"; char* p; p = a; /* OK */ a = p; /* nelze */ printf("sizeof(p) = %lu\n", sizeof(p)); printf("sizeof(a) = %lu\n", sizeof(a));
char a[] = "abcd"; char* b = "abcd"; a[0] = 'x'; // OK b[0] = 'y'; // viz nížeTohle je velice záludné a nebezpečné, protože to mnohé kompilátory neodhalí, a program někdy běží a jindy padá.
a
je normální pole vytvořené na zásobníku a inicializované příslušným řetězcem. Kdežto b
je ukazatel na konstantní řetězec (mělo by tedy správně být const char* b
). Kompilátory ale většinou dovolí (dokonce bez varování) použít nekonstantní pointer, a problémy se vyrojí až za běhu.
void funkce(char *parametr) { int i; for (i = 0; i < 256; i++) parametr[i] = 0; } void slozitejsi(char *pole[256]) { int i, j; for (i = 0; i < 256; i++) for (j = 0; j < 256; j++) pole[i][j] = 0; } int main() { char prvni_pole[256]; char *druhe_pole = (char *) malloc(256); char *treti_pole[256]; char **ctvrte_pole = (char **) malloc(256*sizeof(char *)); char pate_pole[256][256]; int i; for (i = 0; i < 256; i++) { treti_pole[i] = (char *) malloc(256); ctvrte_pole[i] = (char *) malloc(256); } /* toto se chová stejně */ funkce(prvni_pole); funkce(druhe_pole); /* tohle také */ slozitejsi(treti_pole); slozitejsi(ctvrte_pole); #if 0 /* tohle nejde */ slozitejsi(pate_pole); #endif return 0; }
Pole je uspořádana homogenní n-tice.Což vyžaduje kontrolu mezí, která jak známo v Céčku co? No není :) OK, pár příspěvků v této diskusi mne přesvědčilo o tom, že skutečně nelze zaměňovat pole a ukazatel. Ale na tom, že céčkovské pole není skutečné pole, trvám :)
Uspořádáná homogenní n-tice samozřejmě kontrolu mezí nutně nevyžaduje.Pětice, která mi je ochotna vrátit svůj šestý (nebo nedej bože mínus prvý) prvek podle mne není pětice, nýbrž hodně divný způsob nepřímého přístupu k paměti (ukazatel ;) ).
begin end.
blok. Tato věc mi v C chyběla.
static void __attribute__((constructor)) je_mi_smutno_po_begin_end_bloku() { /* něco si tu udělám */ }Ale není to moc podle normy... V C++ lze dělat i takovéto skopičiny. (Pro zajímavost přikládám celý program, který je inspirován tímto zadáním: http://www.fi.muni.cz/usr/jkucera/pb161/color.htm)
#include <iostream> #include <string> using namespace std; class ColorMap { private: ColorMap(); public: static ColorMap colormap; static const int colorcount; static char colors[]; static int inv_colors[256]; }; const int ColorMap::colorcount = 8; char ColorMap::colors[] = "KRGYBMCW"; int ColorMap::inv_colors[256]; ColorMap ColorMap::colormap; ColorMap::ColorMap() { int i; for (i = 0; i < 256; i++) inv_colors[i] = -1; for (i = 0; i < colorcount; i++) { inv_colors[static_cast<int>(colors[i])] = i; inv_colors[static_cast<int>(colors[i]) + 0x20] = i; } } class color { public: color(const char _color = 'K') : color_int(ColorMap::inv_colors[static_cast<int>(_color)]) { } color(const color& _color) : color_int(_color.color_int) { } color operator+ (const color& c) const { color newcolor(*this); newcolor.color_int |= c.color_int; return newcolor; } color operator* (const color& c) const { color newcolor(*this); newcolor.color_int &= c.color_int; return newcolor; } friend std::ostream& operator<< (std::ostream& s, const color c) { return s << ColorMap::colors[c.color_int]; } friend std::istream& operator>> (std::istream& s, color& c) { char _color; int color_int; while (s >> _color) { if ((color_int = ColorMap::inv_colors[static_cast<int>(_color)]) >= 0) { c.color_int = color_int; break; } cout << "Zadání barvy bylo chybné, zadejte, prosím, jedno z písmen " << ColorMap::colors << ": "; } return s; } int color_int; private: };
Třeba v STL by se pár užitečných příkladů našlo.
No, zas tak hrozné to snad není. Já v tom udělal asi 10k program a šlapalo to, když se člověk vyhnul šablonám. Jak by řekl klasik: Když víš, které konstrukce způsobují interní chybu překladače, můžeš v pohodě programovat.V mém případě tou konstrukcí bylo
if
. A programovat bez if
je velmi obtížné.
V BCB jsem narazil ještě na jedu krásnou chybu. Při debugování se po najetí myší nad proměnnou zobrazí v bublině její hodnota. Problém byl, když ta proměnná byl integer ve tvaru i++ nebo ++i. Pak to nejen zobrazilo hodnotu, ale rovnou to číslo i inkrementovalo. Takže po prvním najetí tam bylo 1, pak 2, 3 atd. Trvalo mi pár hodin, než jsem na to přišelNaštěstí jsem v té době ještě byl zvyklý debugger nepoužívat (jedine pro post-mortem). Bohužel jsem od té doby vyměkl.
Já vím, že se dočkám. Naštěstí existuje Microsoft, který se svým .NET prostředím dokázal to, co jsem si dlouhá léta myslel, že nebude možné. A to probudit Sun ze svého bahýnka sebechvály na Javu a ukázat, že Java má co zlepšovat. Takže jsem se třeba dočkal šablon, autoboxu a věřím, že se dočkám i operátorů.To se snad nikdy nestane. Bohatě stačí již udělané boty. Zvláště autoboxing+autounboxing .
==
. Přesněji to, že je přetížený a má dvě zcela rozdílné funkce: buď porovnává hodnoty (u primitivních typů) nebo reference (u objektů). Typická ukázka, kam vede přetěžování operátorů...
Správně by měly být operátory dva: jeden pro porovnání hodnoty, druhý pro porovnání referencí.
Viz Python a operátory '==' a operátor 'is'. Ale nejlepší řešení je runtime systém který zaručuje že totožné integery nebo totožné stringy mají identické reference, pak není druhý operátor opravdu potřeba.Jistě. Ale to se motáme pořád dokola - že primitivní typy do opravdu objektového jazyka nepatří. Nu, co se dá dělat... Mnohem víc mi ale v Javě chybí metody tříd.
Mnohem víc mi ale v Javě chybí metody tříd.Možná mi něco uniklo, ale mám li třeba třídu:
public class Trida { private final int a = 5; // metoda tridy public static int vratA() { return(this.a); } }Tak potom můžu volat metodu třídy takto:
int moje_a = Trida.vratA()
Tedy alespoň ve všech učebnicích Javy se tomuto volání říká volání metody třídy. Ale možná si měl na mysli něco jiného.
this
sem si nebyl jistej. Ale je pravda, že o metodách třídy i o metodách instance se zmiňuje pan Herout ve své (dle mne docela dobré) knize "Učebnice jazyka Java".
Ale o to jde. Java prostě zvolila špatnou cestu. Dokonce i Microsoft, když tvořil svůj .NET toto podstatně zdokonalil, i když také zůstal na půl cesty. U .NET existuje daleko více primitivních typů, ale všechno je objektem. I int je objektem a třeba konstrukce 123.ToString() je v C# platná. Stejně tak to funguje třeba ve Smalltalku, nebo v jiných, opravdu objektových jazycích.Ale prdlajs. C# převzalo primitivní typy z Javy, jenom tam dali autoboxing/autounboxing hned.. Pravda je, že je to daleko více zaintegrované a programátor o tom ani nemusí vědět... do jisté míry. Více viz například zde Uznávám že se to dá brát jako podstatné zdokonalení.
Pak je pro Javu autoboxing jenom dalším prvkem bordelu, který tam vnesla. Prostě Java se jenom topí v bordelu, který si způsobila tím, že neprohlásila promitivní typy za objekty, což by jí nic nestálo a bylo by to efektivnější řešení.No jo, dneska se to lehko mluví, když to víme... Ale jak jsem již zmínil, asi by to bylo rozhodnutí správné technicky, ale velmi špatné politicky.
V C++ neexistuje problém tohoto typu. V C++ je naprosto jasné z použití operátorů, zda porovnáváte hodnotu, nebo adresu. Operátory == a != v C++ vždycky porovnávají hodnotu.To je nějaký fór? Schválně jsem se podíval, a
==
i !=
by měly být přetížitelné.
Ale je to perfektní ukázka toho, co je na přetěžování operátoru špatného: i když se může zdát že je zcela bezpečné, stejně o něj člověk zakopne a rozbije si hubu. I kdyby to bylo za 10 let.
A co jste taktně ignoroval byla moje poznámka, že v C# neexistují nic než objekty. Tedy neexistuje, že by třeba int nebyl objekt, jako je tomu třeba v Javě.Pokud budeme hodnotové typy považovat za objekty - pak je to ovšem jen otázka terminologie. Nebo pokud přistoupíme na to, že boxing/ungoxing je zcela transparentní (což není). Ani s jedním nesouhlasím. Nelze situaci v C# srovnávat se Smalltalkem, CLOS či dalšími - sice se to tak napůl tváří, ale ne zcela.
No jo, dneska se to lehko mluví, když to víme... Ale jak jsem již zmínil, asi by to bylo rozhodnutí správné technicky, ale velmi špatné politicky. Vědělo se to už tehdy. Už tehdy existovaly objektové jazyky, které měly všechny typy jako objekty. Otázkou je, jak říkáte, co bylo průchodné politicky.Přesně to jsem měl na mysli.
A druhak váš důkaz není nic jiného, než co můžete provést s jakoukoli vlastností programovacího jazyka.Jednak nebyl žádný důkaz, jenom poznámka mimochodem. Druhak jsem jasně napsal, že měly být vytvořeny operátory dva, jak se ukázalo při rozšiřování. Přetěžování operátorů je velmi kontroverzní věc a v podstatě se nedá vyřešit (flamewar není řešení). Každopádně je zcela proti základní filozofii jazyka Java a nepatří tam. Přidáváním takovýchto featur by se z toho stal úplně jiný jazyk.
V javě je dokonce operátor klíčové slovoV Javě jsou (neimplementovaná) klíčová slova goto a const, ale o klíčovém slovu operator slyším prvně. Našel jsem akorát tohle: http://jira.opensymphony.com/browse/WF-281, na java.sun.com ani slovo.
třeba se dočkášDoufám že ne, úplně mi stačí statické importy a auto(un)boxing :) Jsem rád, že Anders Hejlsberg dělá na .NETu a ne na Javě :)
Cynická poznámka: nevzniklo takhle nějak céčko? :-)
#include <iostream> #include "pretizeni.h" using namespace std; main() { int a = 3; int b = 4; cout << (a + b) << endl; return 0; }I když to zní divně, tak vypíše
-1
. Je poněkud vylepšen hlavičkovým souborem pretizeni.h
:
#ifndef __PRETIZENI_H #define __PRETIZENI_H #include <ostream> class INT { public: INT() : hodnota(0) { } INT(int _hodnota) : hodnota(_hodnota) { } friend INT operator+ (INT&, INT&); friend INT operator- (INT&, INT&); friend std::ostream& operator<< (std::ostream&, INT&); friend std::ostream& operator<< (std::ostream&, INT ); private: int hodnota; }; INT operator+ (INT& a, INT& b) { return INT(a.hodnota-b.hodnota); } INT operator- (INT& a, INT& b) { return INT(a.hodnota+b.hodnota); } std::ostream& operator<< (std::ostream &s, INT& a) { return s << a.hodnota; } std::ostream& operator<< (std::ostream &s, INT a) { return s << a.hodnota; } #define int INT #endif
int hodnota
, si můžu inicializovat klidně i vevnitř a normálně, zatímco běžné objekty jdou jen před konstruktorem. Ještě můžu mít tu smůlu, že na sobě nějak závisí. To už pak je rovnou lepší použít ukazatele, new
a delete
.
P.S.: Ve výše uvedeném příkladu jsem ještě jaksi zapomněl v operátoru <<
použít const INT&
, takže jsem jej zavedl dvakrát. To mám z toho ponocování. Přetěžování operátorů je ještě celkem srozumitelné. Skutečné depresi jsem propadl až tehdy, když jsem objevil pointery na metody… :-)
#include <iostream> class T { public: T() {}; void ma(int x) { std::cout << "T::ma(" << x << ")\n"; } void mb(int x) { std::cout << "T::mb(" << x << ")\n"; } }; typedef void (T::*method_pointer)(int); void call(T* x, method_pointer m, int n) { (x->*m)(n); } int main() { T x; call(&x, &T::ma, 1); call(&x, &T::mb, 2); }
class Trida { public: void metoda(void); } instance; void Trida::metoda(void) { printf("To JE ale prasarna!\n"); } void (Trida::*funkce(void))(void) { return Trida::metoda; } void main() { void (Trida::*uk)() = funkce(); (instance.*uk)(); }
const dsql_nod* const* const end:-)
Zkoušeno pod BCB, MSVC, GCC. Možná je to dáno závazným způsobem vyhodnocení té konstrukce, nevím.Chtělo by to nahlédnout to K&R Bible. Jestli zítra (vlastně už dnes...) vzpomenu.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.