C++:绑定到基类

发布于 2024-08-29 06:53:50 字数 1394 浏览 10 评论 0原文

编辑:

在以下代码中container::push采用从base派生的T类型的对象作为参数并将指向方法 bool T::test() 的指针存储在向量中。

container::call 在成员对象 p 的上下文中调用每个存储的方法,其类型为 base,而不是T。只要被调用的方法不引用 base 之外的任何成员,并且 test() 未声明为 virtual,它就可以工作。

我知道这很丑陋,甚至可能不正确。

我怎样才能以更好的方式完成同样的事情?

#include <iostream>
#include <tr1/functional>
#include <vector>

class base {
   public:
   base(int v) : x(v)
   {}
   bool test() const { // this is NOT called
      return false;
   }

   protected:
   int x;
};

class derived : public base {
   public:
   bool test() const { // this is called instead
      return (x == 42);
   }
};

class container {
   public:
   container() : p(42)
   {}
   template<typename T>
   void push(const T&) {
      vec.push_back((bool (base::*)() const) &T::test);
   }
   void call() {
      std::vector<bool (base::*)() const>::iterator i;
      for(i = vec.begin(); i != vec.end(); ++i) {
         if( (p .* (*i))() ) {
            std::cout << "ok\n";
         }
      }
   }

   private:
   std::vector<bool (base::*)() const> vec;
   base p;
};

int main(int argc, char* argv[]) {
   container c;
   c.push(derived());
   c.call();
   return 0;
}

EDIT:

In the following code container::push takes an object of type T that derives from base as argument and stores in a vector a pointer to the method bool T::test().

container::call calls each of the stored methods in the context of to the member object p, which has type base, not T. It works as long as the called method does not refer to any member outside base and if test() is not declared virtual.

I know this is ugly and may not be even correct.

How can I accomplish the same thing in a better way?

#include <iostream>
#include <tr1/functional>
#include <vector>

class base {
   public:
   base(int v) : x(v)
   {}
   bool test() const { // this is NOT called
      return false;
   }

   protected:
   int x;
};

class derived : public base {
   public:
   bool test() const { // this is called instead
      return (x == 42);
   }
};

class container {
   public:
   container() : p(42)
   {}
   template<typename T>
   void push(const T&) {
      vec.push_back((bool (base::*)() const) &T::test);
   }
   void call() {
      std::vector<bool (base::*)() const>::iterator i;
      for(i = vec.begin(); i != vec.end(); ++i) {
         if( (p .* (*i))() ) {
            std::cout << "ok\n";
         }
      }
   }

   private:
   std::vector<bool (base::*)() const> vec;
   base p;
};

int main(int argc, char* argv[]) {
   container c;
   c.push(derived());
   c.call();
   return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

故事与诗 2024-09-05 06:53:50

您对“boost::bind”语句所做的事情是调用衍生::test 并将“b”作为“this”指针传递。重要的是要记住,derivative::test 的“this”指针应该是指向“派生”对象的指针 - 但您的情况并非如此。它适用于您的特定情况,因为您没有 vtable 并且内存布局是相同的 - 但一旦这种情况发生变化,您的程序可能会崩溃。

此外,这完全是错误的——丑陋、不可读、容易出错的代码。你到底想做什么?

[编辑]已编辑问题的新答案:您应该使用 boost::bind 创建一个功能闭包,它包装了对象和对象。单个对象中的成员函数 - 并将该对象存储在您的集合中。那么当你调用它时,它总是可靠的。
如果你不能在你的应用程序中使用 boost...那么,你可以自己做一些类似 boost::bind 的事情(只需看看它在 boost 中是如何完成的),但更有可能的是你会出错并出现错误。

What you are doing with your "boost::bind" statement is to call derived::test and pass "b" as a "this" pointer. It's important to remmember that the "this" pointer for derived::test is supposed to be a pointer to a "derived" object - which is not the case for you. It works in your particular situation since you have no vtable and the memory layout is identical - but as soon as that will change, your program will likely break.

And besides, it's just plain wrong - ugly, unreadable, bug-prone code. What are you really trying to do?

[Edit] New answer to the edited question: You should use boost::bind to create a functional closure, that wraps both the object & the member function in a single object - and store that object in your collection. Then when you invoke it, it is always reliable.
If you can't use boost in your application... well, you could do something like boost::bind yourself (just look on how it is done in boost), but it's more likely that you'll get it wrong and have bugs.

云归处 2024-09-05 06:53:50

对于更新的问题:

在基础对象上调用派生成员函数是未定义的行为。您试图实现的目标(代码)是错误的。尝试发布您需要的内容,人们会帮助您进行合理的设计。

To the updated question:

Calling a derived member function on a base object is Undefined Behavior. What you are trying to achieve (code) is wrong. Try to post what you need and people will help with a sensible design.

日久见人心 2024-09-05 06:53:50

您所做的事情是不正确的,在简单的示例中它会起作用,但在其他情况下可能只会引发地狱(未定义行为的可能性之一)。

由于 base::test衍生::test 不是虚拟的,它们是两个不同的成员方法,因此为了简单起见,我将它们称为 base::foo< /code> 和 衍生::bar。在绑定器代码中,您强制编译器调整在 衍生 中定义的指向 bar 的指针,就好像它实际上是在 base 中定义的一样,并且然后调用它。也就是说,您正在调用对象或类型base 上的派生 方法!!!这是未定义的行为。

它没有消亡的原因是 base衍生 中的 this 指针重合,并且您仅访问 base 中存在的数据>基类。但这是不正确的。

当您将 base::test 声明为 virtual 时,您会得到正确的行为:层次结构中最派生的对象是 base,编译器将使用虚拟调度机制并找出base 是找到并执行 test 的最终重写器的地方。

当您仅将衍生::test声明为虚拟(而不是)时,编译器将尝试在传递的对象中使用不存在的虚拟调度机制(通常是虚函数表指针)这会杀死应用程序。

无论如何,除了虚拟 base::test 之外的所有使用都是不正确的。根据您的实际要求,最可能正确的方法是:

class base {
public:
   virtual bool test() const;
};
class derived : public base {
public:
   virtual bool test() const; // <--- virtual is optional here, but informative
};
int main()
{
   derived d; // <--- the actual final type
   base & b = d;  // <--- optional
   if ( std::tr1::bind( &base::test, std::tr1::ref(b))() ) {
      // ...
   }
}

请注意,没有强制转换(强制转换通常是对一些奇怪的东西的暗示,潜在的危险隐藏在那里),该对象是具体类型您希望在哪里调用该方法,并且虚拟调度机制保证即使
bind 是base::test,由于该方法是虚拟的,因此将执行最终的重写器。

这个另一个例子更有可能做有趣的事情(我还没有尝试过):

struct base {
   void foo() {}
};
struct derived : base {
   void foo() { 
      for ( int i = 0; i < 1000; ++i ) {
         std::cout << data[i];
      }
   }
   int data[1000];
};
int main() {
   base b;
   std::tr1::bind((void (base::*)()) &derived::foo, std::tr1::ref(b))();
}

What you are doing is not correct, and in the simple example it will work, but might just raise hell (one of the possibilities for undefined behavior) in other cases.

Since base::test and derived::test are not virtual, they are two different member methods, so for simplicitly I will call them base::foo and derived::bar. In the binder code you are forcing the compiler into adapting a pointer to bar that is defined in derived as if it was actually defined in base and then calling it. That is, you are calling a method of derived on an object or type base!!! which is undefined behavior.

The reason that it is not dying is that the this pointers in base and derived coincide and that you are only accessing data present in the base class. But it is incorrect.

When you declare base::test virtual, you get the correct behavior: your most derived object in the hierarchy is base, the compiler will use the virtual dispatch mechanism and find out that base is where the final overrider for test is found and executed.

When you declare only derived::test as virtual (and not base) the compiler will try to use an inexistent virtual dispatch mechanism (usually a vtable pointer) in the handed object and that kills the application.

At any rate, all but the virtual base::test uses are incorrect. Depending on what your actual requirements are, the most probably correct way of doing it would be:

class base {
public:
   virtual bool test() const;
};
class derived : public base {
public:
   virtual bool test() const; // <--- virtual is optional here, but informative
};
int main()
{
   derived d; // <--- the actual final type
   base & b = d;  // <--- optional
   if ( std::tr1::bind( &base::test, std::tr1::ref(b))() ) {
      // ...
   }
}

Note that there is no cast (casts are usually a hint into something weird, potentially dangerous is hiding there), that the object is of the concrete type where you want the method to be called, and that the virtual dispatch mechanism guarantees that even if the
bind is to base::test, as the method is virtual, the final overrider will be executed.

This other example will more likely do funny things (I have not tried it):

struct base {
   void foo() {}
};
struct derived : base {
   void foo() { 
      for ( int i = 0; i < 1000; ++i ) {
         std::cout << data[i];
      }
   }
   int data[1000];
};
int main() {
   base b;
   std::tr1::bind((void (base::*)()) &derived::foo, std::tr1::ref(b))();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文