C++:绑定到基类
编辑:
在以下代码中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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您对“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.
对于更新的问题:
在基础对象上调用派生成员函数是未定义的行为。您试图实现的目标(代码)是错误的。尝试发布您需要的内容,人们会帮助您进行合理的设计。
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.
您所做的事情是不正确的,在简单的示例中它会起作用,但在其他情况下可能只会引发地狱(未定义行为的可能性之一)。
由于
base::test
和衍生::test
不是虚拟的,它们是两个不同的成员方法,因此为了简单起见,我将它们称为base::foo< /code> 和
衍生::bar
。在绑定器代码中,您强制编译器调整在衍生
中定义的指向bar
的指针,就好像它实际上是在base
中定义的一样,并且然后调用它。也就是说,您正在调用对象或类型base
上的派生
方法!!!这是未定义的行为。它没有消亡的原因是
base
和衍生
中的this
指针重合,并且您仅访问base
中存在的数据>基类。但这是不正确的。当您将
base::test
声明为 virtual 时,您会得到正确的行为:层次结构中最派生的对象是base
,编译器将使用虚拟调度机制并找出base
是找到并执行test
的最终重写器的地方。当您仅将
衍生::test
声明为虚拟(而不是基
)时,编译器将尝试在传递的对象中使用不存在的虚拟调度机制(通常是虚函数表指针)这会杀死应用程序。无论如何,除了虚拟
base::test
之外的所有使用都是不正确的。根据您的实际要求,最可能正确的方法是:请注意,没有强制转换(强制转换通常是对一些奇怪的东西的暗示,潜在的危险隐藏在那里),该对象是具体类型您希望在哪里调用该方法,并且虚拟调度机制保证即使
bind 是
base::test
,由于该方法是虚拟的,因此将执行最终的重写器。这个另一个例子更有可能做有趣的事情(我还没有尝试过):
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
andderived::test
are not virtual, they are two different member methods, so for simplicitly I will call thembase::foo
andderived::bar
. In the binder code you are forcing the compiler into adapting a pointer tobar
that is defined inderived
as if it was actually defined inbase
and then calling it. That is, you are calling a method ofderived
on an object or typebase
!!! which is undefined behavior.The reason that it is not dying is that the
this
pointers inbase
andderived
coincide and that you are only accessing data present in thebase
class. But it is incorrect.When you declare
base::test
virtual, you get the correct behavior: your most derived object in the hierarchy isbase
, the compiler will use the virtual dispatch mechanism and find out thatbase
is where the final overrider fortest
is found and executed.When you declare only
derived::test
as virtual (and notbase
) 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: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):