V této části budou zveřejněny vaše dotazy i s naší odpovědí. Tak si budou moci odpověď přečíst všichni zájemci a ne pouze tazatel. Dotazy ke Kurzu C/C++ posílejte na email mého kolegy: mrandrew@atlas.cz a dotazy ke kurzu o DirectX posílejte na můj email: jiri.formanek@centrum.cz.
Pro větší přehlednost jsem rozdělil tuto sekci do více částí:
Obecné otázky
Dotazy k C++
Dotazy k DirectX
Krátké příklady
Je lepší Microsoft Visual C++ nebo
Borland C++?
Osobně bych Vám doporučoval Visual C++, ale je to také věc vkusu.
Pokud nemáte nic proti Microsoftu tak jděte do Visual C++, v opačném
případě do Borlandu, který sice není špatný, ale podle mého názoru se
VC++ nevyrovná. Microsoft má určitě lepší podporu nebo spíš reklamu:)
Lze změnit nastavení již
rozpracovaného projektu na statické linkovaní MFC, aniž bych musel
projekt znovu zavádět?
Samozřejmě. V menu Project zvolte položku Settings. Tam
hned na první kartě najdete způsob linkovaní. Dávejte si ale pozor na
to, že konfigurace projektu je rozdělena pro Release a Debug
režim. Musíte ji nastavit nejlépe pro oba režimy, ale hlavně pro Release,
protože tuto verzi pak distribuujete.
Mám nějakou třídu (například CMujEdit zděděný od
CEdit) a mám ji zobrazenu v panelu ClassView. Občas se stane
následující věc, které nerozumím: přidám třídě pomocí položky
kontextoveho menu "Add Windows Message Handler" nějakou oblužnou
funkci, pak se mi nelíbí co dělá a já ji chci odstranit. Pokud ji smažu
ručně přímo v kódu, stejně jako odkaz na ní v mapě zpráv a v
hlavičkovém
souboru, je vše v pořádku. Pokud však kliknu v ClassView u příslušné
metody na Delete a potvrdím, že se mají odkazy smazat a tělo metody
zakomentovat, pak se někdy stane, že mi celá třída zmizí z ClassView
a zpět se mi objeví teprve poté, co pomoci ClassWizardu znovu přidám
smazanou metodu. Když ji následně vymažu "ručně", je vše v pořádku. Čím
to je způsobeno?
ClassView není dokonalé. Pokud vymažete kousek klíčového slova "class",
tak třída z ClassView zmizí
a to je v pořádku, protože vývojové prostředí tuto třídu nenajde, ale
občas se stane, že třída zmizí při normalní operaci s třídou jako je
například přidávání a mazání funkcí. Toto je chyba samotného vývojového
prostředí a ani nejnovější SP (Service Pack) tuto chybu neopravuje.
Pokud se vám stane, že třída
zmizí, pokuste se vratit krok, po kterém třída zmizela a posléze ho
proveďte znovu. Pokud nejde krok vrátit, musíte buďto čekat až se
jednoho krásneho dne třída znovu objeví nebo by mělo stačit vyjmout a
zase přidat hlavičkový i implementační soubor, ve kterém je třída.
Zkuste restartovat
VC++ i cely počítač a poslední možnost je, že vymažete soubory s
příponami .ncb a .opt v adresáři vašeho projektu. To jsou soubory, ve
kterých je uložena právě struktura ClassView a vy tak přinutíte VC++,
aby ji znovu vytvořilo.
Existuje nějaké freeware vývojové prostředí pro jazyk
C/C++ a kde jej mohu získat?
Ano, existuje Dev-C++ a stáhnout si ho můžete na této adrese: www.bloodshed.nu. Nové Dev-C++ by
mělo být k dispozici na ChipCD.
Co je to ukazatel?
Ukazatel je druh proměnné, jejíž hodnota je adresou
v paměti. Pomoci ukazatelů se dá dělat mnoho věcí, které však při
špatném použití mohou vest k chybám v programu. Ukazatele se např.
používají pro dynamickou alokaci paměti (tedy přímo za běhu programu).
Předáni parametru pomoci ukazatele má za následek, ze se kopíruje jen
4bajtova adresa v paměti a nemusí se kopírovat cely objekt (pole,
třída), který může být velice rozsáhlý. Ukazatele se také používají k
předávání parametru odkazem, kdy můžeme uvnitř funkce změnit předaný
parametr, což jinak není možné.
Jak mohu objektu CStatic změnit ikonu za běhu
programu?
Třída CStatic obsahuje
členskou funkci
SetBitmap(), kterou lze změnit zobrazovaný
obrázek.
Chtěl by jsem se Vás zeptat jak se dá
v novém VC++ .NET napsat starý dobrý .exe program?
Všechny typy projektů
z verze 6.0 najdete i ve verzi .NET. Problém je, že některé volby jsou
trochu zastrčené. Když vytváříte nový projekt ve Visual C++.NET, můžete
si vybrat pouze Win32 Application a teprve na dialogu Wizardu
vybíráte typ aplikace (.exe, .dll, konzole). Pokud vytváříte MFC
aplikaci, máte na výběr z několika možností (zde je to stejné jako ve
verzi 6.0): MFC EXE a MFC DLL.
Jak převedu řetězec na číslo?
V knihovnách C++
existuje řada funkcí určené ke konverzi řetězců na číselné typy a
naopak. Funkce atoi() převede vstupní
řetězec typu char na celočíselnou hodnotu
typu int. Funkce rozezná číslo se
znaménkem, i když jsou před samotným číslem mezery tj. řetězec
" -9824" je
převeden na číslo -9824 atd. Kromě této funkce můžete použít
atof() pro převod na typ
double a atol() na převod na typ
long. Pokud použijete funkci
atof() nemusí být v řetězci před desetinnou tečkou žádné číslo
tj. ".124" se převede na číslo 0.124. Dále můžete použít formát
"1,05e-34", který se převede na hodnotu redukované Planckovy konstanty
1,05.10^-34. Bližší informace najdete v nápovědě MSDN.
Pokud používáte třídu CString, lze použít
výše uvedené funkce konverzí na char.
Jaký je rozdíl mezi cout a printf()?
Ve VC++ je možné
používat obojí, cout i
printf(). Osobně mám radši
printf(),
protože umožňuje lepší formátování – kratší a přehlednější, ale jinak
je to zcela na Vás.
Mohl byste mi vysvětlit rozdíl mezi
ukazatelem a normální proměnnou? Proč některé funkce chtějí jako
parametry ukazatele a jiné normální proměnnou?
Všimněte si, že např. funkce
SelectObject()
bere jako parametr ukazatel na objekt. Když se funkci předává parametr
hodnotou (to znamená jako normální proměnnou bez hvězdičky), udělá se
kopie předávaného objektu na stacku. Když
se jedná o objekt, musí se udělat kompletní kopie, všechny proměnné!
Představte si, že objekt bude mít 20 proměnných a navíc může obsahovat
další objekty – to jistě uznáte, že je značně neefektivní, když pak
tuto kopii stejně zahodíte. Mnohem lepší je, když předáte pouze
ukazatel. Takže obecně platí, že když předáváte objekty je lepší
používat ukazatele, i když nechcete objekt uvnitř funkce měnit. Naopak
u běžných typů se předává hodnota, samozřejmě pokud chcete hodnotu
proměnné ve funkci měnit, musíte předat buď ukazatel nebo referenci.
Je lepší Java nebo C++, a v čem?
Nedá se říci, jestli je lepší Java nebo C++. Za prvé, každý jazyk má
své přednosti a své nevýhody. Za druhé se jedná o dva různé typy
programovacích jazyků. I když je syntaxe velice podobná, C++ je jazyk
kompilovaný tzn. že program se jednou přeloží a pak už se jen spouští.
Ale Java je jazyk interpretovaný tzn. že se kód překládá při každém
spuštění. Proto také C++ nemá svůj skriptovací jazyk (známe JavaScript,
VBScript, ale C++Script nikoliv:). Takže by se dalo říci, že program v
C++ bude vždy rychlejší než stejný program v Javě, ale také to záleží
na mnoha dalších okolnostech.
Používá se v C++ v deklaraci proměnné slovo var, co
to var znamená?
Nepoužívá. Klíčové slovo var se používá v
Pascalu k deklaraci proměnných a k předávání parametrů funkcím odkazem.
Jinak
var je zřejmě od slova variable,
což je proměnná.
Na základě vašeho kurzu jsem se začal učit
programovací jazyk C, za použití knihy "Učebnice jazyka C 1.". Mám
však problém: vždy když zkompiluji svůj program, ten se zpustí,ale po
provedení všech příkazů se ihned ukončí a já nejsem schopen
zkontrolovat správnost výpočtů na obrazovce. Po zkušenostech s Pascalem
jsem hledal v knize nějaký příkaz podobný READKEY, ale nic takového
jsem nenašel. Má jazyk C nějaký podobný příkaz? Nebo je chyba v
mém kompileru?
Chyba to není. Program prostě skončí a výstupní okno se zavře. Pokud je
výpočet programu rychlý, člověk postřehne jen probliknutí. Úplný
ekvivalent funkci READKEY z Pascalu v C
nenajdete, ale můžete použít funkci getchar(),
která funguje podobně (navíc vrací ordinální číslo stisknutého znaku).
Abyste mohli tuto funkci použít, musíte vložit hlavičkový soubor
stdio.h.
Bude plynulý přechod z C++ na C#?
Snad ano. C# má být něco mezi Visual Basicem a Visual C++, takže
prostředí
bude zřejmě "přátelštější", ale stále se bude používat jazyk C++. Nyní
s odstupem času můžu tento dotaz doplnit. O C# se říká, že je spojením
toho nejlepšího z jazyků C++, Visual Basic a Java. Skutečně, když se
podíváte na syntaxi, tak je velice podobná Javě a přesto nejde o
interpretovaný jazyk. C# je zcela objektově orientovaný jazyk určený
pro novou platformu .NET. Pro toho, kdo se učí C++ bude přechod na C#
velice rychlý.
Jak kreslit přímo do
povrchu DD?
Objekt povrchu má metodu Lock(), která vrací ukazatel na vlastní pole bufferu!
Podrobné informace najdete v nápovedě MSDN. Poté co zavoláte Lock() můžete pomocí
uvedeného ukazatele přistupovat přímo do paměti. Nakonec je třeba
zavolat metodu Unlock().
Chtěl bych se zeptat, jakým způsobem
naprogramovat v DirectX třeba 2D morphing - plynulý přechod mezi dvěmi
bitmapami. Tipuju že se kreslí do nějaké offscreen plochy. Zajímá mě
jak se tam kreslí? Pokud to lze přímým přístupem jako při zápisu do
paměti, tak pak se chci zeptat jak a jaký formát má ta off-screen
plocha.
Do jiných povrchů (i do offscreen) kreslíte úplně stejně jako například
do back bufferu. Jen jako cílový buffer vyberete váš offscreen buffer.
Plynulý přechod mě napadá jen pomocí aplha blendingu coz je ovšem v DD
dost velký problem (vlastní DD ho ani nepodporuje). Našel jsem nějakou
knihovnu kde to šlo, ale nebylo to zcela ideálni (dost pomalé). Mnohem
jednoduší je v tomto případě použít Direct3D. V DD byste leda mohl
použít nějaký algoritmus, který aplikujete na každý pixel výsledného
povrchu (jako zdrojove pixely budou sloužit pixely z tech dvou bitmap a
nějaký integer, jako stupeň přechodu). Tento způsob bude ale velmi
pomaly:(
Právě jsem objevil vaši učebnici DirectX, a líbí se mi, ale
já programuji v Borland C+ + Builder a tak jsem se chtěl zeptat, jestli
by mi uvedené příklady fungovali a celkově o kompatibilitě kurzu.
Dále jsem se hctěl zeptat, jaký je rozdíl mezi Visual C++ a Borland C++
Builder a který z nich je lepší a taky používanější.
Obávám se, ze v mých příkladech využívám MFC a tam kde tomu tak je,
bude áas Borland bezradný:( Jinak DD jako takové by snad měl nějak
podporovat, ale nejsem si jistý neboť osobne programuji v MSVC, kde má
DX velkou podporu v podobe SDK a jinych aplikací. Nevím to přesně, ale
myslím, že se více používá MSVC než Borland, neboť jak jsem říkal má
mnohem větši podporu (například MFC). Borland má sice také vlastní
knihovny pro Windows, ale urcite nebudou obsahovat tolik věcí. Můj
názor je: Borland na programovani v céčku ano, ale na programovani ve
Windows je lepší MSVC. Kurz C++ by měl být kompatibilni s vaším
prostředím.
Co je to sprite?
Odpověď byste jistě našli v některém kurzu DirectX. Sprite je z
anglického překladu něco jako duch:-) Sprite je vlastně
vykreslovaný obrázek. Sprite je většinou vytvořen z bitmapy, kterou
načtete z externího souboru. Pokud budete mít pohybující se letadlo,
právě letadlo bude sprite. Za sprite můžeme považovat například
tlačítko menu nebo kurzor myši.
Jak
zakomponovat zvuk do stávajících projektu DirectX?
Asi nejlepším řešením je použít další komponentu DirectX a tou je
DirectMusic. Tato komponenta umožňuje, jak přehrávání hudby, tak i
samostatných zvuků. DirectMusic částečně nahrazuje komponentu
DirectSound, který je rovněž k dispozici, ale řekl bych, že s
DirectMusic se lépe pracuje. Mám v plánu udělat malý kurz DirectMusic,
kde vysvětlím základy, abyste mohli svou aplikaci ozvučit. Nebude to
ovšem nic podrobného.V příštím díle DirectX se budu tomuto tématu
věnovat.
Poznámka k jiným verzím Visual C++ a DirectX SDK.
Praktickým zkoušením jsem zjistil, že s novým DX SDK 8.0 pracuje
pouze Visual C++ 6.0 a vyšší. Na nižších verzích nelze projekty z kurzů
zkompilovat kvůli zřejmé nekompatibilitě. Proto pokud vlastníte verzi
4.0 nebo 5.0 nemůžete používat DirectX 8.0. DirectX SDK 8.0 vyšlo na
ChipCD vydání 11/01.
Existuje česky psaná kniha o DirectX?
Bohužel jsem o takové publikaci neslyšel. V angličtině je takových knih
několik (viz. www.microsoft.com).
Bude nejaky seriál o DirectShow?
Nebude. Po DirectDraw bych chtěl pokračovat komponentou DirectInput a
pak ještě nevím, ale DirectShow to nebude.
Je
možné vytváření off-screen surfaces větších nežli je velikost primary
surface?
Podle
dokumentace k DirectDraw je to od verze 5.0 možné, ovšem za
následujících podmínek. Pro umístění do video paměti karty je nutné
ověřit pomocí metody rozhraní DirectDraw
GetCaps() flag
DDCAPS2_WIDESURFACES, který se nachází v členské
proměnné
dwCaps2 první struktury
DDCAPS předané metodě
GetCaps(). V případě vytvoření extrémně
velkého surface se však může stát, že se vytvoření neprovede, ačkoliv
ovladač výše uvedený flag uvádí. V tom případě je vrácen chybový kód
DDERR_INVALIDPARAMS. Tyto tzv. široké surfaces (wide
surfaces) jsou vždy podporovány v systémové paměti. Ve verzích starších
je velikost off-screen surface omezena velikostí primárního surface.
Víme. Stačí použít pointer na pointer na integer. Nejprve vytvoříte pole ukazatelů na integer, potom přes ukazatele v tomto poli vytvořite pole integeru. Srozumitelnější bude spíš ukázka:
#include
"stdafx.h"
#include <stdio.h>
int **pp_pole;
typedef int *PINT;
int
main(int argc, char* argv[])
{
pp_pole = NULL;
pp_pole = new PINT[10];
pp_pole[0] = new int[1];
pp_pole[1] = new int[2];
pp_pole[2] = new int[4];
pp_pole[0][0] = 5;
pp_pole[1][0] = 6;
pp_pole[1][1] = 7;
pp_pole[2][0] = 8;
// atd.
printf("V poli jsou:\n%u\n%u %u\n", pp_pole[0][0],
pp_pole[1][0], pp_pole[1][1]);
return 0;
}
1. Program pro převod čísla z desítkové soustavy do binární
1. Trocha teorie
Vezmeme desítkové číslo a stále ho celočíselně dělíme dvěma, zbytek po tomto dělení sepíšete a pak odspoda přečtete číslo vyjádřené ve dvojkové soustavě. Tedy například pro 157:
Operace | Výsledek | Zbytek |
157 : 2 | 78 | 1 |
78 : 2 | 39 | 0 |
39 : 2 | 19 | 1 |
19 : 2 | 9 | 1 |
9 : 2 | 4 | 1 |
4 : 2 | 2 | 0 |
2 : 2 | 1 | 0 |
1 : 2 | 0 | 1 |
A přečteme zespoda. Tedy: (157)dec = (10011101)bin.
2. Praxe
Proměnné:
length je délka binárního čísla, můžete
ale zvolit pevně.
retezec je již alokované pole znaku o
dostatečné velikosti (max. počet znaku bin. čísla + 1 znak na
ukončovací \0) dělíme bitovým posunem, kdy prostě zahodíme nejméně
významný bit a tím se číslo vydělí dvěma. Výsledný řetězec je vytvářen
od konce.
Vlastní program:
if(0 == cislo)
// osetrime 0
{
retezec[length - 2] = '0';
}
else
{
short index = length-1;
while(cislo > 1)
{
retezec[--index] =
(char)('0' + (cislo % 2));//
zbytek dame do spravne pozice
cislo >>= 1;
// vydelime dvema (bitovy
posun)
}
retezec[--index] = '1';
}