在 C++ 中应该使用什么来检查身份?
我有两个指向对象的指针,我想以最可靠的方式测试它们是否是完全相同的对象。我明确不想调用任何 operator ==
重载,并且无论使用什么基类、虚拟基类和多重继承,我都希望它能够工作。
我当前的代码是这样的:
((void*)a) == ((void*)b)
对于我的情况,这是有效的。但是,这不适用于这种情况:
class B1 {};
class B2 {};
class C : public B1, public B2 {}
C c;
B1 *a = &c;
B2 *b = &c;
在 reinterpert_cast
、static_cast
或 dynamic_cast
中进行替补也不起作用。
我特别希望事情最终变得非常简单和高效。理想情况下,它不需要任何分支指令来实现,并且会执行类似的操作,将指针调整到对象的开头并进行比较。
I have two pointers to objects and I want to test if they are the exact same object in the most robust manner. I explicitly do not want to invoke any operator ==
overloads and I want it to work no matter what base classes, virtual base classes and multiple inheritance is used.
My current code is this:
((void*)a) == ((void*)b)
And for my case this works. However, that doesn’t work for this case:
class B1 {};
class B2 {};
class C : public B1, public B2 {}
C c;
B1 *a = &c;
B2 *b = &c;
Subbing in reinterpert_cast
, static_cast
or dynamic_cast
doesn't work either.
Particularly I'm hoping for something that ends up really simple and efficient. Ideally it wouldn't require any branch instructions to implement and would do something like, adjust the pointer to the start of the object and compare.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
如果您需要比较对象的身份,为什么不给他们一个呢?毕竟,您决定了对象的身份。让编译器来做,你就会受到编译器的限制。
有什么东西挡住了...
If you need to compare the identity of your objects, why wouldn't you give them one? After all, it's you who decides what makes the identity of the object. Let the compiler do it, and you're bound to the compiler's limitations.
Something in the way of...
即使在您引用的案例中,您的方法也对我有用:
在这里打印“等于”..
Your approach worked for me even in your cited case:
prints "equal" here..
您可以检查对象是否指向内存中的重叠(从 Mark Ransom 的答案中窃取)。这会调用未定义的行为,但应该在任何合理的编译器上执行您想要的操作:
You could check to point if the objects pointed to overlap in memory (stealing from Mark Ransom's answer). This invokes undefined behavior, but should do what you want on any reasonable compiler:
如果您的类确实与给定的完全一样,那么这是不可能的,因为运行时没有足够的信息来重建所需的信息。
如果它们实际上是具有虚函数的多态类,那么听起来
dynamic_cast
就是答案。它返回一个指向最派生对象的指针。您的检查将是dynamic_cast(a)==dynamic_cast(b)
。请参阅此处第 7 段:
http: //www.csci.csusb.edu/dick/c++std/cd2/expr.html#expr.dynamic.cast
我怀疑通常的
dynamic_cast
问题适用 - 即,不能保证它会很快,并且你的类必须是多态的。恐怕这不是我自己使用过的功能——但我经常看到拥有此功能的人建议它,我推断它得到了广泛的支持并且按照广告宣传的那样工作。
If your classes are genuinely exactly as given then it's impossible as there's not enough information available at runtime to reconstruct the required information.
If they're actually polymorphic classes, with virtual functions, it sounds like
dynamic_cast<void *>
is the answer. It returns a pointer to the most derived object. Your check would then bedynamic_cast<void *>(a)==dynamic_cast<void *>(b)
.See paragraph 7 here:
http://www.csci.csusb.edu/dick/c++std/cd2/expr.html#expr.dynamic.cast
I suspect the usual
dynamic_cast
issues apply -- i.e., no guarantee it will be quick, and your classes will have to be polymorphic.This is not a feature I have used myself, I'm afraid -- but I have seen it suggested often enough by people who have that I infer it is widely-supported and works as advertised.
有一个简单的方法和一个困难的方法。
最简单的方法是引入一个空的虚拟基类。从此类继承的每个对象都会获得一个指向“真实”对象中公共点的指针,这就是您想要的。指针有一点开销,但没有分支或任何东西。
困难的方法是尝试使用模板。这里已经有一些技巧了……当使用模板进行黑客攻击时,请记住,您本质上是在重新发明语言的一部分,只是希望通过管理编译时信息来降低开销。我们可以降低虚拟基类的开销并消除该指针吗?这取决于您需要多少通用性。如果您的基类可以在派生对象中以多种不同的方式排列,那么肯定会有一些信息在编译时无法获得。
但是,如果您的继承层次结构是一个颠倒的树(即,您正在通过大量多重继承构建大型对象),或者几个这样的树,您可以继续将指针强制转换为最派生的类型,如下所示:
当然,您不想将 NULL 指针传递给这个“优化”版本。
There's an easy way and a hard way.
The easy way is to introduce an empty virtual base class. Every object inheriting from such a class gets a pointer to the common point in the "real" object, which is what you want. The pointer has a little overhead but there are no branches or anything.
The hard way is to try to use templates. There are a few hacks here already… when hacking with templates remember that you are essentially reinventing part of the language, just with hopefully lower overhead by managing compile time information. Can we lower the overhead of the virtual base class and eliminate that pointer? It depends on how much generality you need. If your base classes can be arranged in several different ways within a derived object, then there is certainly information that you can't get at compile time.
But if your inheritance hierarchy is an upside-down tree (ie, you are building large objects by lots of multiple inheritance), or several such trees, you can just go ahead and cast the pointers to the most derived type like this:
Of course, you don't want to pass NULL pointers to this "optimized" version.
没有通用的方法可以做到这一点。一般来说,基类子对象不知道它们是这样的,因此,如果您只有一个指向基类子对象的指针,则您没有任何方法来获取指向它所属的最派生对象的指针,如果您事先不知道后者的类型。
让我们从这里开始:
考虑
D
内存布局的典型实现。在没有 vtable 的情况下,我们唯一拥有的是原始数据(可能还有填充):您有两个指针:
每个指针有效地指向
D< 实例中的单个
char
/代码>。然而,如果你事先不知道,你怎么能知道呢?还有一种可能性是指针可能指向Z
实例中的子对象,并且查看指针值本身,显然无法判断;您(或编译器)也无法从指针引用的数据中推断出任何内容,因为它只是结构中的单个字节数据。There's no general way to do it. Base class subobjects, in general, have no knowledge that they are that, so if you only have a pointer to a base class subobject, you do not have any means to obtain a pointer to the most derived object to which it belongs, if you do not know the type of the latter in advance.
Let's start with this:
Consider a typical implementation of in-memory layout of
D
. In the absence of vtable, the only thing we have is the raw data (and possibly padding):You have two pointers:
Each pointer effectively points at a single
char
within an instance ofD
. However, if you do not know that in advance, how could you tell? There's also a possibility that pointers could rather point to subobjects within an instance ofZ
, and looking at pointer values themselves, there's clearly no way to tell; nor is there anything you (or the compiler) can deduce from data referenced by the pointer, since it's just a single byte of data in the struct.除了智能指针(实际上不是指针,而是类对象)之外,指针不可能重载
operator==
,因此不需要进行强制转换。当然,比较不同类型的指针可能行不通。您认为为什么需要这样做?
Except for smart pointers (which aren't really pointers, but class objects), overloading
operator==
isn't possible for pointers, so a cast shouldn't be necessary.Of course, comparing pointers of different types might not work. Why do you think you need to do that?
因此,您正在寻找编译时解决方案。我不相信这是可能的,如 C++ 中所述。这是一个思想实验:
File Bases.hpp:
File Derived.hpp:
File Composite.hpp:
File RandomReturn.cpp:
现在,假设您有:
我们如何在编译时在这里实现
is_same_object
,不了解C 类
和D 类
?另一方面,如果你愿意改变假设,它应该是可行的:
So, you're looking for a compile time solution. I don't believe this is possible as stated in C++. Here's a thought experiment:
File Bases.hpp:
File Derived.hpp:
File Composite.hpp:
File RandomReturn.cpp:
Now, suppose you have:
How could we possibly implement
is_same_object
here, at compile time, without knowing anything aboutclass C
andclass D
?On the other hand, if you're willing to change the hypotheses, it should be workable:
使用
boost::addressof
。我想这是最好的方法。无论操作符重载的潜在用途和误用如何,都提供了 boost::addressof 来获取地址。通过使用一些巧妙的内部机制,模板函数addressof
确保它到达实际对象及其地址。看看这个Use
boost::addressof
. I'm thinking this is the best way.boost::addressof
is provided to get the address anyway, regardless of potential uses and misuses of operator overloading. By using some clever internal machinery, the template functionaddressof
ensures that it gets to the actual object and its address. Look at this