2009-06-29 3 views
20

Tôi đang làm việc với một cơ sở mã lớn có sử dụng những sản phẩm sau xây dựng suốtSử dụng quá nhiều của `this` trong C++

class MyClass 
{ 
public: 
    void f(int x); 
private: 
    int x; 
}; 


void MyClass::f(int x) 
{ 
' 
' 
    this->x = x; 
' 
' 
} 

Cá nhân, tôi muốn luôn được sử dụng và do đó thích hình thức

class MyClass 
{ 
public: 
    void f(int x); 
private: 
    int _x; 
}; 


void MyClass::f(int x) 
{ 
' 
' 
    _x = x; 
' 
' 
} 

Lý do tôi thích thứ hai là nó gọn gàng hơn (ít mã = ​​ít lỗi tiềm năng hơn), và tôi không thích có nhiều biến cùng tên trong phạm vi cùng lúc mà tôi có thể tránh nó. Điều đó nói rằng, tôi nhìn thấy việc sử dụng cũ hơn và thường xuyên hơn những ngày này. Có cách tiếp cận thứ hai nào mà tôi không biết? (ví dụ như hiệu ứng trên thời gian biên dịch, sử dụng với mã templated, vv ...) Có phải những lợi thế của một trong hai cách tiếp cận đáng kể đủ công đức một refactor khác? Lý do tôi hỏi, rằng trong khi tôi không thích cách tiếp cận thứ hai có trong mã, số lượng công sức và nguy cơ liên quan đến việc đưa thêm các lỗi không hoàn toàn xứng đáng với một trình tái cấu trúc.

+5

Bạn không có câu hỏi gì liên quan đến việc tái cấu trúc hoặc mã hóa. Điều này chỉ liên quan đến quy ước đặt tên mà bạn chọn tuân thủ - điều này buộc bạn phải sử dụng từ khóa 'this' để tránh sự mơ hồ (và sẽ không cần thiết nếu tham số được đặt tên là' y'). – Groo

+0

@Groo, IMO, chọn quy ước đặt tên có khả năng dẫn đến sự mơ hồ của tên biến và do đó lỗi là mùi, vì bị buộc sử dụng 'điều này' để giải quyết sự mơ hồ. Nếu đó là một mùi, và đó là mã tôi có khả năng được làm việc trên, nó có khả năng dẫn đến một refactor. –

+0

Tôi thích sử dụng _x và x theo cách khác, tức là _x là tạm thời, chỉ vì nó là một phím tắt ít hơn. –

Trả lời

27

Phiên bản của bạn là một chút bụi, nhưng trong khi bạn đang ở đó, tôi sẽ:

  1. Tránh hàng đầu gạch dưới: _x là ok cho đến khi ai đó chọn _MyField mà là một cái tên dành riêng. Dấu gạch dưới ban đầu theo sau là chữ cái viết hoa không được cho phép dưới dạng tên biến. Xem: What are the rules about using an underscore in a C++ identifier?
  2. Đặt thuộc tính ở chế độ riêng tư hoặc được bảo vệ: thay đổi an toàn nếu nó biên dịch và bạn sẽ đảm bảo setter của mình sẽ được sử dụng.
  3. Câu chuyện này- có sử dụng, ví dụ trong mã templated để làm cho tên trường phụ thuộc vào loại của bạn (có thể giải quyết một số vấn đề tra cứu).

Một ví dụ nhỏ của độ phân giải tên được cố định bằng cách sử dụng một this- rõ ràng> (thử nghiệm với g ++ 3.4.3):

#include <iostream> 
#include <ostream> 

class A 
{ 
public: 
    int g_; 
    A() : g_(1) {} 
    const char* f() { return __FUNCTION__; } 
}; 

const char* f() { return __FUNCTION__; } 
int g_ = -1; 

template < typename Base > 
struct Derived : public Base 
{ 
    void print_conflicts() 
    { 
    std::cout << f() << std::endl; // Calls ::f() 
    std::cout << this->f() << std::endl; // Calls A::f() 
    std::cout << g_ << std::endl; // Prints global g_ 
    std::cout << this->g_ << std::endl; // Prints A::g_ 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    Derived<A>().print_conflicts(); 
    return EXIT_SUCCESS; 
} 
+0

Trên thực tế, phần lớn mã thực sự là MFC và kết thúc là m_ theo hầu hết MFC. Tôi đã đi với gạch dưới đơn giản thay vì để làm cho câu hỏi trung tính hơn một chút, và tương tự như trái ra tư nhân vì nó là một vấn đề khác nhau. 1 cho điểm về mẫu, có thể bạn có thể cung cấp một ví dụ ngắn vì nó thực sự sẽ mang lại lợi ích cho câu trả lời. –

+2

Dấu gạch dưới hàng đầu là tốt miễn là ký tự tiếp theo là chữ thường. – GManNickG

+4

Đúng, nhưng người bảo trì theo dõi bạn có thể không biết điều đó. – bltxd

0

Tôi đồng ý. Tôi không thích quy ước đặt tên đó - tôi thích một nơi có sự khác biệt rõ ràng giữa các biến thành viên và biến cục bộ. Điều gì xảy ra nếu bạn rời khỏi this?

1

Tôi luôn sử dụng quy ước đặt tên m_. Mặc dù tôi không thích "ký hiệu Hungary" nói chung, tôi thấy rất hữu ích khi thấy rõ ràng nếu tôi đang làm việc với dữ liệu thành viên của lớp. Ngoài ra, tôi thấy sử dụng 2 tên biến giống hệt nhau trong cùng một phạm vi quá dễ bị lỗi.

1

class MyClass{ 
public: 
    int x; 
    void f(int xval); 
}; 
// 
void MyClass::f(int xval){ 
    x = xval; 
} 
+0

Sự khác biệt là gì? – Groo

+0

Anh ta không đặt tên hàm paramter giống như biến thành viên - do đó không quá tải tên và buộc điều này-> – Pod

+2

Tôi không nghĩ nó mang lại bất kỳ đối số hữu ích nào cho cuộc thảo luận – Newtopian

0

Theo tôi này có xu hướng thêm lộn xộn vào mã, vì vậy tôi có xu hướng sử dụng tên biến khác nhau (tùy thuộc vào quy ước, nó có thể là một dấu gạch dưới, m_, bất cứ điều gì).

3

Việc sử dụng 'điều này' được khuyến khích bởi các tiêu chuẩn mã hóa của Microsoft C#. Nó cung cấp cho một mã rõ ràng, và được dự định là một tiêu chuẩn về việc sử dụng m_ hoặc _ hoặc bất cứ điều gì khác trong các biến thành viên.

Thành thật mà nói, tôi thực sự không thích gạch dưới trong tên, tôi đã sử dụng tiền tố tất cả các thành viên của mình bằng một chữ 'm'.

+4

Cảm ơn bạn đã chia sẻ tùy chọn đặt tên biến với chúng tôi tất cả các. – Eric

+1

Các tiêu chuẩn mã hóa C# :) Để trả lời câu hỏi một cách rõ ràng hơn, tôi sẽ không coi nó là một mùi mã, bởi vì nó thậm chí còn được coi là thực hành tốt trong ngôn ngữ khác mà IMHO tương tự ưu và khuyết điểm áp dụng. – Jem

+1

@Eric nó đã được chứng minh rằng đó là một vấn đề của hương vị. Gạch dưới bất kỳ nơi nào trong mã thư viện không chuẩn có xu hướng giảm khả năng hiển thị và có thể được coi là mùi mã, so với việc sử dụng mã này. Điều này tất nhiên chỉ là một ý kiến. – Jem

10

Dòng đặt tên không có gì để làm với một codesmell. Như Neil cho biết, khả năng hiển thị trường là mã duy nhất ở đây.

Có nhiều bài báo liên quan đến quy ước đặt tên trong C++:

, vv

+0

Tôi đã không thực sự hỏi liệu quy ước đặt tên có phải là một mùi mã hay không, tôi đã hỏi liệu việc sử dụng quá mức 'this->' có phải là một mùi mã hay không. Tôi nghi ngờ Marco (dưới đây) là đúng trong đó thực hành thêm 'this->' xuất phát từ mong muốn sử dụng Intellisense/Code hoàn thành trong một số IDE. Nếu đây là trường hợp hơn tôi tin rằng nó có thể là một mùi mã. –

3

Rất nhiều người sử dụng này bởi vì trong họ IDE nó sẽ tạo ra một danh sách các số nhận dạng của lớp hiện tại bật lên.

Tôi biết tôi làm trong BCB.

Tôi nghĩ ví dụ bạn cung cấp với xung đột đặt tên là ngoại lệ. Mặc dù vậy, trong Delphi, các nguyên tắc kiểu dáng sử dụng một tiền tố (thường là "a") cho các tham số để tránh chính xác điều này.

+1

Thường nhanh hơn khi gõ Ctrl + Space hoặc bất kỳ phím tắt tự động hoàn chỉnh nào cho IDE của bạn; Visual Studio thường không yêu cầu điều này -> để xem các phương thức và các biến trong phạm vi. –

+0

+1 vì lý do có thể xảy ra nhất là tại sao tôi thấy điều này quá nhiều trong cơ sở mã. Điều thú vị là IDE khuyến khích mọi người viết mã khác với cách họ sẽ làm điều đó trong một trình soạn thảo đơn giản hơn. –

+0

@Pete, đồng ý nhưng tôi nghĩ Marco có lẽ đã đưa ra lý do tại sao nó đang xảy ra quá nhiều. –

0

Càng bình thường trong C++ cho thành viên được khởi tạo khi xây dựng bằng trình khởi tạo.

Để làm điều đó, bạn phải sử dụng tên khác với tên của biến thành viên.

Vì vậy, mặc dù tôi muốn sử dụng Foo(int x) { this.x = x; } trong Java, tôi sẽ không có trong C++.

Mùi thực sự có thể là thiếu sử dụng các trình khởi tạo và phương pháp không làm gì khác ngoài việc biến đổi các biến thành viên, thay vì việc sử dụng chính số this -> x.

Bất cứ ai biết tại sao nó là thực tế phổ biến trong mỗi C++ cửa hàng tôi đã nhập để sử dụng tên gọi khác nhau cho các đối số nhà xây dựng với các biến thành viên khi sử dụng với initialisers? đã có một số trình biên dịch C + + mà không hỗ trợ nó?

+1

Không bạn không: Foo :: Foo (int x): x (x) {} // hoạt động như dự định, x đầu tiên và thứ ba là đối số, x thứ hai là this-> x – MSalters

0
class MyClass 
{ 
public: 
    int m_x; 
    void f(int p_x); 
}; 


void MyClass::f(int p_x) 
{ 
    m_x = p_x; 
} 

... là cách ưa thích của tôi bằng cách sử dụng tiền tố phạm vi. m_ cho thành viên, p_ cho tham số (một số sử dụng a_ cho đối số thay thế), g_ cho global và đôi khi l_ cho local nếu nó giúp dễ đọc.

Nếu bạn có hai biến xứng đáng với cùng tên thì điều này có thể giúp ích rất nhiều và tránh phải tạo ra một số biến thể ngẫu nhiên về ý nghĩa của nó chỉ để tránh định nghĩa lại. Hoặc thậm chí tệ hơn, 'x2, x3, x4, vv' sợ hãi ...

+0

Tôi thường thấy p_ được sử dụng để chỉ ra một con trỏ trong c + + có thể gây nhầm lẫn ở đây. –

-2

Nếu bạn có vấn đề với quy ước đặt tên, bạn có thể thử sử dụng một cái gì đó giống như folowing.

class tea 
{ 
public: 
    int cup; 
    int spoon; 
    tea(int cups, int spoons); 
}; 

hoặc

class tea 
{ 
public: 
    int cup; 
    int spoon; 
    tea(int drink, int sugar); 
}; 

Tôi nghĩ rằng bạn đã có ý tưởng. Về cơ bản nó đặt tên cho các biến khác nhau nhưng "giống nhau" theo nghĩa logic. Tôi hy vọng nó sẽ giúp.

+0

Tôi nhầm lẫn câu hỏi cho một thứ khác. Bây giờ tôi thấy rằng bạn thực sự muốn biết cách tiếp cận khác với ký hiệu. – Secko

2

Sử dụng 'điều này' theo cách này là IMO không phải là mã nguồn, nhưng chỉ đơn giản là sở thích cá nhân. Do đó, nó không quan trọng bằng tính nhất quán với phần còn lại của mã trong hệ thống. Nếu mã này không nhất quán, bạn có thể thay đổi mã này để khớp với mã khác. Nếu bằng cách thay đổi nó, bạn sẽ giới thiệu mâu thuẫn với phần lớn phần còn lại của mã, điều đó rất tệ và tôi sẽ để nó một mình.

Bạn không muốn vào vị trí chơi mã quần vợt, nơi ai đó thay đổi thứ gì đó hoàn toàn để làm cho nó trông "đẹp" chỉ dành cho người khác đi cùng với thị hiếu khác nhau, sau đó thay đổi lại.

-1

Tôi không thích sử dụng "điều này" vì nó không hoạt động. Nếu bạn đang lập trình trong C cũ tốt (nhớ C?), Và bạn muốn bắt chước một số đặc điểm của OOP, bạn tạo một cấu trúc với một số thành viên (chúng tương tự như các thuộc tính của đối tượng của bạn) và bạn tạo một tập hợp các hàm mà tất cả đều lấy một con trỏ tới cấu trúc đó làm đối số đầu tiên của chúng (chúng tương tự như các phương thức của đối tượng đó).

(Tôi nghĩ rằng cú pháp typedef này là đúng nhưng nó được một thời gian ...)

typedef struct _myclass 
{ 
    int _x; 
} MyClass; 

void f(MyClass this, int x) 
{ 
    this->_x = x; 
} 

Trong thực tế, tôi tin rằng ++ biên dịch C cũ thực sự sẽ biên dịch mã của bạn để mẫu ở trên và sau đó chỉ cần vượt qua nó để trình biên dịch C. Nói cách khác - C++ ở một mức độ nào đó chỉ là cú pháp. Vì vậy, tôi không chắc chắn lý do tại sao bất cứ ai muốn chương trình trong C + + và quay trở lại để sử dụng một cách rõ ràng "này" trong mã - có thể đó là "cú pháp Nutrisweet"

3

Cảm giác cá nhân của tôi là đấu tranh với quy ước mã hóa hiện tại không nên làm. Như Sutter/Alexandrescu đặt nó trong cuốn sách 'Quy tắc mã hóa C++' của họ: đừng đổ mồ hôi những thứ nhỏ nhặt. Bất cứ ai cũng có thể đọc cái này hay cái kia, cho dù có cái 'this->' hay '_' hay cái gì cả. Tuy nhiên, nhất quán trong quy ước đặt tên là điều bạn thường muốn, do đó, gắn bó với một quy ước ở một phạm vi nào đó (ít nhất là phạm vi tệp, lý tưởng là toàn bộ cơ sở mã, tất nhiên) được coi là thực hành tốt. Bạn đã đề cập rằng phong cách này được sử dụng xuyên suốt một cơ sở mã lớn hơn, vì vậy tôi nghĩ việc trang bị thêm một quy ước khác sẽ là một ý tưởng tồi.

Nếu bạn, sau khi tất cả, tìm thấy có một lý do chính đáng để thay đổi nó, không làm điều đó bằng tay. Trong trường hợp tốt nhất, IDE của bạn hỗ trợ các loại 'tái cấu trúc' này. Nếu không, hãy viết một kịch bản để thay đổi nó. Tìm kiếm & thay thế phải là tùy chọn cuối cùng. Trong mọi trường hợp, bạn nên có một bản sao lưu (kiểm soát nguồn) và một số loại cơ sở thử nghiệm tự động. Nếu không, bạn sẽ không vui vẻ với nó.

0

Hôm nay, hầu hết các trình chỉnh sửa IDE tô màu các biến của bạn để biểu thị các thành viên lớp của các biến cục bộ. Vì vậy, IMO, không phải là tiền tố hoặc 'this->' nên được yêu cầu để dễ đọc.