class Foo {
public:
static const int kType = 42;
};
void Func() {
Foo *bar = NULL;
int x = bar->kType;
putc(x, stderr);
}
这是定义的行为吗?我阅读了C ++标准但是找不到任何关于访问静态const值的内容......我已经检查了GCC 4.2,Clang ++和Visual Studio 2010生成的程序集,并且它们都没有执行NULL的解引用指针,但我想确定。
答案 0 :(得分:10)
您可以使用指针(或其他表达式)来访问静态成员;但是,遗憾的是,通过NULL指针这样做是官方未定义的行为。从9.4 / 2“静态成员”:
X类的静态成员可能是 使用qualified-id引用 表达式X :: s;没有必要 使用类成员访问语法 (5.2.5)指静态成员。一个 静态成员可以参考使用 类成员访问语法, in 对象表达式是哪种情况 评价。强>
基于以下示例:
class process {
public:
static void reschedule();
};
process& g();
void f()
{
process::reschedule(); // OK: no object necessary
g().reschedule(); // g() is called
}
目的是让您确保在这种情况下调用函数。
答案 1 :(得分:3)
我认为在调用
时根本不使用该类型的实际值bar->kType
因为kType
是静态的,而bar的类型为Foo
,所以它与调用
Foo::kType
为了清楚起见,你应该真正做到这一点。
出于这个原因,调用bar->kType
会在大多数平台上发出编译器警告。
答案 2 :(得分:1)
除了通过NULL指针访问的问题之外,代码中还有另一个微妙的问题
$ 9.4.2 / 2 - “静态数据成员在其类定义中的声明不是定义,并且可能是除了cv-qualified void之外的不完整类型。静态数据成员的定义应出现在命名空间范围包含成员的类定义。“
$ 9.4.2 / 4-“如果静态数据成员是const整数或const枚举类型,它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式(5.19)。在这种情况下,成员可以出现在整数常量表达式中。如果在程序中使用该成员,并且命名空间范围定义不包含初始化程序,则该成员仍应在命名空间作用域中定义。“
class Foo {
public:
static const int kType = 42;
};
int const Foo::kType;
void Func() {
Foo *bar = NULL;
int x = bar->kType;
putc(x, stderr);
}
所以,OP代码中UB的另一个原因。
答案 3 :(得分:1)
即使它运作起来也很糟糕。
在严肃的编程中,您不仅要为自己编码,还要为维护代码的其他人编写代码。 必须避免使用这样的技巧,因为你尊重你的同事。
此代码的一个结果:指针是否为NULL
甚至没有问题,但它暗示该成员kType
可能不是该类的普通非静态成员。有时类很大(这也是邪恶的),并且不能总是重新检查每个变量的定义。
严谨。并以这种方式调用所有静态成员:
Foo::kType
另一种可能性是遵循一个编码约定,让我们知道该成员是静态的,例如,所有类静态成员的s_
前缀:
Foo::s_kType
答案 4 :(得分:-1)
有一个更高的规则可以说基本上说 - 甚至不考虑编译可证明不使用的东西。高级模板编程在很大程度上依赖于此,所以即使编译器清楚地看到构造的结果未被使用时它可能有点灰色 - 它也只是消除它。特别是当它在这种情况下可证明是安全的时候。
如果你愿意,你可能想尝试一些变体 - 比如使指针成为函数的参数,函数的结果,保持指针未初始化(触发编译器投诉的最佳机会),执行0的直接转换(最好没有机会的机会)。