c++如何判断二进制相同的对象?

287 天前
 LuckyPocketWatch

假如有两个自定义类 class A 和 class B ,这两个自定义类的对象长度均为 32 字节,然后有如下代码

A* ptr = new A(/*paratemerA*/);  //先生成一个 class A 的对象为 objectA
A* objectA = ptr;

if(/*condition == true*/){
    ptr->~A();  //销毁对象 class A ,但内存没有还给系统
    ptr = new B(/*paratemerB*/);//在原来的内存上生成一个 class B 的对象为 objectB
}

if(ptr == objectA)
    ptr->doSomething();

objectA 和 objectB 这两个类对象,长度均为 32 字节,这两个对象创建在相同的内存里(创建时间不一样),然后这段代码在运行时出现了一个巧合,生成的这两个对象二进制完全一样

那这种情况下 C++时如何判断 ptr == objectA 这段代码的?

2774 次点击
所在节点    C++
36 条回复
mingl0280
287 天前
@dangyuluo 他原来给的这个代码,除非 ptr 是 void*而且赋值时用了强转,否则怎么想都无法通过编译吧?
hxysnail
287 天前
如果你内存没有还给系统,那么 B 不可能用 A 原来的内存;
如果你内存已经还给系统了,objectA 还指向被回收内存,那就是野指针问题,严重 BUG……
sloknyyz
287 天前
建议再好好学学指针相关的内容,你问的是很基础的东西了。判断 ptr==objectA,很简单,指针判断是否一样就是判断指针值是否一样,根本不会管指针指向了什么内容。你的代码里,按照你说的,两个对象内存相同,那指针的值就是一样的,因此最后的判断是 true 。还有个问题,就是 ptr = new B(), 这段代码是编译不过的,但如果想可以加个强制转换。
antonius
287 天前
Q:如何判断 ptr == objectA ?
A:就是指针指向地址的比较。
ptr = new B 不一定不能通过编译,如果 B 是 A 的子类,是可以的。
不过 objectA 是一个野指针,不算是一个好的编码习惯。
araraloren
287 天前
我们 c++的程序总能搞出不一样的花样,反观 rust 就不太行
jujusama
287 天前
还得是你 cpp 啊,写出这种东西。

正经回答:指针判等仅比较指针变量的值,参考最小示例

https://godbolt.org/z/axPfGezab
aneostart173
287 天前
你这是重写了 allocator?
geelaw
287 天前
@dangyuluo #13 贴代码无意义,不过您说得对,我需要修正 #10 #11 的说法,因为

根据 [basic.compound]/3 ,指向对象的指针的值“表示的地址”是该对象(若该对象不在其生命周期内,则是该对象曾经、将要)占有的存储的第一个字节。这表示 placement new 返回和传入的指针虽然可能指向了不同的对象,但是它们“表示的地址”相同。

根据 [expr.eq]/3.2 ,两个同类型、指向对象的、不是指向末尾之后的指针相等,如果它们“表示的地址”相等。

#10 里面建议的 std::launder 不必要,并且 #11 里面认为的未定义行为不成立。

---

回到 @dangyuluo #12 我认为应该尽量按照楼主希望的意思自动更正他的代码,所以应该认为

struct A { }; struct B : A { };

并且 new B() 改写为 new (ptr) B()。

---

@sloknyyz #23 @antonius #24 @jujusama #26 指针比较并不是比较数值,考虑

alignas(C) std::byte storage[2 * sizeof(C)];
C *a = new (storage) C() + 1;
C *b = new (storage + sizeof(C)) C();
/* 此时 a 和 b 必然表示相同的地址 */
a == b ? 1 : 2; /* 结果可以是 2 见 [basic.compound]/3 的 note 和 [expr.eq]/3.1 */

再比如

void *p = std::malloc(1);
std::free(p);
p == p ? 1 : 2; /* 结果可以是 2 见 [basic.stc]/4 */
geelaw
287 天前
@geelaw #28 贴代码无意义是指贴编译之后的机器代码无意义,因为未定义行为包括任何行为,不能根据实现推断标准。
yeziqing
287 天前
@antonius 看他给的代码只调用了 A 的析构函数,所以这时所占用的内存没被释放,也即 ojbectA 指向的仍是有效内存地址,应该还不算是里指针。
geelaw
286 天前
@geelaw 最后一段里面第一个例子是错误的,因为数组是 implicit-lifetime object ,于是在 a == b 执行的时候它的效果已经变成了 [expr.eq]/3.2 了。不过第二个例子 p == p 依然成立。
cnbatch
286 天前
既然 OP 没说实际业务情况,那我接下来的思路就怎么方便怎么来

首先,既然两个 class 确定都是 32 字节,那么可以当成 int32_t

A *ptr_a = &class_a_var;
B *ptr_b = &class_b_var;
cnbatch
286 天前
(没写完就发了出去,重来)

既然 OP 没说实际业务情况,那我接下来的思路就怎么方便怎么来

首先,既然两个 class 确定都是 32 字节,那么可以当成 int32_t

A *ptr_a = &class_a_var; // 或者是 new 出来的
B *ptr_b = &class_b_var; // 或者是 new 出来的

int32_t value_a = *((int32_t *)ptr_a);
int32_t value_b = *((int32_t *)ptr_b);

std::map<int32_t, std::time_t> stores;

然后把 32 位字节当成 int32_t 存起来:

if (stores.find(value_a) == stores.end())
stores[value_a] = std::time(nullptr);

if (stores.find(value_b) == stores.end())
stores[value_b] = std::time(nullptr);

需要对比时就:

if (stores.find(value_a) != stores.end())
//时间已经存起来了,自己想怎么用就怎么用;

if (stores.find(value_b) != stores.end())
//时间已经存起来了,自己想怎么用就怎么用;
antonius
286 天前
@yeziqing 感谢指正,你说的没错,不算野指针。
tool2d
286 天前
以前有个手动内存管理机制的 flag ,叫 Tomb 墓碑。能确保 ptr 销毁后,短期内地址不会被重复利用。
kirory
286 天前
RTTI, typeid

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/965933

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX