C++类编译代码的控制流顺序是什么?

3

我正在编译一个类,下面是完整的程序:

#include<iostream>
using namespace std;

class Test{
    public:
        Test()
        {
            cout<<"Test variable created...\n";
            // accessing width variable in constructor
            cout<<"Width is "<<width<<".\n";
        }
        void setHeight(int h)
        {
            height = h;
        }
        void printHeight()
        {
            cout<<"Height is "<<height<<" meters.\n";
        }
        int width = 6;
    protected:
        int height;
};

int main()
{
    Test t = Test();
    t.setHeight(3);
    t.printHeight();
    return 0;
}

代码运行得非常好,但是构造函数如何能够访问变量widthpublic块结束之前没有被声明。此外,成员函数如何能够访问稍后在公共块中声明的变量?C++不是按照顺序执行语句的吗?

2
声明不是语句。在尝试编译类的代码之前,会收集和存储其声明。此外,如果程序的可观察行为在“as-if”规则下相同,则语句不必按顺序执行。 - underscore_d
“C++不是按顺序执行语句吗?”实际上不是这样。语句可以按不同的顺序执行,例如成员初始化列表。语句按类中成员列出的顺序执行,而不是按实际语句的顺序执行。 - Thomas Sablik
声明为 int height{};,这样用户在没有调用 setHeight() 的情况下尝试 printHeight() 就不会引起未定义的行为。 - underscore_d
2
成员变量在构造函数被调用之前被初始化(例如 int width = 6;)。这意味着在第一个非静态方法被调用之前,必须知道类的结构。 - Thomas Sablik
在这里,你可以看到实际构造函数开始工作之前发生了什么。虽然有更好的文档资料可供参考,但我无法找到它们。 - Thomas Sablik
使用类作用域的类型别名时,何时可以在方法中的用法先于类型别名声明? - underscore_d
2个回答

4

将类中的内联定义视为声明函数的语法糖,然后在类外定义。手动执行此操作将转换代码为:

class Test{
    public:
        Test();
        void setHeight(int h);
        void printHeight();
        int width = 6;
    protected:
        int height;
};

Test::Test()
{
    cout<<"Test variable created...\n";
    // accessing width variable in constructor
    cout<<"Width is "<<width<<".\n";
}

void Test::setHeight(int h)
{
    height = h;
}

void Test::printHeight()
{
    cout<<"Height is "<<height<<" meters.\n";
}

从这个转换中可以看出,类成员现在在函数定义之前,所以它们可以使用变量而不会出错。

这个技术术语被称为完全类上下文,其要点是当您在成员函数体或类成员初始化列表中时,类被视为完整的,并且可以使用在类中定义的任何东西,无论它在类中声明的位置如何。


在构造函数的初始化列表中也是这样吗? - Vincent Fourmond
1
@VincentFourmond 是的。成员初始化列表被视为函数体的一部分。 - NathanOliver
谢谢@NathanOlivier,我猜这会是你回答中有价值的精度? - Vincent Fourmond
@VincentFourmond 已添加。 - NathanOliver

0

这是因为成员函数的主体是类的完整类上下文,如下面引用的语句所述:

来自class.mem.general#6

6. 类的完整类上下文是:

  • 函数主体([dcl.fct.def.general]),

  • 默认参数,

  • noexcept-specifier ([except.spec]),或

  • 默认成员初始化器

在类的成员说明中。

以上意味着函数主体是类的完整类上下文,这意味着即使在编写类定义时那些数据成员出现得更晚,在构造函数内使用width以及在成员函数setHeightprintHeight内使用height也是允许的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接