2011-08-28 32 views
6

tôi có 2 C++ Lớp câu hỏi:Vượt qua một hàm thành viên lớp như một tham số chức năng

Câu hỏi 1 là: Làm thế nào tôi có thể làm cho nó để tôi có thể vượt qua một hàm thành viên lớp như một tham số trong một chức năng & làm thế nào tôi có thể chạy/gọi chức năng đó? Và làm thế nào tôi có thể làm tương tự với một hàm tĩnh lớp. Nó có thể dễ dàng hơn để hiểu câu hỏi của tôi bằng cách nhìn vào mã này:

class DebuggingManager 
{ 
    string testLog; 

    bool test1() 
    { 
     // run test & return whether it passed or failed 
    }  

    static bool test2() 
    { 

    } 

    // How can I call a member function? 
    void catalogueTest(string testName, bool DebuggingManager::*nMemberFunction) 
    { 
     testLog += "Status of " + testName + ": " + ((*)nMemberFunction()) + "\n"; 
    } 

    // How can I call a static function? 
    void catalogueTest(string testName, bool DebuggingManager::*nStaticFunction) 
    { 
     testLog += "Status of " + testName + ": " + DebuggingManager::nStaticFunction() + "\n"; 
    } 

    // how do I pass a member function or a static function as a parameter in another function 
    bool runTests() 
    { 
     catalogueTest("Test of member functin", test1()); 
     catalogueTest("Test of static functin", test2()); 
    } 

}; 

Câu hỏi thứ 2 là: Liệu nó xấu (hoặc nguy hiểm) thực hành để gọi một thành viên lớp (hoặc tĩnh) chức năng gián tiếp như trên. Tôi có một cảm giác thực sự là C++ xấu?

CHỈNH SỬA: Thực hiện lời khuyên Cảm ơn bạn đã trả lời, tôi đã cố thực hiện lời khuyên đó, rất nhiều thứ để có được đầu của tôi, điều này có đúng không?

// I have a feeling that ParameterList is incorect, would I pass the implicit obj as a parameter or is it done automatically like in normal object function calls? 
    typedef bool (DebuggingManager::*MemberPointerType)(ParameterList); 

    void catalogueTest(tstring testName, DebuggingManager* obj, MemberPointerType *nMemberFunction) 
    { 
     debugLog += _T("Status of ") + testName + _T(": ") + (obj->*nMemberFunction)() + _T("\r\n"); 
    } 

    void catalogueStaticTest(tstring testName, bool DebuggingManager::nStaticFunction) 
    { 
     debugLog += _T("Status of ") + testName + _T(": ") + nStaticFunction + _T("\r\n"); 
    } 
+1

Tôi ngạc nhiên này không được trả lời đã Có lẽ mọi người khác là mệt mỏi quá và không muốn để tra cứu cú pháp cho các con trỏ hàm thành viên trong lần thứ 500. –

+1

xem http: // stackoverflow.com/questions/2463112/pointer-to-ac-class-thành viên-chức năng-as-a-toàn cầu-chức năng-tham số cho phép một liên kết đến http://www.parashift.com/c++-faq-lite/pointers- to-members.html có cú pháp và những điều cần lưu ý khi khai báo/sử dụng con trỏ hàm thành viên bình thường và tĩnh. Về điều xấu, tôi có thể nói: có lẽ không phải trong một trường hợp cụ thể (như thử nghiệm, hoặc có lẽ là những người khác), nhưng sẽ không tốt khi thực hành hàng ngày để viết mã, vì nó phức tạp, và vì có cơ chế tốt hơn cho hầu hết mọi thứ bạn sẽ sử dụng chúng. – shelleybutterfly

Trả lời

13

Chức năng thành viên tĩnh của lớp cuối cùng không khác với chức năng thông thường. Chúng thực sự chỉ là cú pháp; hàm chỉ đơn giản là có tên bao gồm Classname::.

Thành viên không tĩnh là một vấn đề khác hoàn toàn. Có hai điều quan trọng cần nhớ về các hàm thành viên không tĩnh (NSMF).

Trước tiên, mọi chức năng thành viên không tĩnh đều có quyền truy cập vào các thành viên không tĩnh của lớp mà họ là thành viên. Điều này là có thể mặc dù bạn có thể có hai đối tượng của cùng một lớp xảy ra để lưu trữ dữ liệu khác nhau. Nếu bạn có hai đối tượng std::string, mỗi đối tượng sẽ lưu trữ các chuỗi khác nhau. Việc thực hiện một số find trên một chuỗi có thể trả lại kết quả tìm được trong một chuỗi nhưng không thể trả về kết quả khác.

Điều này là do mỗi NSMF có con trỏ this tiềm ẩn. this đề cập đến, không chỉ đơn thuần là một lớp, mà là đối tượng thực tế khi NSMF hoạt động. Khi bạn làm điều này:

std::string aString("data"); 
aString.find("da"); 

Các find chức năng nhận một đối số chuỗi, nhưng nó cũng được aString như this của nó. Mỗi lần find tìm kiếm các thành viên của lớp học, nó sẽ xem xét dữ liệu của aString.

Vì vậy, chúng ta hãy nhìn vào cuộc gọi tiềm năng của bạn trong một NSMF:

((*)nMemberFunction()) 

Đâu là đối tượng mà nó được trỏ this của nó từ đâu? Nếu không có một đối tượng, NSMF không thể truy cập vào các thành viên không tĩnh của đối tượng, vì không có đối tượng nào cho nó tìm thấy chúng. Điều này không hợp pháp.

Vì vậy, quy tắc số 1 về NSMF: Bạn phải gọi cho chúng với một phiên bản thực tế của lớp mà NSMF là thành viên của (hoặc một lớp dẫn xuất của chúng). Bạn không thể chỉ lấy một con trỏ NSMF và gọi nó như một con trỏ hàm; bạn phải gọi nó trên một đối tượng sống của loại đó.

Quy tắc số 2: cú pháp cho con trỏ NSMF thực sự xấu.

Để xác định một biến (hoặc đối số) có tên arg của NSMF kiểu con trỏ, bạn làm như sau:

ReturnType (ClassName::*arg)(ParameterList); 

đâu ReturnType là kiểu trả về của hàm, ParameterList là danh sách các đối số được thực hiện bởi chức năng và ClassName là tên của lớp mà con trỏ NSMF thuộc về.

Với sự xấu xí, nó thường là tốt nhất để bọc nó trong một typedef:

typedef ReturnType (ClassName::*MemberPointerType)(ParameterList); 

Như vậy tạo typedef MemberPointerType, mà là một con trỏ NSMF.

Với một đối tượng tên là object, mà là loại ClassName, bạn sẽ gọi con trỏ thành viên arg như sau:

ReturnType value = (object.*arg)(Params); 

đâu Params là các đối số bạn muốn vượt qua. Nếu object là con trỏ đến số ClassName thay vì tham chiếu hoặc giá trị, thì bạn sử dụng object->*arg thay thế.

Một điều nữa: bạn phải sử dụng & để lấy tên con trỏ thành viên. Không giống như con trỏ hàm, con trỏ NSMF không tự động chuyển thành con trỏ thành viên. Bạn phải yêu cầu họ trực tiếp. Vì vậy, nếu ClassName có thành viên gọi là Chức năng này sẽ phù hợp với trên ReturnTypeParameterList, bạn sẽ điền arg như sau:

arg = &ClassName::Function; 

Rule # 3: con trỏ thành viên không tĩnh là không con trỏ. Có, chúng có thể được đặt thành NULL (về mặt kỹ thuật, chúng có thể được đặt thành 0), nhưng chúng là không phải là giống như con trỏ.

Hầu hết các trình biên dịch C và C++ thực sự sẽ cho phép bạn truyền một con trỏ hàm tới void* và ngược lại. Các tiêu chuẩn xem xét hành vi không xác định này, nhưng nó không hoàn toàn không biết để làm điều này. Bạn hoàn toàn không thể làm điều này với một con trỏ NSMF, trên hầu như tất cả các trình biên dịch C++. Thật vậy, sizeof(MemberPointerType) có thể sẽ không có cùng kích thước với void*.

Vì vậy, con trỏ NSMF không phải là con trỏ thông thường. Đừng đối xử với họ như vậy.

+0

Bạn sẽ không phải nói '(đối tượng. * Arg) (Params);' bởi vì ưu tiên của toán tử? –

+0

@Kerrek SB: Có, '. *' Có mức ưu tiên thấp hơn so với cuộc gọi hàm. –

+0

@Nicol Bolas: Cảm ơn bạn đã trả lời. Tôi đã cố gắng thực hiện những gì bạn nói, nó có đúng không? – user593747

2

Trong C++ 11, họ đã tìm ra cách để thực hiện điều đó. Đọc về các hoạt động functionbind.

Trong trường hợp của bạn, giả sử bạn muốn gọi các hàm thuộc loại test1. . (Tức là hình thức bool FunctionName()

void catalogueTest(string testName, std::function<bool()> myFunction) 
{ 
    testLog += "Status of " + testName + ": " + myFunction() + "\n"; 
} 

Và gọi nó là như thế này:.

DebuggingManager myInstance 
myInstance->catalogueTest("TestName", std::bind(&DebuggingManager::test1, myInstance)); 
Các vấn đề liên quan