C ++ 'dan nefret eden çok fazla C programcısı görüyorum. Neyin iyi neyin kötü olduğunu anlamak biraz zaman aldı (yıllar). Bence bunu ifade etmenin en iyi yolu şudur:
Daha az kod, çalışma zamanı ek yükü yok, daha fazla güvenlik.
Ne kadar az kod yazarsak o kadar iyi. Bu, mükemmellik için çaba gösteren tüm mühendislerde hızla ortaya çıkmaktadır. Tek bir yerde bir hatayı düzeltirsiniz, çok değil - bir kez bir algoritmayı ifade eder ve birçok yerde tekrar kullanırsınız, vb. Yunanlıların bile eski Spartalılara kadar izlenen bir deyişi vardır: Bu konuda bilge olduğunu ". Ve işin aslı, doğru kullanıldığında , C ++, çalışma zamanı hızına mal olmadan, C'den çok daha güvenli (yani derleme zamanında daha fazla hata yakalama) yaparken, C'den çok daha az kodla ifade etmenize izin veriyor.
İşte oluşturucumdan basitleştirilmiş bir örnek : Bir üçgenin tarama çizgisi boyunca piksel değerlerini enterpolasyon yaparken. Bir X koordinatı x1'den başlamalı ve bir X koordinatı x2'ye (bir üçgenin soldan sağına) erişmeliyim. Ve her adım boyunca, geçtiğim her piksel boyunca değerleri enterpolasyon yapmak zorundayım.
Piksellere ulaşan ortam ışığını enterpolasyon ettiğimde:
typedef struct tagPixelDataAmbient {
int x;
float ambientLight;
} PixelDataAmbient;
...
// inner loop
currentPixel.ambientLight += dv;
Rengi enterpolasyon ettiğimde ("Gouraud" gölgelemesi olarak adlandırılır, "kırmızı", "yeşil" ve "mavi" alanların her pikselde bir adım değeri ile enterpolasyonu yapılır):
typedef struct tagPixelDataGouraud {
int x;
float red;
float green;
float blue; // The RGB color interpolated per pixel
} PixelDataGouraud;
...
// inner loop
currentPixel.red += dred;
currentPixel.green += dgreen;
currentPixel.blue += dblue;
"Phong" gölgelemesi yaptığımda, artık bir yoğunluğu (ambientLight) veya bir rengi (kırmızı / yeşil / mavi) enterpolasyon yapmıyorum - Normal bir vektörü (nx, ny, nz) enterpolasyonluyorum ve her adımda enterpolasyonlu normal vektöre dayanarak aydınlatma denklemini hesaplayın:
typedef struct tagPixelDataPhong {
int x;
float nX;
float nY;
float nZ; // The normal vector interpolated per pixel
} PixelDataPhong;
...
// inner loop
currentPixel.nX += dx;
currentPixel.nY += dy;
currentPixel.nZ += dz;
Şimdi, C programcılarının ilk içgüdüsü "heck, değerleri enterpolasyona sokan üç fonksiyon yaz ve bunları ayarlanan moda göre çağır" olacaktı. Her şeyden önce, bu benim bir tür problemim olduğu anlamına gelir - ne ile çalışıyorum? Piksellerim PixelDataAmbient mı? PixelDataGouraud? PixelDataPhong? Bekle, verimli C programcısı der ki, sendika kullan!
typedef union tagSuperPixel {
PixelDataAmbient a;
PixelDataGouraud g;
PixelDataPhong p;
} SuperPixel;
..ve o zaman, bir işlevin var ...
RasterizeTriangleScanline(
enum mode, // { ambient, gouraud, phong }
SuperPixel left,
SuperPixel right)
{
int i,j;
if (mode == ambient) {
// handle pixels as ambient...
int steps = right.a.x - left.a.x;
float dv = (right.a.ambientLight - left.a.ambientLight)/steps;
float currentIntensity = left.a.ambientLight;
for (i=left.a.x; i<right.a.x; i++) {
WorkOnPixelAmbient(i, dv);
currentIntensity+=dv;
}
} else if (mode == gouraud) {
// handle pixels as gouraud...
int steps = right.g.x - left.g.x;
float dred = (right.g.red - left.g.red)/steps;
float dgreen = (right.g.green - left.a.green)/steps;
float dblue = (right.g.blue - left.g.blue)/steps;
float currentRed = left.g.red;
float currentGreen = left.g.green;
float currentBlue = left.g.blue;
for (j=left.g.x; i<right.g.x; j++) {
WorkOnPixelGouraud(j, currentRed, currentBlue, currentGreen);
currentRed+=dred;
currentGreen+=dgreen;
currentBlue+=dblue;
}
...
Kaosun kaydığını hissediyor musun?
Her şeyden önce, kodumu çökertmek için gerekli olan tek bir yazım hatası var, çünkü derleyici beni işlevsellikten "Gouraud" bölümünde, ".a" ya erişmek için asla durmayacak. (ortam) değerleri. C tipi sistem tarafından yakalanmayan bir hata (derleme sırasında), çalışma zamanında ortaya çıkan ve hata ayıklama gerektiren bir hata anlamına gelir. Eğer ben erişen olduğumu fark ettiniz left.a.green
"dgreen" hesabında? Derleyici kesinlikle sana söylemedi.
Ardından, her yerde tekrarlama var - for
döngü oluşturma modları olduğu kadar çok sayıda orada, "sağa eksi sola adım adım bölünmüş" yapmaya devam ediyoruz. Çirkin ve hataya açık. "J" yi kullanmam gerektiğinde, Gouraud döngüsünde "i" kullanarak karşılaştırdığımı fark ettiniz mi? Derleyici yine sessiz.
Peki ya modlar için if / else / ladder? Üç hafta içinde yeni bir işleme modu eklersem ne olur? Yeni modu tüm kodumda "if mode ==" biçiminde kullanmayı hatırlayacağım mı?
Şimdi yukarıdaki çirkinliği, bu C ++ yapı seti ve bir şablon işlevi ile karşılaştırın:
struct CommonPixelData {
int x;
};
struct AmbientPixelData : CommonPixelData {
float ambientLight;
};
struct GouraudPixelData : CommonPixelData {
float red;
float green;
float blue; // The RGB color interpolated per pixel
};
struct PhongPixelData : CommonPixelData {
float nX;
float nY;
float nZ; // The normal vector interpolated per pixel
};
template <class PixelData>
RasterizeTriangleScanline(
PixelData left,
PixelData right)
{
PixelData interpolated = left;
PixelData step = right;
step -= left;
step /= int(right.x - left.x); // divide by pixel span
for(int i=left.x; i<right.x; i++) {
WorkOnPixel<PixelData>(interpolated);
interpolated += step;
}
}
Şimdi şuna bak. Artık bir sendika çorbası yapmıyoruz: her bir mod için özel türlerimiz var. Bir ortak sınıftan ( CommonPixelData
) miras alarak ortak şeylerini ("x" alanı) yeniden kullanırlar . Ve şablon derleyiciyi CREATE'e (yani, kod-oluşturmaya) kendimizi C olarak yazdığımız üç farklı işlevi, ancak aynı zamanda, türleri hakkında çok katı kılıyor!
Şablondaki döngümüz değişmez ve geçersiz alanlara erişemez - yaparsak derleyici havlayacaktır.
Şablon ortak çalışma (her seferinde "adım" artan döngü) gerçekleştirir ve bunu sadece çalışma zamanı hatalarına neden olmayacak şekilde yapabilir. Tipine göre enterpolasyon ( AmbientPixelData
, GouraudPixelData
, PhongPixelData
) ile yapılır operator+=()
biz yapılar içinde katacak - temelde dikte nasıl her tür enterpolasyonlanan.
Ve WorkOnPixel <T> ile ne yaptığımızı görüyor musunuz? Tür başına farklı işler yapmak istiyoruz? Biz sadece bir şablon uzmanlığı diyoruz:
void WorkOnPixel<AmbientPixelData>(AmbientPixelData& p)
{
// use the p.ambientLight field
}
void WorkOnPixel<GouraudPixelData>(GouraudPixelData& p)
{
// use the p.red/green/blue fields
}
Yani - çağırılacak işleve türe göre karar verilir. Derleme zamanında!
Tekrar silmek için:
- ortak parçaları tekrar kullanarak kodu en aza indiririz (şablon üzerinden),
- Derleyici her zaman kontrol edebilmemiz için çirkin hack kullanmıyoruz, katı tip bir sistem kullanıyoruz.
- ve hepsinden önemlisi: Yaptığımızın hiçbiri, herhangi bir çalışma zamanı etkisine sahip değil. Bu kod SADECE eşdeğer C kodu kadar hızlı çalışacaktır - aslında, C kodu çeşitli
WorkOnPixel
sürümleri çağırmak için işlev işaretçileri kullanıyorsa , C ++ kodu C'den daha HIZLI olacaktır, çünkü derleyici türe özgü WorkOnPixel
şablon uzmanlaşmasını satırda tutacaktır aramak!
Daha az kod, çalışma zamanı ek yükü yok, daha fazla güvenlik.
Bu, C ++ 'ın her şeyden önce ve tüm dillerin sonu olduğu anlamına mı geliyor? Tabii ki değil. Yine de takasları ölçmek zorundasın. Bilgisiz insanlar Bash / Perl / Python betiği yazmaları gerektiğinde C ++ 'ı kullanacaklar. Tetikleme mutlu C ++ yenileri, onları durdurup paketlemeden önce, sanal çoklu kalıtım içeren derin iç içe sınıflar yaratacaktır. Bunun gerekli olmadığını fark etmeden önce karmaşık Boost meta-programlama kullanacaklar . Onlar HALA kullanacak char*
, strcmp
ve makrolar yerine std::string
ve şablonları.
Fakat bu, kiminle çalıştığınızı ... izlemekten başka bir şey ifade etmez. Sizi yetersiz kullanıcılardan koruyacak bir dil yok (hayır, hatta Java değil).
Çalışmaya ve C ++ 'ı kullanmaya devam edin - fazla abartmayın.