ฉันเห็นโปรแกรมเมอร์ C มากเกินไปที่เกลียด C ++ ฉันใช้เวลาค่อนข้างนานหลายปีในการเข้าใจสิ่งที่ดีและสิ่งที่ไม่ดีเกี่ยวกับเรื่องนี้อย่างช้าๆ ฉันคิดว่าวิธีที่ดีที่สุดในการใช้วลีนี้คือ:
รหัสน้อยไม่มีค่าใช้จ่ายในการดำเนินการความปลอดภัยมากขึ้น
ยิ่งโค้ดน้อยที่เราเขียนก็จะยิ่งดี สิ่งนี้จะชัดเจนในทุกวิศวกรที่มุ่งมั่นสู่ความเป็นเลิศ คุณแก้ไขข้อผิดพลาดในที่เดียวไม่มาก - คุณแสดงอัลกอริทึมหนึ่งครั้งและนำกลับมาใช้ใหม่ในหลาย ๆ ที่ ฯลฯ ชาวกรีกยังมีคำพูดย้อนกลับไปที่สปาร์ตันโบราณ: "พูดอะไรบางอย่างด้วยคำพูดน้อย ว่าคุณฉลาดเกี่ยวกับเรื่องนี้ " และความจริงของเรื่องนี้คือเมื่อใช้อย่างถูกต้อง C ++ จะช่วยให้คุณสามารถแสดงรหัสที่น้อยกว่า C ได้โดยไม่ต้องเสียค่าใช้จ่ายในการรันไทม์ในขณะที่มีความปลอดภัยมากขึ้น (เช่นการจับข้อผิดพลาดในเวลารวบรวม) มากกว่า C คือ
ต่อไปนี้เป็นตัวอย่างที่เรียบง่ายจากตัวแสดงผลของฉัน: เมื่อทำการแก้ไขค่าพิกเซลข้าม scanline ของรูปสามเหลี่ยม ฉันต้องเริ่มต้นจากพิกัด X X และถึงพิกัด X x2 (จากซ้ายไปทางด้านขวาของรูปสามเหลี่ยม) และในแต่ละขั้นตอนในแต่ละพิกเซลที่ฉันผ่านไปฉันต้องแก้ไขค่า
เมื่อฉันแก้ไขแสงโดยรอบที่มาถึงพิกเซล:
  typedef struct tagPixelDataAmbient {
      int x;
      float ambientLight;
  } PixelDataAmbient;
  ...
  // inner loop
  currentPixel.ambientLight += dv;
เมื่อฉันสอดแทรกสี (เรียกว่า "การแรเงา" Gouraud "โดยที่ฟิลด์" สีแดง "," สีเขียว "และ" สีฟ้า "ถูกแก้ไขโดยค่าขั้นตอนในแต่ละพิกเซล):
  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;
เมื่อฉันแสดงเป็นเงาพงษ์ฉันจะไม่สอดแทรกความเข้ม (ambientLight) หรือสี (แดง / เขียว / น้ำเงิน) อีกต่อไป - ฉันสอดแทรกเวกเตอร์ปกติ (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;
ตอนนี้สัญชาตญาณแรกของโปรแกรมเมอร์ C คือ "heck เขียนสามฟังก์ชั่นที่สอดแทรกค่าและเรียกมันขึ้นอยู่กับโหมด set" ก่อนอื่นนี่หมายความว่าฉันมีปัญหาประเภท - ฉันจะทำงานกับอะไร PixelDataAmbient ของฉันเป็นพิกเซลหรือไม่ PixelDataGouraud? PixelDataPhong? อ๋อเดี๋ยวก่อนโปรแกรมเมอร์ C ที่มีประสิทธิภาพพูดว่าใช้งานยูเนี่ยน!
  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;
          }
...
คุณรู้สึกถึงความโกลาหลที่เข้ามาหรือไม่?
ก่อนอื่นหนึ่งการพิมพ์ผิดคือสิ่งที่จำเป็นในการทำให้รหัสของฉันผิดเนื่องจากคอมไพเลอร์จะไม่หยุดฉันในส่วน "Gouraud" ของฟังก์ชันเพื่อเข้าถึง ".a" อย่างแท้จริง ค่า (แวดล้อม) ข้อผิดพลาดที่ไม่ถูกจับโดยระบบชนิด C (นั่นคือระหว่างการรวบรวม) หมายถึงข้อผิดพลาดที่ปรากฏในเวลาทำงานและจะต้องมีการดีบัก คุณสังเกตเห็นว่าฉันกำลังเข้าสู่left.a.greenการคำนวณ "dgreen" หรือไม่? ผู้แปลไม่ได้บอกคุณอย่างแน่นอน
จากนั้นมีการทำซ้ำทุกที่ - forลูปมีหลายครั้งที่มีโหมดการเรนเดอร์เราทำต่อไป "ลบด้วยการหารซ้ายขวาด้วยขั้นตอน" น่าเกลียดและผิดพลาดได้ง่าย คุณสังเกตเห็นฉันเปรียบเทียบโดยใช้ "i" ใน Gouraud loop เมื่อใดที่ฉันควรใช้ "j" คอมไพเลอร์นั้นเงียบอีกครั้ง
สิ่งที่เกี่ยวกับ if / else / ladder สำหรับโหมด? จะเกิดอะไรขึ้นถ้าฉันเพิ่มโหมดการแสดงผลใหม่ในสามสัปดาห์ ฉันจะจำได้หรือไม่ว่าจะจัดการโหมดใหม่ใน "if mode ==" ในรหัสของฉันทั้งหมดหรือไม่
ตอนนี้เปรียบเทียบความอัปลักษณ์ข้างต้นด้วยชุด C ++ structs และฟังก์ชันเทมเพลต:
  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;
      }
  }
ตอนนี้ดูที่นี้ เราไม่ได้ทำซุปรวมยูเนี่ยนอีกต่อไป: เรามีประเภทเฉพาะต่อแต่ละโหมด พวกเขาใช้สิ่งทั่วไป (ฟิลด์ "x") อีกครั้งโดยสืบทอดจากคลาสพื้นฐาน ( CommonPixelData) และเทมเพลตทำให้คอมไพเลอร์สร้าง (นั่นคือรหัสสร้าง) สามฟังก์ชั่นที่แตกต่างกันเราจะได้เขียนตัวเองใน C แต่ในเวลาเดียวกันการเข้มงวดมากเกี่ยวกับประเภท!
การวนซ้ำของเราในเทมเพลตไม่สามารถทำให้ผิดและเข้าถึงฟิลด์ที่ไม่ถูกต้อง - คอมไพเลอร์จะเห่าถ้าเราทำ
เทมเพลตดำเนินงานทั่วไป (ลูปเพิ่มขึ้นโดย "ขั้นตอน" ในแต่ละครั้ง) และสามารถทำได้ในลักษณะที่ไม่สามารถทำให้เกิดข้อผิดพลาดรันไทม์ แก้ไขต่อประเภท ( AmbientPixelData, GouraudPixelData, PhongPixelData) จะทำกับoperator+=()ว่าเราจะเพิ่มใน structs - ซึ่งโดยทั่วไปกำหนดวิธีการแต่ละประเภทจะถูกหยัน
และคุณเห็นสิ่งที่เราทำกับ WorkOnPixel <T> หรือไม่ เราต้องการทำงานที่แตกต่างกันตามประเภทหรือไม่ เราเรียกความเชี่ยวชาญเฉพาะด้านเทมเพลต:
void WorkOnPixel<AmbientPixelData>(AmbientPixelData& p)
{
    // use the p.ambientLight field
}
void WorkOnPixel<GouraudPixelData>(GouraudPixelData& p)
{
    // use the p.red/green/blue fields
}
นั่นคือ - ฟังก์ชั่นการโทรจะตัดสินใจตามประเภท ที่รวบรวมเวลา!
ในการใช้ถ้อยคำใหม่อีกครั้ง:
- เราย่อโค๊ด (ผ่านเทมเพลต) นำชิ้นส่วนทั่วไปกลับมาใช้ใหม่
 
- เราไม่ใช้แฮ็กที่น่าเกลียดเราใช้ระบบแบบเข้มงวดเพื่อให้คอมไพเลอร์สามารถตรวจสอบเราได้ตลอดเวลา
 
- และดีที่สุด: ไม่มีสิ่งที่เราทำมีผลกระทบต่อรันไทม์ใด ๆ รหัสนี้จะเรียกใช้เพียงเร็วเท่ารหัส C เทียบเท่า - ในความเป็นจริงถ้ารหัส C ใช้ฟังก์ชันพอยน์เตอร์เพื่อเรียกใช้
WorkOnPixelรุ่นต่าง ๆรหัส C ++ จะเร็วกว่า C เนื่องจากคอมไพเลอร์จะอินไลน์WorkOnPixelแม่แบบเฉพาะประเภท โทร! 
รหัสน้อยไม่มีค่าใช้จ่ายในการดำเนินการความปลอดภัยมากขึ้น
สิ่งนี้หมายความว่า C ++ เป็นภาษาที่เป็นทั้งหมดและสิ้นสุดหรือไม่ ไม่แน่นอน คุณยังต้องวัดการแลกเปลี่ยน คนที่โง่เขลาจะใช้ C ++ เมื่อพวกเขาควรเขียนสคริปต์ Bash / Perl / Python มือใหม่ C ++ ที่มีความสุขในการเรียกใช้จะสร้างคลาสที่ซ้อนกันอย่างลึกล้ำด้วยการสืบทอดหลาย ๆ แบบเสมือนจริงก่อนที่คุณจะหยุดและส่งพวกมันออก พวกเขาจะใช้การเขียนโปรแกรมBoost meta ที่ซับซ้อนก่อนที่จะรู้ว่าสิ่งนี้ไม่จำเป็น พวกเขาจะยังคงใช้char*, strcmpและแมโครแทนstd::stringและแม่แบบ
แต่นี่ไม่ได้พูดอะไรมากไปกว่า ... ดูว่าคุณทำงานกับใคร ไม่มีภาษาใดที่จะป้องกันคุณจากผู้ใช้ที่ไร้ความสามารถ (ไม่แม้แต่ Java)
ศึกษาและใช้ C ++ ต่อไป - อย่าเพิ่งออกแบบมากเกินไป