为啥我不能在主函数之外定义一个类的对象(继承了另一个类)?

Posted

技术标签:

【中文标题】为啥我不能在主函数之外定义一个类的对象(继承了另一个类)?【英文标题】:Why can't I define a object of class(which inherited another class) outside the main function?为什么我不能在主函数之外定义一个类的对象(继承了另一个类)? 【发布时间】:2020-06-27 12:15:29 【问题描述】:

我目前正在使用 fltk 库处理 VS15。当我尝试在主函数之外创建我的类的对象(继承 Fl_Double_Window)时,程序崩溃了。

#include"FL/Fl.H"
#include"FL/Fl_Double_Window.h"
#include"FL/Fl_draw.h"
#include"FL/Fl_Slider.H"
#include"FL/Fl_Input.H"
#include"FL/Fl_Button.H"
#include"FL/Fl_Text_Display.H"
#include<string>

struct MyWindow :Fl_Double_Window 
    MyWindow(string s):Fl_Double_Window(10, 10, 500, 500, s.c_str())
        color(FL_BLACK);
        show();
    
;

MyWindow window("Special");

int main()

    return Fl::run();

但是,当我直接创建 Fl_Double_Window 类的对象时,一切正常(同样在主函数之外):

#include"FL/Fl.H"
#include"FL/Fl_Double_Window.h"
#include"FL/Fl_draw.h"
#include"FL/Fl_Slider.H"
#include"FL/Fl_Input.H"
#include"FL/Fl_Button.H"
#include"FL/Fl_Text_Display.H"
#include<string>

string name = "Special";
Fl_Double_Window window(10, 10, 500, 500, name.c_str());

int main()

    window.color(FL_BLACK);
    window.show();
    return Fl::run();

我下载代码的那个人使用 C++11 在 Ubuntu 上运行代码,该程序在这两种情况下都有效。 我很困惑,我真的无法弄清楚问题所在。

【问题讨论】:

一个临时的字符指针,听起来像 UB(未定义的行为) 如果你不明白 @JVApen 在 MyWindow(string s):Fl_Double_Window(10, 10, 500, 500, s.c_str())... 中谈论 s.c_str() 因为根据定义 s 是暂时的所以你不能假设它的内部持续时间c_str()返回的指针 你为什么在 Fl_Double_Window 中使用char*而不是string @bruno 我不知道,这正是 FLTK 库的创建者决定的。 @Xeron 如果您不能使用文字字符串代替 std::string 来命名您的窗口,则重复 c_str() 的结果,例如strdup(xxx.c_str()) ... 即使是 lib 获取指针为 const 所以永远不会释放它,很遗憾有一个使用 char * 的 C++ lib ... 【参考方案1】:

您正在崩溃,因为您在构造函数中放置了 show(如 @bruno 所述)。如果您从构造函数中取出 show 并将其放入 main 中,您将不会看到您看到的崩溃,但由于@Sam Varshavchik 提到的原因,标题将不正确。

struct MyWindow :Fl_Double_Window 
    MyWindow(const std::string& s)
   :    Fl_Double_Window(10, 10, 500, 500) // 1) Do not set the title here
    ,   caption(s)  // 2) Take a copy of the title
    
        // 3) Set the title here
        this->copy_label(caption.c_str());
        color(FL_BLACK);
        // 4) Remove show from constructor
        // show();
    

    // 5) Declare caption - see Sam's solution
    std::string caption;
;

MyWindow window("Special");


int main()

    // 6) Move show here
    window.show();
    return Fl::run();

【讨论】:

我在 6 月 27 日对 OP 问题的评论中说,但 OP 没有反应。 我只是看到了 - 没有在其他 cmets 上展开。【参考方案2】:
MyWindow(string s)

构造函数与函数没有太大区别。 s 是构造函数的参数。一旦构造函数返回,s 就会被销毁。

                  :Fl_Double_Window(10, 10, 500, 500, s.c_str())

c_str() 返回一个指向s 内容的指针,并将它传递给超类的构造函数。然而,因为s 将被破坏,对该指针的任何进一步使用都将成为未定义的行为,并且可能会崩溃。这显然是正在发生的事情。

解决方案有点复杂。如果您仍然希望从FL_Double_Window 继承,这需要一个微妙的杂耍行为:

struct my_data 
    string s_name;
    my_data(string s) : s_name(s)
    
    
;

struct MyWindow :my_data, Fl_Double_Window 

    MyWindow(string s): my_data(s),
                        Fl_Double_Window(10, 10, 500, 500, this->s_name.c_str())
        color(FL_BLACK);
        show();
    
;

这会调整事物的构造和销毁顺序,因此只要 FL_DoubleWindow 存在,s_name 类成员就会继续存在。

还有一个(更)更简单的传递引用的选项,但是只要窗口对象存在,您就必须确保引用的std::string 存在。这种方法虽然有点麻烦,但也更防弹。

【讨论】:

感谢您的回答,但程序继续崩溃... 您的调试器显示崩溃的原因是什么? First_example.exe 中 0x777029CC (ole32.dll) 处未处理的异常:0xC0000005:访问冲突读取位置 0x00000004。 您的调试器显示的信息远不止这些。例如,调试器显示每个堆栈帧中所有变量的值,并让您确定崩溃的确切原因。当我编译并链接我的版本时(在添加了您必须拥有的缺少的using namespace std 之后),程序运行没有问题,并打开一个黑色窗口。您的崩溃一定是因为其他一些代码,而不是这个。您应该花一些时间学习如何使用调试器。这就是调试器的用途。了解如何使用调试器是每个 C++ 开发人员必备的技能。 是的,我只是从 Stroustrup 的《使用 C++ 的原则和实践》一书中学习 C++,所以我还没有接触到调试器。你确定你在主函数之外定义了一个 MyWindow 对象,因为当我在主函数内定义一个 MyWindow 对象时,即使程序也适用于我?

以上是关于为啥我不能在主函数之外定义一个类的对象(继承了另一个类)?的主要内容,如果未能解决你的问题,请参考以下文章

在JAVA中,子类继承父类,父类可以调用子类继承父类的方法,父类为啥不能调用子类自己定义的方法呢

Heremei C++

面向对象程序设计)

Java基础08 继承

OOP三大核心封装继承多态

为何需要虚函数 直接对象名加作用域来调用子类父类爷类的函数不就行了 为啥