c++:为什么 'this' 不能是 nullptr?
c++: why can't 'this' be a nullptr?
在我使用 C++ 的早期,我似乎记得您可以使用 NULL 指针调用成员函数,并在成员函数中检查它:
class Thing {public: void x();}
void Thing::x()
{ if (this == NULL) return; //nothing to do
...do stuff...
}
Thing* p = NULL; //nullptr these days, of course
p->x(); //no crash
这样做可能看起来很傻,但是在编写递归函数来遍历数据结构时绝对很棒,在这种情况下导航很容易 运行 进入 NULL 的死胡同;导航函数可以在顶部对 NULL 进行一次检查,然后愉快地调用自己以尝试更深入地导航,而不会在代码中乱扔额外的检查。
至少根据 g++,自由(如果存在的话)已被撤销。编译器对此发出警告,如果编译优化,则会导致崩溃。
问题 1:C++ 标准(任何风格)是否不允许 NULL this?还是 g++ 就在我面前?
问题2。更哲学地说,为什么? 'this' 只是另一个指针。指针的荣耀在于它们可以是 nullptr,这是一个有用的条件。
我知道我可以通过创建静态函数来解决这个问题,将一个指针作为第一个参数传递给数据结构(你好 C 的日子),然后检查指针。我只是很惊讶我需要这样做。
编辑:要对答案进行投票,我想查看标准中关于为什么不允许这样做的章节和诗句。请注意,我在 NO POINT 处的示例取消了对 NULL 的引用。这里没有什么是虚拟的,p 被复制到 "argument this" 但在使用前检查。没有尊重发生!所以 NULL 的取消引用不能用作 UB 的声明。
人们对 *p 做出下意识的反应,并假设如果 p 为 NULL 则它无效。但确实如此,证据就在这里:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
事实上,当指针 p 作为 *p 出人意料地有效时,它指出了两种情况:当 p 为 null 或当 p 指向一个元素超过数组末尾时。你绝不能做的是使用 *p... 的值,而不是获取它的地址。 &*p 其中 p == nullptr 对于任何指针类型 p 都是有效的。指出 p->x() 实际上是 (*p).x() 很好,但在一天结束时转换为 x(&*p) 并且它是完全正确的格式和有效的。对于 p=nullptr... 它只是变成 x(nullptr).
我认为我应该与标准社区进行辩论;由于他们急于削弱空引用的概念,所以措辞不明确。因为这里没有人要求 p->x() 是 UB 而不试图要求它是 UB 因为 *p 是 UB;并且因为 *p 绝对不是 UB,因为 x() 的任何方面都没有使用引用值,所以我将把这归结为 g++ 在标准歧义上的过度扩张。使用静态函数和额外参数的完全相同的机制定义明确,所以它不会阻止我的重构工作。考虑撤回问题;可移植代码不能假设 this==nullptr 会工作,但有一个可移植的解决方案可用,所以最后没关系。
处于 this
为 nullptr
的情况意味着您调用了一个非静态成员函数,但未使用有效实例,例如将指针设置为 nullptr
。由于这是被禁止的,要获得空 this
你必须已经处于未定义的行为中。换句话说,除非您有未定义的行为,否则 this
永远不会是 nullptr
。由于未定义行为的性质,您可以将语句简化为“this
永远不会 nullptr
”,因为在存在未定义行为的情况下不需要维护任何规则。
C++ 不允许调用空对象的成员函数。对象需要身份并且不能存储到空指针。如果成员函数读取或写入空指针引用的对象的字段会发生什么?
听起来您可以在代码中使用 null object pattern 来创建想要的结果。
空指针在面向对象语言中被认为是一个有问题的实体,因为在大多数语言中它不是一个对象。这就产生了对专门处理 something 为 null 的情况的代码的需求。虽然检查特殊的空指针是常态。还有其他方法。 Smalltalk 实际上有一个 NullObject,它有自己的方法。作为所有对象,它也可以被扩展。 Go 编程语言确实允许为 nil 的东西调用 struct 成员函数(这听起来像是问题中需要的东西)。
Question 1: does the C++ standard (any flavor) disallow a NULL this?
Or is g++ just getting in my face?
C++ 标准不允许它 -- calling a method on a NULL pointer 正式 'undefined behavior' 并且你必须避免这样做,否则你会被咬。特别是,优化器在进行优化时会假定 this 指针为非 NULL,从而导致运行时出现 strange/unexpected 行为(我从经验中知道这一点 :))
Question 2. More philosophically, why? 'this' is just another pointer.
The glory of pointers is that they can be nullptr, and that's a useful
condition.
我不确定这是否重要,真的;这是 C++ 标准中指定的内容,它们可能有其原因(哲学或其他原因),但由于标准指定了它,编译器期望它,因此作为程序员我们必须遵守它,否则将面临未定义的行为。 (可以想象一个允许 NULL this 指针的替代宇宙,但我们不住在那里)
问题已经得到解答 - 取消引用空指针是未定义的行为,使用 *obj
或 obj->
都是取消引用。
现在(因为我假设您对如何解决这个问题有疑问)解决方案是使用静态函数:
class Foo {
static auto bar_st(Foo* foo) { if (foo) return foo->bar(); }
}
话虽如此,我确实认为 gcc 为 nullptr 删除所有分支的决定并不明智。没有人因此获益,很多人因此受苦。有什么好处?
this
如果您 delete this
也可能为空(这是可能的但不推荐)
在我使用 C++ 的早期,我似乎记得您可以使用 NULL 指针调用成员函数,并在成员函数中检查它:
class Thing {public: void x();}
void Thing::x()
{ if (this == NULL) return; //nothing to do
...do stuff...
}
Thing* p = NULL; //nullptr these days, of course
p->x(); //no crash
这样做可能看起来很傻,但是在编写递归函数来遍历数据结构时绝对很棒,在这种情况下导航很容易 运行 进入 NULL 的死胡同;导航函数可以在顶部对 NULL 进行一次检查,然后愉快地调用自己以尝试更深入地导航,而不会在代码中乱扔额外的检查。
至少根据 g++,自由(如果存在的话)已被撤销。编译器对此发出警告,如果编译优化,则会导致崩溃。
问题 1:C++ 标准(任何风格)是否不允许 NULL this?还是 g++ 就在我面前?
问题2。更哲学地说,为什么? 'this' 只是另一个指针。指针的荣耀在于它们可以是 nullptr,这是一个有用的条件。
我知道我可以通过创建静态函数来解决这个问题,将一个指针作为第一个参数传递给数据结构(你好 C 的日子),然后检查指针。我只是很惊讶我需要这样做。
编辑:要对答案进行投票,我想查看标准中关于为什么不允许这样做的章节和诗句。请注意,我在 NO POINT 处的示例取消了对 NULL 的引用。这里没有什么是虚拟的,p 被复制到 "argument this" 但在使用前检查。没有尊重发生!所以 NULL 的取消引用不能用作 UB 的声明。
人们对 *p 做出下意识的反应,并假设如果 p 为 NULL 则它无效。但确实如此,证据就在这里: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232 事实上,当指针 p 作为 *p 出人意料地有效时,它指出了两种情况:当 p 为 null 或当 p 指向一个元素超过数组末尾时。你绝不能做的是使用 *p... 的值,而不是获取它的地址。 &*p 其中 p == nullptr 对于任何指针类型 p 都是有效的。指出 p->x() 实际上是 (*p).x() 很好,但在一天结束时转换为 x(&*p) 并且它是完全正确的格式和有效的。对于 p=nullptr... 它只是变成 x(nullptr).
我认为我应该与标准社区进行辩论;由于他们急于削弱空引用的概念,所以措辞不明确。因为这里没有人要求 p->x() 是 UB 而不试图要求它是 UB 因为 *p 是 UB;并且因为 *p 绝对不是 UB,因为 x() 的任何方面都没有使用引用值,所以我将把这归结为 g++ 在标准歧义上的过度扩张。使用静态函数和额外参数的完全相同的机制定义明确,所以它不会阻止我的重构工作。考虑撤回问题;可移植代码不能假设 this==nullptr 会工作,但有一个可移植的解决方案可用,所以最后没关系。
处于 this
为 nullptr
的情况意味着您调用了一个非静态成员函数,但未使用有效实例,例如将指针设置为 nullptr
。由于这是被禁止的,要获得空 this
你必须已经处于未定义的行为中。换句话说,除非您有未定义的行为,否则 this
永远不会是 nullptr
。由于未定义行为的性质,您可以将语句简化为“this
永远不会 nullptr
”,因为在存在未定义行为的情况下不需要维护任何规则。
C++ 不允许调用空对象的成员函数。对象需要身份并且不能存储到空指针。如果成员函数读取或写入空指针引用的对象的字段会发生什么?
听起来您可以在代码中使用 null object pattern 来创建想要的结果。
空指针在面向对象语言中被认为是一个有问题的实体,因为在大多数语言中它不是一个对象。这就产生了对专门处理 something 为 null 的情况的代码的需求。虽然检查特殊的空指针是常态。还有其他方法。 Smalltalk 实际上有一个 NullObject,它有自己的方法。作为所有对象,它也可以被扩展。 Go 编程语言确实允许为 nil 的东西调用 struct 成员函数(这听起来像是问题中需要的东西)。
Question 1: does the C++ standard (any flavor) disallow a NULL this? Or is g++ just getting in my face?
C++ 标准不允许它 -- calling a method on a NULL pointer 正式 'undefined behavior' 并且你必须避免这样做,否则你会被咬。特别是,优化器在进行优化时会假定 this 指针为非 NULL,从而导致运行时出现 strange/unexpected 行为(我从经验中知道这一点 :))
Question 2. More philosophically, why? 'this' is just another pointer. The glory of pointers is that they can be nullptr, and that's a useful condition.
我不确定这是否重要,真的;这是 C++ 标准中指定的内容,它们可能有其原因(哲学或其他原因),但由于标准指定了它,编译器期望它,因此作为程序员我们必须遵守它,否则将面临未定义的行为。 (可以想象一个允许 NULL this 指针的替代宇宙,但我们不住在那里)
问题已经得到解答 - 取消引用空指针是未定义的行为,使用 *obj
或 obj->
都是取消引用。
现在(因为我假设您对如何解决这个问题有疑问)解决方案是使用静态函数:
class Foo {
static auto bar_st(Foo* foo) { if (foo) return foo->bar(); }
}
话虽如此,我确实认为 gcc 为 nullptr 删除所有分支的决定并不明智。没有人因此获益,很多人因此受苦。有什么好处?
this
如果您 delete this
也可能为空(这是可能的但不推荐)