我想知道将
assert( this != nullptr );
放在每个成员函数上是否是一个好主意。我相信编译器可以决定完全忽略这个断言,因为假设this
不能为空,因此断言始终为真并且可以在编译时解析。
但是如果编译器没有做出这个假设,那么这个断言对于及早发现问题非常有用。
编译器会假设这一点吗?
不,编译器通常不会这么假设。甚至还有商业代码与这些检查相关,有些不仅仅是断言,而且实际上是其中的逻辑。
if (!this) { doSomeWork(); }
。
尽管您无法达到
this
为 NULL
的情况而不遇到未定义的行为,但如果您非常了解实现细节,那么您可以进行检查;是的,你是对的,它可以帮助调试。
不过我不会把它到处放。任何地方,就此而言。如果
this
确实是 NULL
,稍后当您访问某个成员时可能会发生崩溃。如果您不访问任何成员,请考虑标记该方法 static
。它还会不必要地使代码变得臃肿。
编译器通常仅在打开优化时做出任何假设。因此,如果您在没有优化的情况下进行编译(只要您不定义
assert
,即启用 NDEBUG
,这与优化无关),则断言将起作用。
它将捕获普通方法的问题,但请记住,对于虚拟方法,
this
指针被取消引用,以便调用该方法,因此在调用此检查之前已经发生了崩溃。对于大多数非虚拟方法,如果该方法访问任何成员,问题就不会被忽视(如果没有,那么它首先就不应该是实例成员函数),因此添加断言是否有意义是个问题.
除了已经说过的内容之外,测试
this
是否为 nullptr
是没有意义的。即使指针确实是nullptr
,this
也不会始终为零。这是一个例子:
#include <iostream>
struct A
{
void foo() { std::cout << this << std::endl; }
int a;
};
struct B
{
void boo() { std::cout << this << std::endl; }
int b;
};
struct C: public A, public B
{
};
int main()
{
C *c = 0;
c->foo(); // this == 0
c->boo(); // this == 4
return 0;
}
不,只需要在非虚拟非静态成员函数上断言它。
在静态成员上,
this
是被禁止的,所以没关系。
在非虚拟非静态成员上,由于调用是由编译器静态确定的,因此像
ptr->f()
这样的调用可以转换为类似 f(ptr)
的东西,所以你可以有一个 null this
!奇怪但有可能。所以你可以断言它。
在虚拟成员上,由于调用是动态计算的,因此这种情况永远不会发生。为了找到这个函数,需要解引用指针(通过指针查询虚函数表),所以在你有机会进行有效调用之前机器会崩溃(解引用空指针总是崩溃) .
对此进行实验:
#include <iostream>
using namespace std;
struct foo {
int bar;
void baz() { bar = 1; }
void barf() { int i = 5; cout << "barf" << this << endl;}
virtual void barf2() { int i = 5; cout << "barf2" <<this << endl;}
};
int main() {
struct foo * crash = 0;
crash->barf(); // do not crash
crash->barf2(); // crash
//crash->baz(); // crash
}