22

可能重复:
类实例上的 C++ 静态成员方法调用

今天我发现我长久以来(我的意思是长久以来——比如,二十年),在 C++ 中认为非法的东西实际上是合法的。即,调用静态成员函数,就好像它属于单个对象一样。例如:

struct Foo
{
    static void bar() { cout << "Whatever."; }
};

void caller()
{
    Foo foo;
    foo.bar();    // Legal -- what?
}

我通常会看到使用“范围解析语法”严格调用静态成员函数,因此:

Foo::bar();

这是有道理的,因为静态成员函数不与类的任何特定实例相关联,因此我们不希望特定实例在语法上“附加”到函数调用。

然而我今天发现 GCC 4.2、GCC 4.7.1 和 Clang 3.1(作为编译器的随机抽样)接受前一种语法,以及:

Foo* foo = new Foo;
foo->bar();

在我的特殊情况下,这个表达式的合法性导致了一个运行时错误,这让我相信这种语法的特殊性不仅仅是学术上的兴趣——它还有实际的后果。

为什么 C++ 允许调用静态成员函数,就好像它们是单个对象的直接成员一样——也就是说,通过使用 . 或 -> 附加到对象实例的语法?

4

5 回答 5

20

在第 288 页的 C++ 的设计和演变中,Bjarne Stroustrup 提到在静态成员函数出现之前的日子里,程序员使用诸如((X*)0)->f()调用不需要对象的成员函数之类的技巧。我的猜测是,当静态成员函数被添加到语言中时,允许通过访问,->这样具有类似代码的程序员就可以更改f为,static而无需寻找和更改它的每次使用。

于 2012-08-15T21:14:36.740 回答
10

大概是这样,您可以在您可能不知道某些东西的类类型但编译器知道的地方调用它。

假设我有一堆类,每个类都有一个返回类名的静态成员:

class Foo
{
    static const char* ClassName() { return "Foo"; }
};

class Bar
{
    static const char* ClassName() { return "Bar"; }
};

然后在我的代码中,我可以执行以下操作:

Foo foo;

printf( "This is a %s\n", foo.ClassName() );    

不必一直担心知道我的对象的类别。例如,在编写模板时,这将非常方便。

于 2012-08-15T21:03:26.490 回答
7

之所以这样,是因为标准说这就是它的工作方式。n3290 § 9.4 规定:

类 X 的静态成员 s 可以使用限定 ID 表达式来引用,X::s;而不必使用类成员访问语法 (5.2.5) 来引用静态成员。可以使用类成员访问语法来引用静态成员,在这种情况下,对象表达式会被求值。[ 例子:

struct process { 
  static void reschedule(); 
}; 

process& g();

void f() { 
  process::reschedule(); // OK: no object necessary
  g().reschedule(); // g() is called 
} 

结束示例]

于 2012-08-15T21:01:50.630 回答
7

摘自The Evolution of C++ (pdf),第 8 节。静态成员函数

...还观察到不可移植的代码,例如

    ((x*)0)->f();

用于模拟静态成员函数。

所以我的猜测是(基于几乎所有其他奇怪的语法事物的基本原理模式)当你刚刚拥有提供与已建立但已损坏的习语的向后兼容性的类型时,它们允许调用静态成员函数。

于 2012-08-15T21:17:03.913 回答
1

如果您不订阅“因为标准这么说”的因果关系学派,我还建议静态方法已经足够老了,以至于人们真正担心将this参数传递给函数调用的额外开销时,因此,将纯函数“静态”作为优化可能在 1985 年风靡一时。

于 2012-08-15T21:05:01.547 回答