ThaiGameDevX - Thai Game Developers eXchange Forums
21 November 2017, 10:17:31 PM *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
News: หากมาครั้งแรก เชิญอ่าน ประกาศเจตนารมณ์ของ ThaiGameDevX และ กติกา ข้อตกลงในการใช้เว็บบอร์ด ครับ
 
   Home   Help Search Calendar Login Register  
Pages: 1 [2]   Go Down
  Print  
Author Topic: เทคนิคการเขียนโปรแกรมให้ปลอด Bug  (Read 31476 times)
0 Members and 1 Guest are viewing this topic.
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #30 on: 12 September 2005, 11:59:10 PM »

+1 ให้คุณ ตาหวาน คับ  +ปักหมุดเลยนะ
Logged

..
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #31 on: 13 September 2005, 08:56:33 AM »

ในที่สุดก็มาถึงหน้า 2 แล้ว Smiley

กรณี reply นี้ เคยเกิดขึ้นกับตัวผมเอง� Smiley

สำหรับเกมหรือโปรแกรมที่มีการทำงานเป็น state
-- สมมุติว่าเป็น state ของ มอนสเตอร์ ตัวนึงนะครับ (เป็นตัว slime )

1. กำหนด state ดังนี้
Code:
enum SLIME_STATE
{
� �IDLE,
� �ANNOYED,
� �ANGRY,
� �MAD
};

แล้วใน code ai หลายๆ ที่ เราก็มีการตรวจสอบสถานะว่าอยู่ในสถานะไหนก่อนที่จะตัดสินใจทำตัวยังไง

Code:
if (state==IDLE) ..DoIdleState..
else if (state== ANNOYED) ..DoAnnoyedState..
else if (state== ANGRY) ..DoAngryState..
else� // เหลือ อันสุดท้ายยังไงก็เป็น MadState อยู่แล้ว
}
จากโค้ดข้างบน หากตอนหลังมีการเพิ่ม state ใหม่เข้ามา เช่น SLEEP (slime โดนเวทย์ทำให้หลับ)
หากเราลืมแก้การตรวจสอบสถานะบางจุดในโปรแกรม bug ตัวนี้ก็จะแสดงตัวออกมาตอนสังเกตพฤติกรรมของตัว slime หรืออาจจะมองไม่ออกเลย และมันก็จะไปแสดงอาการแปลกๆแทน

Code:
switch (state)
{
case IDLE: ..DoIdleState..
case ANNOYED: ..DoAnnoyedState..
case ANGRY: ..DoAngryState..
case MAD: ..DoMadState..
default: ..Do nothing
}
จากโค้ดต่อมาเราทำการดักให้ตรงทุกเงื่อนไข หากมี state (SLEEP) เพิ่มขึ้นมา ส่วนการตรวจสอบสถานะเดิมมันก็ยังทำตัวถูกอยู่เหมือนเดิม
แต่มันก็ไม่ได้แก้ bug กลายเป็น bug ซ่อนเร้นอีกตัวหนึ่ง เพราะมันก็ทำให้เราลืมใส่การตรวจสอบสถานะนั่นเอง

ทางที่ดี เราควรดักความผิดพลาดตั้งแต่เนิ่นๆ โดยการวางกับดักไว้ตั้งแต่แรกครับ
Code:
switch (state)
{
case IDLE: ..DoIdleState..
case ANNOYED: ..DoAnnoyedState..
case ANGRY: ..DoAngryState..
case MAD: ..DoMadState..
default: ERROR OUT LOUD, LINE NO ..., FILE ...� // แสดง error เด้งออกมาให้เห็นเลย
}
หากเราเพิ่ม state เข้ามาใหม่ โปรแกรมส่วนที่ลืมตรวจสอบสถานะจะแจ้งให้เรารู้เอง (ต้อง test ให้เจอด้วยล่ะ)


** แต่ผมไม่เคยเขียน state ของ slime นะ เป็น state การเปลี่ยนจอของเกม ตอนที่รู้ว่าต้องเพิ่มหน้า credit เข้าไปในเกมอีก หลังจากเพิ่มไปแล้วก็นั่งแก้ bug อยู่ทั้งวัน
« Last Edit: 13 September 2005, 08:58:50 AM by mak » Logged
tumy
Newbie
*

จำนวน ชม/ไม่พอใจ: +1/-0
Offline Offline

Posts: 2


« Reply #32 on: 17 September 2005, 06:26:41 PM »

ขอต่อจาก พี่ yod และ พี่ mak นะครับ
เห็นพี่ พูดเรื่อง && || กันผม เลยยกตัวอย่างนี้มาให้ดูครับ
 
Code:
if(object != null && do( object ))
  do do... statement;

bool do( object o )
{
     ...do somthing o
     return true/false;
}


แบบนี้อ่ะครับไม่รู้เกี่ยวกับ ลด บัคได้ไหม แต่ช่วย ให้ performance ดีขึ้น นิสสสสสสสส นึงครับ
« Last Edit: 17 September 2005, 08:07:28 PM by Cray » Logged
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #33 on: 17 September 2005, 07:50:16 PM »

อุอุ อธิบายนิดนึงด้วยสิครับ ว่ามันช่วยให้ performance ดีขึ้นได้ยัีงไง
เผื่อว่ามีคนที่ไม่เข้าใจด้วยน่ะครับ
แต่ยังไงก็ยังให้ 1 คะแนน สำหรับความตั้งใจในการช่วยกันออก Idea ครับ  Wink


อ้อ.. ผมแอบแก้ให้นิดนึงนะครับ เพราะพิมพ์ผิด ตรงบรรทัด if (object == null && do( object )) และ retrun ture/false น่ะครับ  Cheesy

ขอต่อจาก พี่ yod และ พี่ mak นะครับ
เห็นพี่ พูดเรื่อง && || กันผม เลยยกตัวอย่างนี้มาให้ดูครับ
 
Code:
if(object != null && do( object ))
 do do... statement;

bool do( object o )
{
 ...do somthing o
 return true/false;
}


แบบนี้อ่ะครับไม่รู้เกี่ยวกับ ลด บัคได้ไหม แต่ช่วย ให้ performance ดีขึ้น นิสสสสสสสส นึงครับ


แต่จาก function ทีุ่คุณ tumy ให้มานะครับ
ถ้าเราเปลี่ยน bool เป็น HRESULT (ซึ่งก็คือ int) แทน ก็จะสามารถตรวจสอบได้ว่า
มันเกิดอะไรขึ้น ถ้า function ไม่สามารถทำงานได้อย่างถูกต้องด้วยครับ
อาจจะมีคนบอกว่า ให้ตรวจสอบแล้วก็ throw exception ใน function เลย
แต่ด้วยความที่ return type เป็น bool นั้น ถ้าทำอย่างที่ว่า โอกาสในการ return ค่า false
กลับมาก็จะไม่มีเลยครับ เพราะฉะนั้น เปลี่ยนมาใช้ HRESULT น่าจะช่วยให้ make sense กว่าครับ

Code:
#define RET_OK   0
#define RET_ERR_1       1
#define RET_ERR_2       2

HRESULT hResult;

if(object != null && ((hResult = do( object )) == RET_OK)) {
 do do... statement;
}
else {
  // show error message
  switch (hResult) {
     case RET_ERR_1:
     MessageBox(...);
     break;
     case RET_ERR_2:
     MessageBox(...);
     break;
  }
}

็HRESULT do( object o )
{
 ...do somthing o
 
return RET_OK/RET_ERR_1/RET_ERR2;
}

« Last Edit: 17 September 2005, 08:09:00 PM by Cray » Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #34 on: 17 September 2005, 11:44:17 PM »

ที่แนะนำมามีประโยชน์ดีครับ� Smiley

เพิ่มเติมเล็กน้อยๆ
if(object != null && do( object ))
(เอาตัวอย่างของน้องนะครับ คุณ cray คงไม่น้อยใจนะ Tongue )
ที่นี้อาจจะต้องระวังนิดนึงถ้าจะเขียนภาษา C/C++ แบบ compile ที่ไหนก็ได้
เพราะว่าคอมไพเลอร์มันทำงานไม่เหมือนกันครับ
เวลามันแปลประโยคข้างบนนั้น น่าจะหมายถึงทำจากซ้ายไปขวา
ดูง่ายๆ จะเป็น
ทำใน A ก่อน แล้วทำ B ถ้าTRUE ให้ไปทำใน if
Code:
//แบบที่ 1.A
// -- compiler เพิ่ม
A = (object != null )
B = do( object )
// -- compiler เปลี่ยน
if ( A && B ) {...}
ที่นี้คอมไพเลอร์มันทำงานไม่เหมือนกันครับ อีกหลายๆ ตัวจะทำแบบขวาไปซ้าย
ดังนั้น มันจะทำแบบนี้แทน
Code:
//แบบที่ 1.B
// -- compiler เพิ่ม
B = do( object )
A = (object != null )
// -- compiler เปลี่ยน
if ( A && B ) {...}
พอเห็นปัญหานะครับ� �;)

ดังนั้นถ้าจะเขียนภาษา C/C++ แบบ compile ที่ไหนก็ได้แยกเป็นตัวแปรมารับค่าให้เสร็จก่อน
การทำงานใน if จะปลอดภัยกว่าครับ
Code:
//แบบที่ 2
A = (object != null )
if ( A ) {
B = do( object )
if ( B ) {..}
}
ก็คือแบบที่คุณ mak อธิบายไปแล้ว� Smiley

มีคำถามให้ร่วมสนุกกัน
คำถาม 1. แบบไหนทำงานปลอดภัยกว่า ?
คำถาม 2. แบบไหนทำงานเร็วกว่า ?
ตอบถูกรางวัลเป็น +1 (ให้คุณ cray แจกให้นะคับ ฮะๆ ) Grin
( ตอบผิดติดลบด้วยดีไหม ฮะๆ� Grin )
Logged

..
Cray
Global Moderator
Sr. Member
*****

จำนวน ชม/ไม่พอใจ: +10/-0
Offline Offline

Gender: Male
Posts: 385



« Reply #35 on: 18 September 2005, 07:29:15 AM »

ไม่น้อยใจหรอกคับ Smiley .... แต่ผมไม่ตอบคำถามคุณยอดดีกว่า เพราะกลัวโดนติดลบอ่ะคับ   Grin
ไม่เชี่ยวชาญเรื่อง compiler  Cheesy

่แต่โดยปกติแล้ว ผมจะเอา object นั่นไป check ใน function เลยครับ  Smiley


Code:

#define RET_OK                                           0x00000000
#define RET_NULL_REFERENCE_EXCEPTION     0x80100001 //ค่าสมมุติ

HRESULT do(Object* object)
{
    if (object == NULL)
        return RET_NULL_REFERENCE_EXCEPTION;

    ...
    // ทำคำสั่งอื่นๆ
    ...

    return RET_OK;
}
Logged

"I'm starting with the man in the mirror, I'm asking him to change his ways." - Michael Jackson (Man in the mirror)
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #36 on: 18 September 2005, 09:27:05 AM »

สงสัยจะไม่มีใครตอบ ^^" ...
เก็บไว้คิดละกันไม่เฉลยดีกว่า
Logged

..
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #37 on: 18 September 2005, 02:02:15 PM »

ปกติแล้วผมจะเลือกแบบปลอดภัยไว้ก่อนครับ (ก็คือแบบที่ 2 นั่นเอง และก็เป็นแบบที่ช้าที่สุดด้วย)

ยกตัวอย่างนี้มาอีกรอบนะครับ (แก้ไขนิดหน่อย)
Code:
if(object != null && do( object ))
do do... statement;

bool do( object o )
{� �
� �if (o == null)
� �{
� � � �// มีการแก้ไขตัวแปรแบบ global
� �}
� �// do something
� �return true/false;
}

หากเป็น function ไม่ได้ จัดการกับ parameter ที่เป็น null ว่าต้องเป็น error เสมอไปละครับ เช่น สมมุติผมมี function ที่ใช้สำหรับ set viewport ดังนี้
Code:
bool SetViewport(Rect *r)
{
� � if (r == null)
� � {
� � � � // viewport size = screen size
� � � � return true;
� � }
� � � �
� �if (r out of screen)
� � � �return false;

� �// viewport size = r
� �return true;
}
function ข้างบนนี้ต้องการให้ set viewport เป็นขนาดของหน้าจอหากใส่ parameter เป็น null

สำหรับ
Code:
if (rect != null && SetViewport(rect))
{
}
คอมไพล์เลอร์อาจข้ามการ set viewport ที่ต้องการให้มีขนาดเท่ากับหน้าจอ ไปเลยก็ได้ครับ

เคยดูตัวอย่าง code ใน directx sdk แล้วเห็นอะไรแบบนี้ไหมครับ (ไม่เกี่ยวกับข้างบนแล้วนะ)

Code:
hresult = pDirectDraw->DoSomeThing();
if (S_OK == hresult)
{
� �// ....
}

แทนที่จะเขียน
Code:
if (hresult == S_OK)

ก็เพราะว่า ป้องกันการใส่เครื่องหมาย assign ค่า (=) แทนเครื่องหมาย เปรียบเทียบความเท่ากัน (==) ในประโยคที่ใช้ในการเปรียบเทียบครับ

ถ้ามีการเผลอเขียนแบบนี้ลงไป
Code:
hresult = pDirectDraw->DoSomeThing();
if (S_OK = hresult)
{
� �// ....
}
ตอมไพเลอร์ก็จะเตือนแจ้ง error การ assign ค่าลงในค่าคงที่ครับ


ส่วนการ assign ค่าแบบจงใจ เช่น
Code:

if (a && a = b)
{
}
แม้จะตั้งใจให้มีการ assign ในประโยคเปรียบเทียบจริงๆ แต่ภายหลังอาจถูกเปลี่ยนไปเป็น (==) โดยไม่ได้ตั้งใจ เพราะเข้าใจว่าเขียน = ตกไปตัวนึงก็ได้


Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #38 on: 18 September 2005, 04:05:34 PM »

เฉลยละกัน ^.^

if(object != null && do( object ))

ถ้า compiler แปลจากซ้ายไปขวา และ compiler optimize ด้วย
จะได้ code เหมือนกะแบบที่ 2 ครับ (ไม่ใช่แบบที่ 1.A นะ )
เพราะว่ากรณี optimize ถ้ารู้ว่า A เป็น เท็จ มันจะต้องไม่ทำต่อในเทอม exp หลังจากนี้อีก คือไม่ต้องหาค่า B น่ะละ

ถ้า compiler แปลจากซ้ายไปขวา และ compiler ไม่ optimize
จะได้แบบที่ 1.A และนั่นหมายความว่า ไม่ว่ามันจะเป็น null หรือไม่ ทั้งค่า A� และ B จะต้องเอาไปหาค่าก่อนมาคำนวน logic (A&&B) หุหุ (เห็นปัญหามั้ยล่ะ)

ดังนั้นคุณ mak จึงตอบถูก ในคำถามข้อ 1 ครับ
ส่วนในคำถามข้อ 2 จากเหตุผลข้างต้น run-time จึงน่าจะใช้เวลาพอๆ กันครับ
+1 ให้นะคับ ยินดีด้วย  Grin
Logged

..
EndoBee
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +15/-0
Offline Offline

Posts: 187


« Reply #39 on: 19 September 2005, 02:49:54 AM »

คุณตาหวานคับ ตัวอย่างแรกเนี่ยถ้าเป็นภาษา C++ มันไม่ผิดนะคับ
การ return ค่ามันเป็นการ return ในมิติของตัวแปรธรรมดา ไม่ใช่ pointer

จะผิดต่อเมื่อ

Date* Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   Date date("today");
   return &date;
}

อันนี้ผิดแน่ๆ อาจแก้เป็นแบบนี้ได้ แต่อาจดูเพี้ยนๆไปหน่อย
Date* Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   static Date date("today");
   return &date;
}
แบบนี้ถูกคับแต่อาจงงเอง
แต่แบบนี้ไม่ได้

Date[] Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   Date date("today")[100];
   return &date;
}
เพราะ array ของ C++ คือ pointer โง่ๆตัวหนึง
สมมุติว่า
struct date
{
    int* xxx;
    int  i;
};
ให้ date.xxx = new int[50]();
date.i=5;

ถ้าแบบนี้อยู่ใน function return แบบไหนก็ผิด

ภาษา C++ ก็เงี่ย ไม่ฉลาดนัก แต่ผมก็รักมันที่สุด

Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #40 on: 19 September 2005, 03:55:37 AM »

ขอบอกว่าผมก็เพิ่งนึกออกว่าผิดอะไร (เมื่อหลายวันผ่านไป) ... คือ ผมตั้งจะเขียนให้มันคืนค่า Reference กลับไป ... แต่ตอนเขียนดันลืมครับผม (ไอ้ที่เขียนมันส่ง Value กลับไป ยังไงก็ไม่ผิด)

จริง ๆ ผมก็ไม่ค่อยเชี่ยวชาญด้าน C++น่ะครับ หุหุหุ

ปล. พูดไปก็เหมือนแก้ตัวแฮะ 555
« Last Edit: 19 September 2005, 11:59:20 AM by นายตาหวาน » Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #41 on: 19 September 2005, 10:54:54 AM »

อืม คุณ endobee พูดถูกแฮะ ชม +1 ^^/

ส่วนอันล่างนั้น ถ้า struct date define เป็น localvar ทำไม่ได้ครับ เพราะว่ามันชี้ไปที่ local stack ซึ่งจะถูกลบโดยคอมไพเลอร์

แต่ถ้า data เป็น pointer to struct และจอง mem ไว้ใหม่ ก็น่าจะคืน return ได้นะ
เพราะว่ามันชี้ไปที่ heap

ส่วนข้างใน structจะเป็น pointer อะไรอีกก็ไม่เป็นไรเพราะถ้าบอกอย่างนี้ date.xxx = new int[50](); มันก็จะชี้ไปที่ heap อยู่แล้ว

Logged

..
EndoBee
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +15/-0
Offline Offline

Posts: 187


« Reply #42 on: 19 September 2005, 11:06:11 PM »

อุ๋ย... ผมผิดจริงๆด้วย ขอโทษครับ
ตอนแรกผมคิดว่า date.xxx มันเป็นการใช้พื้นที่ร่วมกัน ทำให้เมื่อแก้ตัวนึง อีกตัวนึงก็เปลี่ยนตาม
เป็นจุดก่อให้เกิด bug นาๆ ประการ
แต่ในกรณีนี้มันเป็นการ return ค่า ทำให้ตัวนึงถูกตัดทิ้งไปอยู่แล้วทำให้ไม่มีผลอะไร

เพิ่มเติม : ในกรณีของคุณตาหวานจะเกิด bug ต่อเมื่อเป็น java ใช่มั้ยครับ (ดูออกเลยว่าใช้ java มาก่อน ซึ่งจริงๆแล้วภาษาระดับสูงแบบ java คงไม่ยอมให้ทำอยู่แล้วละมั้ง)
การ return ค่าแบบ pass by ref ในภาษา C++ เขียนแบบนี้คับ     
Code:
Date& Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   Date date("today");
   return date;
}
ซึ่งแบบนี้ผิด 100% แต่แบบนี้ถูก
Code:
Date& Date::Today(){
   //สร้าง object ขึ้นมาตัวนึง
   static Date date("today");
   return date;
}
แต่การอ้างตัวแปรแบบ pass by ref ในภาษา C++ ไม่นิยมใช้กันนัก น่าจะเป็นเพราะมีการใช้ pointer อยู่แล้ว

Edit : เมื่อกี้กดผิดไปโดน Modify เลยมาแก้เพิ่ม Tag ให้คับ หุหุ ขออภัยในความไม่สะดวกไปสองวิ หุหุหุ
« Last Edit: 20 September 2005, 01:59:23 AM by นายตาหวาน » Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #43 on: 20 September 2005, 02:00:52 AM »

...
เพิ่มเติม : ในกรณีของคุณตาหวานจะเกิด bug ต่อเมื่อเป็น java ใช่มั้ยครับ (ดูออกเลยว่าใช้ java มาก่อน ซึ่งจริงๆแล้วภาษาระดับสูงแบบ java
...

ถูกต้องนะคร้าบบบบบบ Cheesy
Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #44 on: 20 September 2005, 08:33:57 AM »

เพิ่มเติมสำหรับ C++ นะครับ

สำหรับการใช้ operator = ในการ copy object

Code:
class MyClass
{
private:
     int field1, field2, field3;
};

ปกติเราสามารถ assign ค่าของ object ด้วย ค่าของ object อีกตัวได้ครับแบบนี้
Code:
MyClass a, b;
a = b;
...

หรือ

MyClass func()
{
    MyClass b;
    return b;
}

a = func();


การ assign ด้วย operator = โดยทั่วไปจะเป็นการ copy filed ทั้งหมด แบบนี้ครับ
a.field1 = b.field1;
a.field2 = b.field2;
a.field3 = b.field3;

แต่ถ้าหาก มี field ที่เป็น pointer อยู่ใน class
Code:
class MyClass
{
public:
    int i;
    int *p;
};
หากมีการ ใช้ operator ในการ copy ก็จะเป็นการ copy address  ไปอย่างเดียว

Code:
MyClass a, b;
a = b;   // a.i = b.i;  a.p = b.p;

หากไม่ระวังให้ดีเมื่อเรา copy pointer ไปแล้วเมื่อเราเปลี่ยนค่าที่ pointer หนึ่ง ค่าที่ pointer อีกตัวหนึ่งชี้ก็จะเปลี่ยนตาม

แต่การลบ pointer ก็เป็นปัญหาเช่นกัน เช่น
Code:
class MyClass
{
public:
    MyClass()
    {
        p = new int[100];
    }

    ~MyClass()
    {
        delete[] p;
    }

private:
    int *p;
};


แล้วสร้าง function ให้มีการ return copy ของ object ที่สร้างไว้ใน function
Code:
MyClass func()
{
    MyClass a;
    return a;
}

MyClass b = func();


ค่าที่ return เหมือนกับ a.p จริง  แต่เมื่อจบ function ตัวแปร a ไม่ได้ใช้ และจะถูกเรียก destructor ~MyClass ก่อนที่จะทำลายตัวแปร a ทิ้ง ผลก็คือ pointer b.p ชี้ไปยัง address ที่ถูก delete[] ไปแล้ว
Logged
mak
Approved Member
Full Member
*

จำนวน ชม/ไม่พอใจ: +10/-1
Offline Offline

Gender: Male
Posts: 113



« Reply #45 on: 20 September 2005, 08:41:45 AM »

สำหรับผู้ที่ยังไม่รู้ ใน c++ operator = สามารถทำการ overload ได้โดยการทำ "copy constructor"

เช่น

1.
Code:
class MyClass
{
public:
     MyClass(MyClass &o);
};

หรือ 2.
Code:
class MyClass
{
public:
     MyClass(const MyClass &o);
};

จากนั้นเมื่อมีการใช้ operator = ก็จะเป็นการเรียกใช้ copy constructor แทนที่จะเป็นการ copy ให้เองทุกตัวแปรแบบที่เป็นตามปกติ
เราอาจสามารถใช้ในการ copy ค่าใน pointer ได้ ดังนี้

Code:
class MyClass
{
public:
    MyClass()   // default constructor
    {
        p = new int[100];
    }

    MyClass(const MyClass &o)  // copy constructor
    {
       p = new int[100];
       // copy ค่าใน o.p ไปยัง p
    }

    ~MyClass()
    {
        delete[] p;
    }

private:
    int *p;

};

Logged
yod
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +150/-15
Offline Offline

Posts: 3,240


WWW
« Reply #46 on: 20 September 2005, 11:13:31 AM »

(+1ให้คุณ mak� Grin )
สำหรับ Delphi Wink
Code:
//code1
type Tbase=class
� � private
� � � a,b,c:integer;
� � protected
� � public� �
� � end;

assign
Code:
//code2
var ca,cb:Tbase;
begin
� � � // create
� � � �ca:=Tbase.create;
� � � �cb:=Tbase.create;
� � � // assign
� � � �ca:=cb;
� � � �// free
� � � �ca.free;
� � � �cb.free;
end;

ในการ assign อีกแบบหนึ่งที่เขียนเองก็ได้ครับ
Code:
//code3
type Tbase=class
� � private
� � � a,b,c:integer;
� � � p:pointer;
� � protected
� � public
� � � procedure assign(base_source:Tbase);
� � end;

procedure Tbase.assign(base_source:Tbase);
begin
� � �self:=base_source;
end;

เวลาเรียกใช้ก็ ..
� � � �ca.assign(cb);

กรณีถ้ามี pointer
Code:
//code4
procedure Tbase.assign(base_source:Tbase);
begin
� � �// clear pointerเก่า ก่อน
� � � if p<>nil then freemem(p);
� � // copy ค่าทั่วไป (รวมทั้งpointer addr)
� � �self:=base_source;
� � � // จัดการ copy data pointer เอง
� � � �getmem(p, size);
� � � move( base_source.p^, p^, size);
end;
เวลาเรียกใช้ก็เหมือนเดิม
� � � �ca.assign(cb);
« Last Edit: 20 September 2005, 11:17:23 AM by yod » Logged

..
Pisal
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +37/-1
Offline Offline

Gender: Male
Posts: 556


ช้างๆๆๆ น้องเคยเห็นช้างหรือเปล่า?


WWW
« Reply #47 on: 02 October 2005, 11:51:27 PM »

ผมขอแนะนำหนังสือ:

Effective C++: 55 Specific Ways to Improve Your Programs and Designs, 3rd Edition - Scott Meyers
Logged
Pisal
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +37/-1
Offline Offline

Gender: Male
Posts: 556


ช้างๆๆๆ น้องเคยเห็นช้างหรือเปล่า?


WWW
« Reply #48 on: 08 January 2006, 04:30:55 AM »

นอกจากการ comment code ให้ programmer ที่ทำงานด้วยกันสามารถเข้าใจว่าเราเขียน program อะไรเราก็สามารถใช้ automatic document generator ทำเอกสารประกอบ code ได้อย่างเร็ว

ตัวอย่างที่ใช้ใน Java เป็น Javadoc (http://java.sun.com/j2se/javadoc/)
ตัวอย่างที่ใช้กับ language อื่นๆ เป็น Doxygen (http://www.stack.nl/~dimitri/doxygen/)
Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #49 on: 09 January 2006, 07:25:56 AM »

และผมจะขอยืมไป Xerox ได้มั้ยครับอาจารย์ ? (ช่วงนี้จนง่ะคับ  Embarrassed )
Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
Pisal
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +37/-1
Offline Offline

Gender: Male
Posts: 556


ช้างๆๆๆ น้องเคยเห็นช้างหรือเปล่า?


WWW
« Reply #50 on: 12 January 2006, 10:23:26 AM »

และผมจะขอยืมไป Xerox ได้มั้ยครับอาจารย์ ? (ช่วงนี้จนง่ะคับ  Embarrassed )

เรามี 2nd edition ถ้า okay ให้ยื้มได้ โทรมาบอกก่อนนะจะได้หาว่าอยู่ที่ไหน
Logged
นายตาหวาน (Mr.Tawan)
Global Moderator
Hero Member
*****

จำนวน ชม/ไม่พอใจ: +61/-36
Offline Offline

Gender: Male
Posts: 1,591


« Reply #51 on: 12 January 2006, 11:42:08 AM »

อืม ขอเพิ่มอีกหน่อยแล้วกัน ไหน ๆ ก็พอมีไอเดียบ้าง อิอิ ก็ เป็นเรื่องพื้นฐานสุด ๆ ครับ กันพลาด

ออกแบบให้ดีก่อนลงมือเขียน และ เขียน Comment ซะบ้าง อันนี้ประสพการณ์ตรงครัับ ก่อนจะลงมือเขียนอะไรต้องวางแผนให้ดีว่าอะไรจะใช้ทำอะไรมีหน้าที่อย่างไร และ Comment ไว้ด้วย

วิธีการเขียนคอมเมนท์ที่ดี  คือ เขียนว่าโค๊ดตรงนี้ทำไปเพื่ออะไร ไม่ใช่ ทำอะไร นะครับ  เพราะว่าไอ้ทำอะไรเนี่ยไปไล่โค๊ดหน่อยนึงก็รู้แแล้วล่ะครับ แต่ว่าไอ้ทำไปเพื่ออะไรนี่ต้องถึงขั้นเดาใจคนเขียนเลยนะครับ (ผมว่า คนส่วนใหญ่คงจำโค๊ดตัวเองไม่ได้เมื่อเวลาผ่านไปไม่ถึงเดือน Tongue)

ส่วนการออกแบบที่ดีขอไม่พูดถึงมากนะครับ ลองไปศึกษาพวก Design Pattern ด้วยก็ดีครับ

อย่าเพิ่งใจร้อนรีบ Optimize ให้เขียนโค๊ดจนกระทั่งมันทำงานได้ถูกต้องก่อน ผมคิดว่า Programmer รุ่นก่อน ๆ คงถูกปลูกฝังมาให้เขียนโค๊ดให้ทำงานได้เร็ว แต่ว่าการเขียนโค๊ดที่ทำงานได้เร็วผมว่าบางทีมันก็อ่านไม่รู้เรื่องน่ะครับ แล้วพออ่านไม่รู้เรื่องการมาแก้ Bug นี่ก็ยากด้วย ผมขอยกตัวอย่างจากหนังสือ "รูปแบบและมาตรฐานการเขียนโปรแกรมภาษา ซี" ของ ดร.พีระพนธ์ โสพิศสถิตย์ ละกันครับ

Code:
for(u=q=p->st.parm; q&& u&(1<<27)||p->st.next;p++,q++,u%=q){
    //Do something
}

ถามกันเล่น ๆ ครับ ... อ่านรู้เรื่องมั้ย ? คือ เท่าที่พอรู้โค๊ดตรงนี้มันเร็วกว่าที่เราจะเขียนแยกเป็นบรรทัด ๆ จริง ๆ แต่ ... มันอ่้านไม่รู้เรื่องครับให้ตาย Tongue

เขียนโครงโค๊ดให้เหมาะสม อืม ปกติหลังจากออกแบบคลาสเสร็จ เราก็มักจะเขียนโครงของคลาสกัน ก่อนที่จะลงมือเขียนตัวข้างใน บางทีก็ Generate มากจาก UML ด้วยซ้ำ  ในกรณีของ Java,C# เนี่ยมันจะไม่มี header file เหมือน Java ดังนั้นเราก็ต้อง Implement เลย ถูกมั้ยครับ  ดังนั้นก็อาจจะเขียนประมาณว่า
Code:
class A
{
     public A(){};
     public String toString(){
        return null;
     }
}

มันก็ พอได้นะครับ คืน Null กลับมาเนื่องจากว่าเรายังไม่ได้รู้่ว่าจะให้มันคืนค่าอะไร  แต่ สมมติว่าผมทำแบบนี้ล่ะ :-

Code:
public static void doSomething()
{
     A a = new A();
     label = new JLabel(a.toString());
}

มันจะเ้กิดอะไรขึ้น ... ก็ NullPointerException น่ะสิครับ Cheesy วิธีที่น่าจะดีกว่าก็อาจจะเป็น

Code:
class A
{
     public A(){};
     public String toString(){
        return "This is Ouput";
     }
}

แต่จะดีกว่ามาก ถ้าเราเปลี่ยนไปใช้ค่าที่เป็นไปได้ที่จะคืนกลับไปน่ะครับ (อย่างเช่น ถ้าเป็น Class Date ก็ควรจะคืนค่าประมาณ "Monday" อะไรงี้ครับ)

ปล. ถึง อ.ช้่าง  ถ้าอย่างนั้นผมให้น้องยืมของ Library ดีกว่าครับ  หนังสืออ.จะได้ไม่เละด้วย เล่มนึงก็หลายตังค์  แต่ก็ขอบคุณครับผม Cheesy
« Last Edit: 12 January 2006, 08:56:03 PM by นายตาหวาน » Logged

Are you feeling fine?
眠れない夜には君の幻が...
She said, "Loving you made me happy everyday"
ปวีร์ (PentaX)
Veteran Developer
Sr. Member
*

จำนวน ชม/ไม่พอใจ: +47/-5
Offline Offline

Gender: Male
Posts: 369


Maid Lv.0


« Reply #52 on: 29 June 2006, 09:33:50 AM »

แจมด้วยๆ

ของผมนี่... อาจจะเบสิกไปหน่อย ชี้โพรงให้กระรอก... แต่ก็ขอโพสต์นิดนะครับ ของผมจะเป็นใน 3D RAD... ของง่ายๆอ่ะครับ

ตอนปิดเทอมใหญ่เนี่ย ผมเคยพยายามทำเกมส์หุ่นยนต์ ในเอนจิ้นที่ไม่มีโบนส์... ก็คือสร้างชิ้นส่วนร่างกาย มาต่อๆกันเอง

อันนี้เป็นโครงสร้างโค้ดของส่วนหัว ที่อยู่ใต้ส่วนตัว ใน List Editor ไป 1 element ครับ

var px=0
var py=0
var pz=0

var hx=0
var hy=0
var hz=0

var rx=0
var ry=0
var rz=0

px=position_x(element)
py=position_y(element)
pz=position_z(element)

hx=position_x(element#-1)
hy=position_y(element#-1)
hz=position_z(element#-1)

rx=hx-px
ry=hy-py
rz=hz-pz

child_pos(element,element#-1,rx,ry,rz)
child_rot(element,element#-1,0,0,0)

ซึ่งก็ดูปกติดีนะครับ ลองเทสต์ดู... ไหนลองขยับส่วน body ดูซิ...

ผลคือ... สักพัก หัวจะปลิวครับ... แล้วเดินไปพักหนึ่ง... หัวจะลงมาติดที่เป้าแทน (เห้ย... บั๊กอะไรเนี่ยยยยย)

กะไอ้เรื่องแค่เนี้้ย... หาบั๊กกันประมาณสามวันครับ (ตอนนั้นฟิต... ทำเกมส์วันละ 15 ชั่วโมง)

แล้วก็ได้โค้ดที่น่าพอใจ... ประมาณนี้นะครับ

var begin=0

var px=0
var py=0
var pz=0

var hx=0
var hy=0
var hz=0

var rx=0
var ry=0
var rz=0

if begin=0
px=position_x(element)
py=position_y(element)
pz=position_z(element)

rx=position_x(element#-1)
ry=position_y(element#-1)
rz=position_z(element#-1)

rx=hx-px
ry=hy-py
rz=hz-pz
begin=1
endif

child_pos(element,element#-1,rx,ry,rz)
child_rot(element,element#-1,0,0,0)

หลังจากที่พี่ๆดูโค้ดง่ายๆนี้มา... อาจจะคิดในใจว่า "แล้วจะเขียนแยกไปทำไมเนี่ย... เชื่อมโมเดลไปเลยก็ได้นี่" อันนี้ที่ผมไม่เชื่อม ก็ทำเผื่อว่า อยากจะดัดหุ่นส่วนหัว ก็ไปดัดแปลงในวรรค child_rot(element,element#-1,xrot,yrot,zrot) อะไรประมาณนี้นะงับ

เฮ้อ... เป็นรีพลายที่ง่ายที่สุดในกระทู้อีกแย้วสินะเนี่ย แหะๆ
Logged

เกม ProjectNimbus



ติดตามผลงานพวกเราได้ที่นี่

http://www.facebook.com/pages/GameCrafterTeam/1818761
ปวีร์ (PentaX)
Veteran Developer
Sr. Member
*

จำนวน ชม/ไม่พอใจ: +47/-5
Offline Offline

Gender: Male
Posts: 369


Maid Lv.0


« Reply #53 on: 29 June 2006, 09:34:54 AM »

อ้อ... ลืมๆสรุปข้างบน

นิทานเรื่องนี้สอนให้รู้ว่า... ค่าไหนเป็นค่านิ่ง... ให้คำนวนใน Intialize Section ทีเดียวเลยครับ
Logged

เกม ProjectNimbus



ติดตามผลงานพวกเราได้ที่นี่

http://www.facebook.com/pages/GameCrafterTeam/1818761
Pages: 1 [2]   Go Up
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2013, Simple Machines Valid XHTML 1.0! Valid CSS!