【问题标题】:C++ - Scope of the object created at the end of class definitionC++ - 在类定义结束时创建的对象的范围
【发布时间】:2016-11-24 11:31:58
【问题描述】:

如果我有类似的课程

class sample{
  // ...
} obj;

上面创建的对象obj的范围是什么?对象什么时候销毁?

【问题讨论】:

  • 那么会在堆中创建对象吗?
  • 它将具有声明的任何范围,如果是全局范围,将静态创建,如果是方法范围,将创建方法本地。它永远不会在堆上创建。超出范围时将被销毁。
  • @iammilind 可能是重复的,但不是那个问题。
  • @EJP,是的,你是对的。在this question 中,不幸的是它没有讨论class 对象。我能找到的另一个是c++ global objectGlobal variables and scope - C++。我正在重新打开Q,看看你能不能关闭它。
  • @iammilind 毫无疑问,全球范围是可以接受的重复。问题是关于什么范围,这取决于上下文。

标签: c++ class object scope


【解决方案1】:

obj 的范围与类定义的范围相同。如果你在所有函数之外定义它,它的作用域将是全局的,它将在静态初始化时创建和初始化,并作为程序终止的一部分被销毁。

但是,也可以在函数中定义这样的对象。那样的话,它会在函数进入时创建,在函数返回时销毁。

void test()
{
   class sample{ }obj;
}

是完全有效的代码。

您也可以将其定义为类中的嵌套类型。在这种情况下,obj 将是该类的成员变量。它将随着包含类的对象的构造和销毁而被创建和销毁。

struct Foo
{
   class sample{ }obj;
};

另外(感谢@sjdalessandro 指出),如果在库中使用全局范围定义对象并且动态加载该库,则在加载库时创建对象(这不一定当程序启动时)并在卸载库时销毁(不一定在程序退出时)。

【讨论】:

  • 此外,如果对象是在库中以全局范围定义的,并且该库是动态加载的;然后在卸载库时销毁对象;这不一定是程序退出时。
  • @sjdalesandro,说得好。如果您不介意,我会将其添加到答案中。
  • 一点也不。做我的客人吧。
【解决方案2】:

它将具有上述范围声明的范围。 Try it.

例子:

#include <iostream>

namespace A {
    namespace B {
        struct C {
            struct D {
                int e;  // variable e lifetime is same with struct D lifetime. Available only within struct D initialized
                D() : e(42) {}
            } d;    // variable d lifetime is samw with C struct lifetime. Available only within struct C initialized
            C() {}
            C(int v) {
                d.e = v;
            }
        } c;    // variable c lifetime is entire program. Scope is A::B::c
    }
}

int main() {
    // access variable with program lifetime in A::B namespace
    std::cout << A::B::c.d.e << std::endl;

    // create local variable of type A::B::C and access its fields
    A::B::C myc(55);
    std::cout << myc.d.e << std::endl;

    // declare struct F within main function scope and access its fields
    struct F {
        int g;
        F(int v) : g(v) { }
    } f (100);
    std::cout << f.g << std::endl;

    return 0;
}

【讨论】:

    【解决方案3】:

    如果类在全局范围内定义,它将具有全局范围。在这种情况下,对象obj 将在应用程序启动期间被初始化,并在应用程序退出期间被销毁。否则,范围将仅限于实际定义类的位置。

    很容易观察到这种行为。请参阅下面的示例全局类定义。

    class sample
    {
    public:
        sample()
        {
    
        }   
        ~sample()
        {
        }
    }Obj;
    

    在 Constructor 和 Destructor 中都设置一个断点,然后查看调用两者的点。您可以在应用程序启动期间观察调用构造函数时的调用堆栈,如下所示。

    App.exe!sample::sample() Line 582   C++
    App.exe!`dynamic initializer for 'Obj''() Line 589  C++
    msvcr110d.dll!_initterm(void (void) * * pfbegin, void (void) * * pfend) Line 889    C
    App.exe!__tmainCRTStartup() Line 460    C
    App.exe!wmainCRTStartup() Line 377  C
    

    从上面的调用堆栈可以看出,sample 类的构造函数是作为 C++ 运行时库初始化的一部分被调用的,该初始化发生在应用程序启动期间。类 Destructor 也是如此,即它将在应用程序退出期间被调用。

    正如@sjdalessandro 所指出的,如果对象被定义在一个中,并且这个库是动态加载的,那么当库被卸载时,该对象就会被销毁;这不一定是程序退出时。

    【讨论】:

    • 这不一定正确;对象的范围取决于它的声明位置。如果Obj 在一个类中声明,它将由封闭类的实例化作用域,如果它在函数中声明,它将具有函数作用域。
    • 您还应该删除错误的 cmets。
    • 另外,如果对象被定义在一个库中,并且这个库是动态加载的,那么在卸载库时该对象就被销毁了;这不一定是程序退出时。
    • @EJP 我没听懂你。代码中没有 cmets。如您所知,另一个是调试器中显示的调用堆栈。
    • 我没有说“在代码中”。您在问题下没有一个而是两个错误的 cmets,它们重复了您最初在此答案中发布的错误信息。
    猜你喜欢
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 1970-01-01
    • 2018-02-13
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多