"this"的值何时偏移?
When is the value of "this" shifted by an offset?
我想知道assert( this != nullptr );
在成员函数中是否是一个好主意,有人指出,如果this
的值被添加偏移量,它将无法工作。在这种情况下,它不是 0,而是 40,使断言无用。
不过,这是什么时候发生的?
多重继承可能会导致偏移,跳过对象中额外的逆向表指针。 通用名称是"此指针调节器抛骸"。
但是你帮的忙太大了。 空引用是非常常见的错误,操作系统已经为您内置了一个断言。 程序将因段错误或访问冲突而停止。 从调试器获得的诊断总是足够好,可以告诉你对象指针为 null,你将看到一个非常低的地址。 不仅为空,它也适用于 MI 案例。
this
调整只能在使用多重继承的类中进行。下面是一个说明这一点的程序:
#include <iostream>
using namespace std;
struct A {
int n;
void af() { cout << "this=" << this << endl; }
};
struct B {
int m;
void bf() { cout << "this=" << this << endl; }
};
struct C : A,B {
};
int main(int argc, char** argv) {
C* c = NULL;
c->af();
c->bf();
return 0;
}
当我运行这个程序时,我得到这个输出:
this=0
this=0x4
也就是说:你的assert this != nullptr
不会捕获nullptr
c 的 c->bf()
的调用,因为C
对象内B
子对象的this
移动了四个字节(由于A
子对象(。
让我们尝试说明C
对象的布局:
0: | n |
4: | m |
左侧的数字是对象开头的偏移量。因此,在偏移量0
我们有A
子对象(及其数据成员 n
(。在偏移量4
我们有B
子对象(及其数据成员m
(。
整个对象的this
以及A
子对象的this
都指向偏移量 0。但是,当我们想要引用B
子对象时(当调用由B
定义的方法时(,需要调整this
值,使其指向B
子对象的开头。因此+4。
无论如何,这是UB。
多重继承可能会引入偏移量,具体取决于实现:
#include <iostream>
struct wup
{
int i;
void foo()
{
std::cout << (void*)this << std::endl;
}
};
struct dup
{
int j;
void bar()
{
std::cout << (void*)this << std::endl;
}
};
struct s : wup, dup
{
void foobar()
{
foo();
bar();
}
};
int main()
{
s* p = nullptr;
p->foobar();
}
某些版本的 clang++ 上的输出:
0 0x4
活生生的例子。
另请注意,正如我在对 OP 的评论中指出的那样,此assert
可能不适用于虚拟函数调用,因为 vtable 未初始化(如果编译器执行动态调度,即如果它知道*p
的动态类型,则不会进行优化(。
以下是可能会发生的情况:
struct A {
void f()
{
// this assert will probably not fail
assert(this!=nullptr);
}
};
struct B {
A a1;
A a2;
};
static void g(B *bp)
{
bp->a2.f(); // undefined behavior at this point, but many compilers will
// treat bp as a pointer to address zero and add sizeof(A) to
// the address and pass it as the this pointer to A::f().
}
int main(int,char**)
{
g(nullptr); // oops passed null!
}
对于一般而言,这是C++未定义的行为,但对于某些编译器,它可能具有this
指针在A::f()
内具有一些小的非零地址的一致行为。
编译器通常通过将基对象按顺序存储在内存中来实现多重继承。如果您有,例如:
struct bar {
int x;
int something();
};
struct baz {
int y;
int some_other_thing();
};
struct foo : public bar, public baz {};
编译器将在同一地址分配foo
和bar
,baz
将被偏移sizeof(bar)
。因此,在某些实现下,nullptr -> some_other_thing()
可能会导致非空this
。
Coliru 的这个示例演示了(假设您从未定义的行为中获得的结果与我所做的结果相同(情况,并显示了无法检测到情况的assert(this != nullptr)
。(感谢我基本上从谁那里偷了示例代码@DyP(。
我认为放置断言并不是一个坏主意,例如至少它可以捕获,请参见以下示例
class Test{
public:
void DoSomething() {
std::cout << "Hello";
}
};
int main(int argc , char argv[]) {
Test* nullptr = 0;
nullptr->DoSomething();
}
上面的示例将运行而不会出错,如果更复杂的断言不存在,则很难调试。
我试图指出一个观点,即null这个指针可以被忽视,并且在复杂的情况下变得难以调试,我遇到了这种情况。
- 表示"accepting anything for this template argument" C++概念的通配符
- 为什么使用 "this" 指针调用派生成员函数?
- 何时在引用或唯一指针上使用移动语义
- 何时提供默认参数作为模板参数
- C++-明确何时以及如何调用析构函数
- C++错误:"error: int aaa::bbb is protected within this context"
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 在以唯一ptr为值的C++映射中,动态内存何时会被销毁
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 关于C++中具有多重继承"this"指针的说明
- 在noexcept 规范中是否允许使用"this"?
- 何时应通过引用传递矢量参数而不是按值传递矢量参数?
- 如何修复"error: ‘_1’ was not declared in this scope"?
- 在命名成员函数重载解析期间,"this"何时不在范围内?
- 何时使用'this'访问自己的成员
- 何时使用*this
- 使用构造函数时何时必须使用"this C++?
- 何时在成员函数中使用"this"指针
- "this"的值何时偏移?
- C++:何时应该通过引用使用 PLUS 参数"this->"