VC里尝试回调类成员函数

C#与VC线程启动对比
本文通过实例对比了C#与VC中启动线程并调用类成员函数的不同方式,发现VC环境下若成员变量被修改,则转换失败,限制了实际应用。

  自从用C#以后,对一句话启动线程引用本类的成员函数很是感觉好用,对比以前VC的类里不得不专门建个静态函数来转向到成员函数,相差太多,打算封装成一句话启动的方法,经过在网上搜索,选用下面的方案:

  http://www.cppblog.com/justin-shi/archive/2008/07/22/56813.html

  摘要如下:

类成员方法是一个比较特殊的函数,它在编译时会被转化成普通函数,比如有TMyClass类:
class TMyClass{
    void Func();
};

这个TMyClass::Func最终会转化成 void Func(TMyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。

 

  在进行封装时,一开始确实如文章所言,通过对回调函数的引用,正确的转到了类成员,但很快就发现了不对,于是写了个测试例子:(读者要建个工程, 调用Test方法。)

 

1:
class clsTest{
public:
 clsTest(){
  m_i = 0;
 };
 void Test(){
  Test2();
 }
protected:
 int m_i;

 void CallBack(){
  TRACE1("%x>",this);
 }
 void Test2(){
  //m_i = 3;
  union {
   void (*Proc1)(void* pv);
   void (clsTest::*mProc)();
  } pp;
  pp.mProc = CallBack;
  TRACE1("<%x,",this);
  pp.Proc1(this);
 }
};

输出窗显示两个this指针:<12f6ec,12f6ec>,说明转换是正确的。

2:把上面的注释//m_i = 3;取消,如下:
class clsTest{
public:
 clsTest(){
  m_i = 0;
 };
 void Test(){
  Test2();
 }
protected:
 int m_i;

 void CallBack(){
  TRACE1("%x>",this);
 }
 void Test2(){
  m_i = 3;
  union {
   void (*Proc1)(void* pv);
   void (clsTest::*mProc)();
  } pp;
  pp.mProc = CallBack;
  TRACE1("<%x,",this);
  pp.Proc1(this);
 }
};

输出窗显示两个this指针:<12f6ec,0>,转换失败。


3:如果Test2()中的m_i=3移到Test中,如下:
class clsTest{
public:
 clsTest(){
  m_i = 0;
 };
 void Test(){
  m_i = 3;
  Test2();
 }
protected:
 int m_i;

 void CallBack(){
  TRACE1("%x>",this);
 }
 void Test2(){
  union {
   void (*Proc1)(void* pv);
   void (clsTest::*mProc)();
  } pp;
  pp.mProc = CallBack;
  TRACE1("<%x,",this);
  pp.Proc1(this);
 }

};
输出窗显示两个this指针:<12f6ec,12f6ec>,说明转换是正确的。


在上述例子中m_i = 3改成 int x = m_i;也就是不变更成员变量,结果也是一样。

以上如果启动回调函数的函数里不访问或者变更成员变量,就可以正确的回调类成员函数。
但这样就没有实用价值了。
结论是:不敢用。

 

<think>问题:以上代码显示std没有成员function 原因分析:在C++代码中使用`std::function`时遇到编译错误,提示“std没有成员function”,通常有以下几种原因: 1. 编译器不支持C++11或更高版本。 2. 没有包含必要的头文件<functional>。 3. 拼写错误,如将`function`拼写成了`function`(注意拼写)或`Function`(大小写错误)。 4. 在命名空间`std`中确实没有`function`,这通常是由于编译器或标准库的实现问题,但现代编译器一般都支持。 解决方案: 1. 确保包含了头文件<functional>。 ```cpp #include <functional> ``` 2. 确保编译器启用了C++11或更高标准。在编译时添加参数,例如: - g++/clang: `-std=c++11` 或更高 - MSVC: 确保项目属性中设置C++标准为C++11或更高。 3. 检查拼写,确保使用的是`std::function`(全小写)。 4. 如果以上都正确,检查编译器是否支持。可以尝试使用以下代码测试: ```cpp #include <functional> #include <iostream> int main() { std::function<void()> func = []{ std::cout << "Hello, World!\n"; }; func(); return 0; } ``` 如果测试代码编译运行成功,则说明环境支持,原问题可能是其他原因。 5. 如果使用的是较老的编译器(如Visual Studio 2010之前的版本),可能不支持C++11。考虑升级编译器。 示例代码: 以下是一个使用`std::function`的示例,演示如何正确包含和使用: ```cpp #include <functional> #include <iostream> void hello() { std::cout << "Hello from function!\n"; } int main() { // 使用函数指针 std::function<void()> func1 = hello; func1(); // 使用lambda表达式 std::function<void()> func2 = []() { std::cout << "Hello from lambda!\n"; }; func2(); // 绑定成员函数 struct Greeter { void greet(const char* message) const { std::cout << message << std::endl; } }; Greeter greeter; std::function<void()> func3 = std::bind(&Greeter::greet, &greeter, "Hello from member function!"); func3(); return 0; } ``` 注意事项: - `std::function`是一个通用的函数包装器,可以存储、复制和调用任何可调用目标(函数、lambda表达式、绑定表达式等)。 - 当使用`std::bind`绑定成员函数时,注意第一个参数需要是成员函数的指针,第二个参数是对象指针(或引用),后面是参数列表。 如果问题仍然存在,请提供具体的错误信息和代码片段以便进一步分析。 §§相关问题§§: 1. C++11中的std::function主要用途是什么? 2. 除了std::function,C++中还有哪些方式可以实现回调函数? 3. 如何正确使用std::bind绑定成员函数? 4. 在C++11中,lambda表达式与std::function有什么关系?</think>### 问题:`std` 没有成员 `function` 当编译器提示 "std 没有成员 function" 错误时,通常有以下几种原因和解决方案: #### 1. **缺少头文件包含** **原因**:`std::function` 定义在 `<functional>` 头文件中,未包含该头文件会导致编译器无法识别。 **解决方案**:包含必要的头文件 ```cpp #include <functional> // 必须包含此头文件 ``` #### 2. **编译器未启用 C++11 或更高标准** **原因**:`std::function` 是 C++11 引入的特性,旧编译器默认使用 C++98 标准。 **解决方案**:启用 C++11 或更高标准 - **GCC/Clang**:编译时添加 `-std=c++11` 或更高标志 - **Visual Studio**: - 项目属性 → C/C++ → 语言 → C++ 语言标准 → 选择 "ISO C++11 标准" 或更高 - 或在代码顶部添加: ```cpp #pragma warning(disable: 4067) // 忽略警告 #define _HAS_CXX17 1 // 启用 C++17 特性 ``` #### 3. **命名空间污染** **原因**:使用了 `using namespace std;` 后定义了同名的 `function` 。 **解决方案**:避免命名冲突 ```cpp // 错误示例 using namespace std; class function {}; // 自定义与 std::function 冲突 // 正确做法:显式使用 std:: std::function<void()> myFunc; ``` #### 4. **拼写错误** **原因**:`function` 拼写错误(注意全小写)。 **解决方案**:检查拼写 ```cpp // 错误示例 std::Function<void()> func; // 'F' 应为小写 // 正确写法 std::function<void()> func; // 全小写 ``` #### 5. **编译器不兼容** **原因**:使用古董级编译器(如 VC6)或未完全实现 C++11 的编译器。 **解决方案**: - 升级到现代编译器(VS2015+,GCC 4.8.1+,Clang 3.3+) - 使用 Boost 库替代: ```cpp #include <boost/function.hpp> boost::function<void()> func; ``` ### 完整使用示例 ```cpp #include <iostream> #include <functional> // 必须包含的头文件 void classic_function() { std::cout << "经典函数\n"; } struct Functor { void operator()() { std::cout << "函数对象\n"; } }; int main() { // 1. 包装普通函数 std::function<void()> func1 = classic_function; func1(); // 2. 包装lambda表达式 std::function<void()> func2 = []() { std::cout << "Lambda表达式\n"; }; func2(); // 3. 包装函数对象 std::function<void()> func3 = Functor(); func3(); // 4. 包装成员函数 struct MyClass { void member_func() { std::cout << "成员函数\n"; } }; MyClass obj; std::function<void()> func4 = std::bind(&MyClass::member_func, &obj); func4(); return 0; } ``` ### 关键注意事项 1. **型签名必须精确匹配**: ```cpp // 错误:返回型不匹配 std::function<int()> func = []{ return 3.14; }; // 返回double // 正确 std::function<double()> func = []{ return 3.14; }; ``` 2. **空函数对象检查**: ```cpp std::function<void()> func; if (func) { // 检查是否可调用 func(); } else { std::cout << "函数对象为空!\n"; } ``` 3. **性能考虑**: - `std::function` 有小对象优化(通常存储小于 16 字节的对象) - 频繁调用的热点代码考虑使用函数指针或模板 4. **与 `auto` 的区别**: ```cpp auto lambda = []{ /*...*/ }; // 具体型,无开销 std::function<void()> func = lambda; // 型擦除,有间接调用开销 ``` ### 替代方案(当无法使用 C++11 时) ```cpp // 1. 函数指针 typedef void (*FuncPtr)(); FuncPtr fptr = &classic_function; // 2. 模板函数 template <typename F> void call_function(F func) { func(); } // 3. 抽象基(传统OOP方式) class Callable { public: virtual void operator()() = 0; virtual ~Callable() = default; }; class MyCallback : public Callable { public: void operator()() override { std::cout << "传统回调实现\n"; } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值