C ++ ට වෛර කරන බොහෝ C ක්රමලේඛකයින් මට පෙනේ. හොඳ දේ සහ නරක කුමක්ද යන්න සෙමින් තේරුම් ගැනීමට මට සෑහෙන කාලයක් (අවුරුදු) ගත විය. වාක්ය ඛණ්ඩයට හොඳම ක්රමය මෙය යැයි මම සිතමි.
අඩු කේතයක්, ධාවන කාලයට ඉහළින්, වැඩි ආරක්ෂාවක් නොමැත.
අපි ලියන අඩු කේතය, වඩා හොඳය. විශිෂ්ටත්වය සඳහා වෙහෙසෙන සියලුම ඉංජිනේරුවන් තුළ මෙය ඉක්මනින් පැහැදිලි වේ. ඔබ එක් ස්ථානයක දෝෂයක් නිවැරදි කරයි, බොහෝ නොවේ - ඔබ එක් වරක් ඇල්ගොරිතමයක් ප්රකාශ කර එය බොහෝ ස්ථානවල නැවත භාවිතා කරයි. ග්රීකයන්ට පුරාණ ස්පාටන්වරුන් දක්වා දිවෙන කියමනක් ඇත: “අඩු වචන වලින් යමක් කීමට, අදහස් කරන්නේ ඔබ ඒ ගැන wise ානවන්ත බව ". කාරණයේ සත්යය නම්, නිවැරදිව භාවිතා කරන විට , C ++ ඔබට C ට වඩා අඩු කේතයකින්, ධාවන වේලාවකින් තොරව ප්රකාශ කිරීමට ඉඩ ලබා දෙන අතර, C ට වඩා ආරක්ෂිත (එනම් සම්පාදක වේලාවේදී වැඩි දෝෂ අල්ලා ගැනීම).
මෙන්න මගේ විදැහුම්කරුගේ සරල උදාහරණයකි : ත්රිකෝණයක ස්කෑන්ලයින් හරහා පික්සල් අගයන් අන්තර්ග්රහණය කිරීමේදී. මට X ඛණ්ඩාංක x1 සිට ආරම්භ කළ යුතු අතර X ඛණ්ඩාංක x2 වෙත ළඟා විය යුතුය (ත්රිකෝණයක වමේ සිට දකුණට). සෑම පියවරක් හරහාම, එක් එක් පික්සෙල් හරහා මා පසුකර යන විට, මට අගයන් අන්තර්ග්රහණය කළ යුතුය.
මම පික්සෙල් වෙත ළඟා වන සංසරණ ආලෝකය අන්තර්ග්රහණය කරන විට:
typedef struct tagPixelDataAmbient {
int x;
float ambientLight;
} PixelDataAmbient;
...
// inner loop
currentPixel.ambientLight += dv;
මම වර්ණය අන්තර්ග්රහණය කරන විට (“ගෞර ud ්” සෙවන ලෙස හැඳින්වේ, එහිදී “රතු”, “කොළ” සහ “නිල්” ක්ෂේත්ර එක් එක් පික්සෙල් එකක පියවර අගයකින් අන්තර්සම්බන්ධ වේ):
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;
මම "ෆොන්ග්" සෙවනෙහි විදැහුම් කරන විට, මම තවදුරටත් තීව්රතාවයක් (සංසරණ ආලෝකය) හෝ වර්ණයක් (රතු / කොළ / නිල්) අතරමැදි නොකරමි - මම සාමාන්ය දෛශිකයක් (nx, ny, nz) අන්තර්ග්රහණය කරමි. සෑම පියවරකදීම මට නැවත කළ යුතුය අන්තර් සම්බන්ධිත සාමාන්ය දෛශිකය මත පදනම්ව ආලෝකකරණ සමීකරණය ගණනය කරන්න:
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;
දැන්, සී ක්රමලේඛකයින්ගේ පළමු සහජ බුද්ධිය වනුයේ “හෙක්, අගයන් අන්තර්ග්රහණය කරන ශ්රිත තුනක් ලියන්න, සහ කට්ටල මාදිලිය අනුව ඒවා අමතන්න” යන්නයි. පළමුවෙන්ම, මෙයින් අදහස් කරන්නේ මට වර්ගයේ ගැටලුවක් ඇති බවයි - මා කුමක් සමඟ වැඩ කරන්නේද? මගේ පික්සල් පික්සල් ඩේටා ඇම්බියන්ට් ද? PixelDataGouraud? PixelDataPhong? ඔහ්, ඉන්න, කාර්යක්ෂම සී ක්රමලේඛකයා පවසන්නේ සමිතියක් භාවිතා කරන්න!
typedef union tagSuperPixel {
PixelDataAmbient a;
PixelDataGouraud g;
PixelDataPhong p;
} SuperPixel;
..එවිට ඔබට කාර්යයක් තිබේ ...
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;
}
...
අවුල් ජාලාව ලිස්සා යන බවක් ඔබට දැනෙනවාද?
පළමුවෙන්ම, මගේ කේතය බිඳවැටීමට අවශ්ය වන්නේ එක් යතුරු ලියනයකි, මන්දයත් සම්පාදකයා කිසි විටෙකත් “.a” වෙත ප්රවේශ වීම සඳහා ශ්රිතයේ “ගෞර ud ්” කොටසේ මාව නවත්වන්නේ නැත. (සංසරණ) අගයන්. සී වර්ගයේ පද්ධතියට හසු නොවූ දෝෂයක් (එනම්, සම්පාදනය අතරතුර), එයින් අදහස් වන්නේ ධාවන වේලාවේදී පෙන්නුම් කරන දෝෂයක් වන අතර එය නිදොස් කිරීම අවශ්ය වේ. left.a.green
"Dgreen" ගණනය කිරීමේදී මා ප්රවේශ වන බව ඔබ දුටුවාද ? සම්පාදකයා නිසැකවම ඔබට එසේ කීවේ නැත.
එවිට, සෑම තැනකම පුනරාවර්තනයක් ඇත - for
විදැහුම්කරණ ක්රම ඇති තරම් වාර ගණනක් ලූපය ඇත, අපි දිගටම කරන්නේ “දකුණු us ණ වමේ පියවරෙන් බෙදනු ලැබේ”. කැත සහ දෝෂ සහිත. මම "ජේ" භාවිතා කළ යුතුව තිබූ විට ගෝරාඩ් ලූපයේ "අයි" භාවිතා කිරීම සංසන්දනය කර ඇති බව ඔබ දුටුවාද? සම්පාදකයා නැවතත් නිහ is ය.
මාතයන් සඳහා if / else / ඉණිමඟ ගැන කුමක් කිව හැකිද? සති තුනකින් මම නව විදැහුම්කරණ මාදිලියක් එකතු කළහොත් කුමක් කළ යුතුද? මගේ සියලු කේතයන්හි "if mode ==" හි නව මාදිලිය හැසිරවීමට මට මතකද?
දැන් ඉහත කැතකම, මෙම C ++ ව්යුහයන් සහ අච්චු ශ්රිතය සමඟ සසඳන්න:
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;
}
}
දැන් මේ දෙස බලන්න. අපි තවදුරටත් යූනියන් වර්ගයේ සුප් එකක් සාදන්නේ නැත: එක් එක් මාදිලිය සඳහා අපට නිශ්චිත වර්ග තිබේ. මූලික පන්තියකින් ( CommonPixelData
) උරුම වීමෙන් ඔවුන් ඔවුන්ගේ පොදු දේවල් ("x" ක්ෂේත්රය) නැවත භාවිතා කරයි . තවද අච්චුව විසින් සම්පාදකයා නිර්මාණය කරයි (එනම් කේත උත්පාදනය) අප විසින් C හි ලියා ඇති විවිධ කාර්යයන් තුන, නමුත් ඒ සමඟම, වර්ග පිළිබඳව ඉතා දැඩි ලෙස සිටීම!
අච්චුවේ ඇති අපගේ ලූපයට අවලංගු කළ නොහැකි අතර අවලංගු ක්ෂේත්ර වෙත ප්රවේශ විය නොහැක - අප එසේ කළහොත් සම්පාදකයා බුරනු ඇත.
අච්චුව පොදු කාර්යය ඉටු කරයි (ලූපය, එක් එක් කාලයේදී "පියවරෙන්" වැඩි වේ), සහ එය කළ හැක්කේ හුදෙක් ධාවන කාල දෝෂ ඇති නොවන ආකාරයට ය. වර්ගය අනුව මෙම අන්තර්නිවේෂණය ( AmbientPixelData
, GouraudPixelData
, PhongPixelData
) සමග සිදු operator+=()
මූලික වශයෙන් පැනවීමට කරන - අපි structs එකතු කරන බව ආකාරය එක් එක් වර්ගය interpolated ඇත.
WorkOnPixel <T> සමඟ අප කළ දේ ඔබට පෙනේද? අපට එක් වර්ගයකට වෙනස් වැඩක් කිරීමට අවශ්යද? අපි හුදෙක් අච්චු විශේෂීකරණය ලෙස හඳුන්වමු:
void WorkOnPixel<AmbientPixelData>(AmbientPixelData& p)
{
// use the p.ambientLight field
}
void WorkOnPixel<GouraudPixelData>(GouraudPixelData& p)
{
// use the p.red/green/blue fields
}
එනම් - ඇමතීමේ කාර්යය, වර්ගය මත පදනම්ව තීරණය වේ. සම්පාදනය කරන වේලාවේදී!
එය නැවත මුද්රණය කිරීම සඳහා:
- අපි කේතය අවම කරමු (අච්චුව හරහා), පොදු කොටස් නැවත භාවිතා කිරීම,
- අපි කැත හැක්ස් භාවිතා නොකරමු, අපි දැඩි ආකාරයේ පද්ධතියක් තබා ගන්නෙමු, එවිට සම්පාදකයාට සෑම විටම අපව පරීක්ෂා කළ හැකිය.
- හොඳම දේ: අප කළ කිසිම දෙයකට කිසිදු ධාවන කාල බලපෑමක් නැත. මෙම කේතය සමාන සී කේතය තරම් වේගයෙන් ධාවනය වනු ඇත - ඇත්ත වශයෙන්ම, සී කේතය විවිධ
WorkOnPixel
අනුවාදයන් ඇමතීමට ශ්රිත දර්ශක භාවිතා කරන්නේ නම් , සී ++ කේතය සී ට වඩා වේගවත් වනු ඇත, මන්ද සම්පාදකයා විසින් වර්ගය-විශේෂිත WorkOnPixel
අච්චු විශේෂීකරණයට අනුබල දෙයි. අමතන්න!
අඩු කේතයක්, ධාවන කාලයට ඉහළින්, වැඩි ආරක්ෂාවක් නොමැත.
මෙයින් අදහස් කරන්නේ C ++ යනු සියල්ලම සහ අවසානය යන භාෂාවන් බවයි. ඇත්ත වශයෙන්ම නැත. ඔබ තවමත් වෙළඳ ලකුණු මැනිය යුතුය. නොදන්නා අය B ++ / Perl / Python ස්ක්රිප්ට් එකක් ලිවිය යුතු විට C ++ භාවිතා කරනු ඇත. ප්රේරක-ප්රීතිමත් C ++ නවක වදය මඟින් ඔබට ඒවා නැවැත්වීමට සහ ඇසුරුම් යැවීමට පෙර අතථ්ය බහු උරුමයක් සහිත ගැඹුරු කැදැලි පන්ති නිර්මාණය කරනු ඇත. මෙය අවශ්ය නොවන බව වටහා ගැනීමට පෙර ඔවුන් සංකීර්ණ බූස්ට් මෙටා ක්රමලේඛනය භාවිතා කරනු ඇත . ඔවුන් තවමත් භාවිතා කරනු ඇත char*
, strcmp
සහ මැක්රෝස් වෙනුවට std::string
සහ සැකිලි.
නමුත් මෙයින් කියැවෙන්නේ ඊට වඩා දෙයක් නොවේ ... ඔබ වැඩ කරන්නේ කා සමඟද යන්න බලන්න. අකාර්යක්ෂම පරිශීලකයින්ගෙන් ඔබව ආරක්ෂා කිරීමට භාෂාවක් නොමැත (නැත, ජාවා පවා නැත).
C ++ දිගටම අධ්යයනය කර භාවිතා කරන්න - ඕනෑවට වඩා සැලසුම් නොකරන්න.