在類裏面定義了幾個線程函數,用以訪問類成員,編譯的時候遇到了“error: invalid use of non-static member function”。測試代碼如下:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
class MyClass
{
public:
MyClass()
{
num = 0;
}
int num;
// Define a thread function
void* increase_num(void *param)
{
while(num < 100)
{
num++;
usleep(10000);
}
return NULL;
}
// Define a start function to call the thread function
void run()
{
pthread_t thread_inc;
pthread_create(&thread_inc, NULL, increase_num, NULL);
pthread_join(thread_inc, NULL);
}
};
int main()
{
MyClass my_class;
my_class.run();
std::cout << my_class.num << std::endl;
return 0;
}
編譯結果:
分析發現,線程函數在編譯時必須知道函數的入口地址,而類中的普通成員函數入口地址在類定義階段是未知的,只有在類實例化之後纔會爲其分配內存空間。
C++程序的內存格局通常包含四個區,分別是:全局數據區、代碼區、棧區、堆區。
類成員函數是放在代碼區。類的靜態成員在類定義時已經在全局數據區分配了內存;而非靜態成員則是在實例化過程中才在棧區或堆區爲其分配內存,爲每個對象生成一個拷貝。類的非靜態成員函數都內涵了一個指向類對象的this指針,只有生成類對象後this指針纔有實際值。
那麼,怎樣解決類成員函數作爲線程函數的問題呢?網上搜了一下,有幾種方式,嘗試了兩種比較簡單的方式,都行得通。
1. 將線程函數定義成靜態類型
如果把線程函數改成靜態成員函數會怎樣?我們把上面代碼中的語句
void* increase_num(void *param)
改成
static void* increase_num(void *param)
編譯結果:
靜態成員函數不能訪問普通的類成員變量!
靜態成員函數因爲在類定義的時候就已經分配了內存,因此自然可以作爲線程函數使用。但是存在一個問題,靜態成員函數只能直接訪問類的靜態成員,而無法訪問類的非靜態成員。這是因爲,靜態成員函數在編譯時會被編譯器轉換爲不帶this指針的全局函數,因此無法直接訪問類的普通成員。瞭解了這一點,那麼我們可以通過傳遞this指針的方式來解決這個問題。
修改原始代碼中的類定義:
class MyClass
{
public:
MyClass()
{
num = 0;
}
int num;
// Define a thread function
static void* increase_num(void *param)
{
MyClass *p = (MyClass *)param;
while(p->num < 100)
{
p->num++;
usleep(10000);
}
return NULL;
}
// Define a start function to call the thread function
void run()
{
pthread_t thread_inc;
pthread_create(&thread_inc, NULL, increase_num, this);
pthread_join(thread_inc, NULL);
}
};
編譯通過,運行結果正常。
2. 將線程函數定義爲友元函數
友元函數定義在類外部,並非類的成員函數,但它可以訪問類的private和protected成員,因此也可以將線程函數定義爲類的友元函數。程序代碼如下:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
class MyClass
{
public:
MyClass()
{
num = 0;
}
// Define a thread function
friend void* increase_num(void *param);
// Define a start function to call the thread function
void run();
void print_num();
private:
int num;
};
void* increase_num(void *param)
{
MyClass *p = (MyClass *)param;
while(p->num < 100)
{
p->num++;
usleep(10000);
}
return NULL;
}
void MyClass::run()
{
pthread_t thread_inc;
pthread_create(&thread_inc, NULL, increase_num, (void*)this);
pthread_join(thread_inc, NULL);
}
void MyClass::print_num()
{
std::cout << num << std::endl;
}
int main()
{
MyClass my_class;
my_class.run();
my_class.print_num();
return 0;
}
編譯、執行都OK!