2010-08-25 6 views
12

Đôi khi bạn chạy vào mã có dấu ngoặc nhọn thêm, không có gì liên quan đến phạm vi, chỉ dành cho khả năng đọc và tránh nhầm lẫn.Dấu ngoặc nhọn thêm trong mã C++

Ví dụ: những nơi

GetMutexLock(handle) ; 
{ 
    // brace brackets "scope" the lock, 
    // must close block/remember 
    // to release the handle. 
    // similar to C#'s lock construct 
} 
ReleaseMutexLock(handle) ; 

khác tôi đã thấy nó là:

glBegin(GL_TRIANGLES) ; 
{ 
    glVertex3d(..) ; 
    glVertex3d(..) ; 
    glVertex3d(..) ; 
} // must remember to glEnd! 
glEnd() ; 

này giới thiệu một lỗi biên dịch nếu mutex không giải phóng (giả sử bạn nhớ cả} và Gọi Release()).

  1. Đây có phải là thực tiễn không tốt? Tại sao?
  2. Nếu nó không phải là một, nó có thể thay đổi cách mã được biên soạn hoặc làm cho nó chậm hơn?
+2

Trong OpenGL, thực tiễn này có nghĩa là tăng cường khả năng đọc mã. Dấu ngoặc trên glBegin() gọi hành vi như là một delimitator trực quan của nhóm đỉnh cần được rút ra. – karlphillip

+0

Đó là một thực tế khủng khiếp! Tại sao bất cứ ai sẽ sử dụng các dấu phân cách khối để tạo ra một khối mã nổi bật là ngoài tôi. Tôi chỉ có thể tưởng tượng tất cả các vi tối ưu hóa vi mô mà họ đang thiếu ...tất cả hy sinh cho một cái gì đó bên cạnh điểm như dễ đọc. Thế giới đang đến với cái gì ???? –

+1

@ Noah, tôi đã đổi tên thành bạn là Sarcastic Cat. – bobobobo

Trả lời

30

Niềng răng chính là tốt, tất cả những gì họ làm là phạm vi giới hạn và bạn sẽ không làm chậm bất cứ điều gì. Nó có thể được xem là sạch hơn. (Luôn luôn thích mã sạch hơn mã nhanh, nếu nó sạch hơn, đừng lo lắng về tốc độ cho đến khi bạn hồ sơ.)


Nhưng đối với tài nguyên với đó là thực tế xấu vì bạn đã đặt mình vào một vị trí để rò rỉ tài nguyên. Nếu bất cứ điều gì trong khối ném hoặc trả lại, bang bạn đã chết.

Sử dụng Phạm vi-bound Quản lý tài nguyên (SBRM, còn được gọi là RAII), làm hạn chế một nguồn lực để một phạm vi, bằng cách sử dụng các destructor:

class mutex_lock 
{ 
public: 
    mutex_lock(HANDLE pHandle) : 
    mHandle(pHandle) 
    { 
     //acquire resource 
     GetMutexLock(mHandle); 
    } 

    ~mutex_lock() 
    { 
     // release resource, bound to scope 
     ReleaseMutexLock(mHandle); 
    } 

private: 
    // resource 
    HANDLE mHandle; 

    // noncopyable 
    mutex_lock(const mutex_lock&); 
    mutex_lock& operator=(const mutex_lock&); 
}; 

Vì vậy, bạn nhận được:

{ 
    mutex_lock m(handle); 
    // brace brackets "scope" the lock, 
    // AUTOMATICALLY 
} 

Làm điều này sẽ tất cả các tài nguyên, tài nguyên này sạch hơn và an toàn hơn. Nếu bạn đang ở trong một vị trí để nói "Tôi cần phải phát hành tài nguyên này", bạn đã làm sai; chúng sẽ được xử lý tự động.

+7

+1 cho tính chính xác ... và từ viết tắt SBRM mới này mà tôi không biết! Tốt hơn nhiều so với RAII khi thể hiện ý định đó. Và thậm chí không có vẻ là một trang wikipedia cho nó! –

3

Thực tiễn không tồi. Nó không làm chậm hơn; nó chỉ là một cách để cấu trúc mã.

Làm cho trình biên dịch thực hiện kiểm tra lỗi & thực thi cho bạn luôn là điều tốt!

+0

+1 để nhận trình biên dịch thực hiện công việc của bạn ... –

+2

thật không may khi tự giải phóng tài nguyên là dễ bị lỗi ... –

1

Bất cứ điều gì cải thiện khả năng đọc IMHO là thực hành tốt. Nếu thêm niềng răng giúp với khả năng đọc, sau đó đi cho nó!

Thêm dấu ngoặc bổ sung sẽ không thay đổi cách mã được biên dịch. Nó sẽ không làm cho việc chạy chương trình chậm hơn.

+0

{I {think {that {adding {braces {can {reduce} readability}}}}}} . Nghiêm túc, niềng răng nói với tôi "có một cấu trúc điều khiển ở đây". Khi không có, tôi vẫn tiếp tục tìm kiếm nó một lúc. Gây cho tôi để chi tiêu nhận thức và một phần nhỏ của một thứ hai tìm kiếm một cái gì đó mà không phải là không có khả năng đọc. –

+0

@ David - Tôi không nghĩ rằng OP đã suy nghĩ thêm một số tùy ý của niềng răng vào mã như trong ví dụ của bạn. Tôi đã đề cập đến cách OP được sử dụng niềng răng. Rõ ràng là thêm niềng răng như trong ví dụ của bạn không giúp với khả năng đọc. – Starkey

+0

Sử dụng niềng răng cho bất kỳ mục đích nào khác ngoài việc chỉ ra cấu trúc điều khiển không giúp ích cho khả năng đọc, theo như tôi quan tâm. Điều này không được tính là cấu trúc điều khiển. Sử dụng chúng để giới hạn phạm vi của tài nguyên được phân bổ RAII là. –

17

Niềng răng ảnh hưởng đến phạm vi biến. Theo như tôi biết đó là tất cả những gì họ làm.

Có, điều này có thể ảnh hưởng đến cách chương trình được biên dịch. Destructors sẽ được gọi ở cuối khối thay vì đợi đến khi kết thúc hàm.

Thường thì đây là những gì bạn muốn làm. Ví dụ, GetMutexLock và ReleaseMutexLock của bạn sẽ tốt hơn C++ code viết như thế này:

struct MutexLocker { 
    Handle handle; 
    MutexLocker(handle) : handle(handle) { GetMutexLock(handle); } 
    ~MutexLocker() { ReleaseMutexLock(handle); }  
}; 
... 
{ 
    MutexLocker lock(handle); 
    // brace brackets "scope" the lock, 
    // must close block/remember 
    // to release the handle. 
    // similar to C#'s lock construct 
} 

Sử dụng nhiều kiểu ++ C này, khóa được giải phóng tự động vào cuối khối. Nó sẽ được phát hành trong mọi trường hợp, kể cả trường hợp ngoại lệ, với ngoại lệ của setjmp/longjmp hoặc một chương trình bị lỗi hoặc hủy bỏ.

+0

Đây là cách đi đúng đắn. – Puppy

+12

Bạn cần đặt tên cho tủ khóa, hoặc đó chỉ là tạm thời sẽ chết ngay lập tức. Và có lẽ bạn nên thay đổi các bình luận sau nó để phù hợp với việc quản lý tài nguyên mới. – GManNickG

+0

Cú pháp chính xác là 'MutexLocker handle;', không phải là 'MutexLocker (handle);'. – kennytm

2

Nó sẽ không tạo sự khác biệt cho mã được biên dịch, ngoài việc gọi bất kỳ trình phá hủy nào ở cuối khối đó thay vì kết thúc khối xung quanh, trừ khi trình biên dịch hoàn toàn mất trí.

Cá nhân, tôi sẽ gọi đó là hành vi xấu; cách để tránh các loại lỗi mà bạn có thể thực hiện ở đây là sử dụng quản lý tài nguyên phạm vi (đôi khi được gọi là RAII), không sử dụng các lời nhắc đánh máy dễ bị lỗi. Tôi sẽ viết mã như một cái gì đó như

{ 
    mutex::scoped_lock lock(mutex); 
    // brace brackets *really* scope the lock 
} // scoped_lock destructor releases the lock 

{ 
    gl_group gl(GL_TRIANGLES); // calls glBegin() 
    gl.Vertex3d(..); 
    gl.Vertex3d(..); 
    gl.Vertex3d(..); 
} // gl_group destructor calls glEnd() 
1

Điều này hữu ích hơn nhiều (IMHO) trong C++ với các đối tượng hủy; ví dụ bạn đang ở trong C.

Hãy tưởng tượng nếu bạn đã thực hiện một lớp MutexLock:

class MutexLock { 
private: 
    HANDLE handle; 
public: 
    MutexLock() : handle(0) { 
     GetMutexLock(handle); 
    } 

    ~MutexLock() { 
     ReleaseMutexLock(handle); 
    } 
} 

Sau đó, bạn có thể phạm vi mà khóa để chỉ mã mà cần nó bằng cách cung cấp một phạm vi mới với niềng răng:

1

Nếu bạn đang đặt mã vào niềng răng, có lẽ bạn nên tách nó ra thành phương pháp riêng của nó. Nếu đó là một đơn vị riêng biệt, tại sao không gắn nhãn nó và phá vỡ nó ra chức năng? Điều đó sẽ làm cho nó rõ ràng những gì khối không, và những người sau này đọc mã sẽ không phải tìm ra.

3

Vị trí cụ thể của { ... } trong ví dụ ban đầu của bạn phục vụ hoàn toàn như định dạng đường bằng cách làm cho nó rõ ràng hơn khi một nhóm các câu lệnh liên quan logic bắt đầu và kết thúc ở đâu. Như được hiển thị trong các ví dụ của bạn, nó không ảnh hưởng đến mã được biên dịch.

Tôi không biết ý bạn là gì bởi "điều này giới thiệu một lỗi trình biên dịch nếu mutex không được giải phóng". Điều đó không đúng. Việc sử dụng { ... } không thể và sẽ không giới thiệu bất kỳ lỗi trình biên dịch nào.

Cho dù thực hành tốt là vấn đề sở thích cá nhân. Có vẻ OK. Ngoài ra, bạn có thể sử dụng các nhận xét và/hoặc thụt đầu dòng để cho biết nhóm các câu lệnh hợp lý trong mã, mà không cần thêm bất kỳ { ... }.

Có nhiều kỹ thuật dựa trên phạm vi khác nhau, một số đã được minh họa bằng các câu trả lời khác ở đây, nhưng những gì bạn có trong OP của bạn thậm chí không nhìn từ xa như thế. Một lần nữa, những gì bạn có trong OP của bạn (như được hiển thị) hoàn toàn là một thói quen định dạng nguồn với dư thừa { ... } mà không ảnh hưởng đến mã được tạo ra.

+0

Dường như bạn là người duy nhất nhận xét về vấn đề "lỗi biên dịch" - cũng được quan sát. Ấn tượng của tôi là áp phích đã thấy các macro được sử dụng như thế này: BEGIN_X // thực hiện công cụ END_X nơi thay thế BEGIN_X và END_X bao gồm {&}, do đó END_X thiếu sản xuất lỗi trình biên dịch. Đồ xấu xí. –

+0

@Tony: Boost có một vài trong số đó. Tôi đã từng có khoảng một trăm trang giá trị của các lỗi vì tôi đã quên một. "Stuff xấu xí" không bắt đầu mô tả nó :) –