出现错误“在 Juce Framework 中分配抽象 class 类型的对象?

Getting an error "allocating an object of abstract class type in Juce Framework?

我是 juce 的新手,遇到类似 "allocating an object of abstract class type 'CreateAccount'" 的错误,我是 c++ 的初学者。我正在尝试从 juce 的主要 window 调用 class 并得到像 this.Below 这样的错误是我的代码

Main.cpp 文件

#include <JuceHeader.h>
#include "CreateAccount.h"

//==============================================================================
class TestApplication  : public JUCEApplication
{
public:
    //==============================================================================
    TestApplication() {
    }

    const String getApplicationName() override  {
        return "Test";
    }
    const String getApplicationVersion() override    { return "1.0.0"; }

    void initialise (const String&) override         {
        mainWindow.reset (new MainWindow ("Test", new CreateAccount(), *this));
     //  splash = new SplashScreen("Welcome to Screen!",ImageFileFormat::loadFrom(File("/Resources/bell.png")),
                             //    true);

       // splash->deleteAfterDelay(RelativeTime::seconds(5), false);
    }

       void shutdown() override {
        mainWindow = nullptr;
    }

private:
    class MainWindow    : public DocumentWindow
    {
    public:
        MainWindow (const String& name, Component* c, JUCEApplication& a)
                : DocumentWindow (name, Desktop::getInstance().getDefaultLookAndFeel()
                                          .findColour (ResizableWindow::backgroundColourId),
                                  DocumentWindow::allButtons),
                  app (a)
        {
            setUsingNativeTitleBar (true);
            setContentOwned (c, true);

#if JUCE_ANDROID || JUCE_IOS
            setFullScreen (true);
#else
            setResizable (true, false);
            setResizeLimits (300, 250, 10000, 10000);
            centreWithSize (getWidth(), getHeight());
#endif

            setVisible (true);
        }

        void closeButtonPressed() override
        {
            app.systemRequestedQuit();
        }


    private:
        JUCEApplication& app;


        //==============================================================================
        JUCE_DECLARE_NON_COPYABLE (MainWindow)
    };

    std::unique_ptr<MainWindow> mainWindow;


};

//==============================================================================
START_JUCE_APPLICATION (TestApplication)

CreateAccount.h 文件

#include <JuceHeader.h>

//new page for create profile
class CreateAccount : public Component,
                        public Button::Listener

        {
public:
    CreateAccount() {

        addAndMakeVisible(lblloginwithfb);
        lblloginwithfb.setFont(Font(18.0f));
        lblloginwithfb.setText("Login with Facebook", dontSendNotification);
        lblloginwithfb.setColour(Label::textColourId, Colours::white);

        //ready to play button
        btncreteprofile.setButtonText("Create your profile");
        btncreteprofile.setColour(TextButton::textColourOffId, Colours::white);
        btncreteprofile.setColour(TextButton::textColourOnId, Colours::white);
        btncreteprofile.setColour(TextButton::buttonColourId,  Colour::fromRGB(235,135,15));
        btncreteprofile.setColour(TextButton::buttonOnColourId, Colour::fromRGB(235,135,15));
        addAndMakeVisible(btncreteprofile);

    }
    //==============================================================================
    void paint(Graphics &g) override {
        g.fillAll(Colours::black);
    }

    void resized() override {

        /*Rectangle<int> bounds = getLocalBounds();
        FlexBox flexBox;

        flexBox.flexDirection = FlexBox::Direction::column;
        flexBox.flexWrap = FlexBox::Wrap ::noWrap;
        flexBox.alignContent = FlexBox::AlignContent::center;

        Array<FlexItem> itemarray;
        itemarray.add(FlexItem(80,50,btncreteprofile));
        itemarray.add(FlexItem(getWidth(),50,lblloginwithfb));


        flexBox.items = itemarray;
        flexBox.performLayout(bounds.removeFromBottom(200));*/
        Rectangle<int> bounds = getLocalBounds();
        const int insetX = getWidth() / 4;
        const int insetY = getHeight() / 4;


        btncreteprofile.setBounds(insetX,bounds.getCentre().y+80,getWidth() - insetX * 2, 50);
        lblloginwithfb.setBounds(insetX,insetY,getWidth() - insetX * 2,getHeight());

    }

private:
    //==============================================================================
    // Your private member variables go here...
    Label lblloginwithfb;
    TextButton btncreteprofile;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CreateAccount)
};

当我尝试在 createaccount 文件中添加监听器时出现错误,请帮助我。

您需要在 Button::Listener 中实现所有纯虚方法,以使您的 class 不抽象。 Button::Listener包含这两个方法

virtual void    buttonClicked (Button *)=0
virtual void    buttonStateChanged (Button *)

第二种方法不是纯虚的,所以你不必实现它。

所以给你的 class

添加一个方法
virtual void    buttonClicked (Button *) override {}

并添加点击按钮时所需的代码。

根据您使用的 C++ 版本,override 可能不会被编译器接受。在这种情况下,只需省略它。它用于允许 编译器针​​对覆盖的方法发出 warnings/errors。

在JUCE中,大多数UI相关的东西都是作为Component的子class实现的。

一些子classes 具有内容组件的概念:所有子classes ResizableWindow,它添加了 setContentOwned()setContentNonOwned() 方法,由 ResizableWindow 的所有子 class 继承(特别是 DocumentWindow).

要定义DocumentWindow的内容,你问题中的示例代码 在 MainWindow 构造函数中使用 setContentOwned() initialise()TestApplication.

要实现导航,您可以执行以下操作(还有其他方法):

1。在 MainWindow 中实现导航,这样您就可以将导航逻辑集中在一个中心位置

2。为您的每个 "Pages"(让我们称一步为页面。)一个指向 MainWindow 的指针。对于 CreateAccount 那将是

CreateAccount.h 文件:

void setMainWindow(MainWindow * _mainWindow)
{
  mainWindow = _mainWindow;
}

3。在 CreateAccount class:

的某处也添加一个实例变量
MainWindow * mainWindow;

并更新构造函数:

CreateAccount()
  : mainWindow(nullptr)
{
  // ...
}

4。更改创建代码

替换

mainWindow.reset (new MainWindow ("Test", new CreateAccount(), *this));

作者:

CreateAccount * ca = new CreateAccount();
MainWindow * mw = new MainWindow ("Test", ca, *this)
ca->setMainWindow(mw);
mainWindow.reset(mw);

5。在 MainWindow 中的一组自定义方法(需要 public)中实现您的导航,例如

public:
void createProfileClicked()
{
   // ...get entered data...
   // ...process entered data...
   // ...implement next navigation step...
   // (you would create or recycle the "Page" for the next step here)
   setContentOwned(
     // ...
   );
}

6。从 CreateAccount:

中的 buttonClicked() 事件处理程序调用该方法
mainWindow->createProfileClicked();

这只是实现您想要的功能的一种方式。这完全取决于您项目的复杂程度。


编辑:

我在这个答案中给出的建议引入了 classes 的循环依赖。

让我首先重申我认为你目前拥有的,从你的评论中推断出来的:

mainwindow.h

#include "splashpage.h"

class MainWindow : public DocumentWindow
{
public:
    // ...

    void alaramReadyBtnClicked()
    {
        setContentOwned(new SplashPage(), false);
    }

    // ...
};

splashpage.h:

#include "mainwindow.h"

class SplashPage : public Component, public Button::Listener
{
public:
    SplashPage()
        : mainWindow(nullptr)
    {}

    void setMainWindow(MainWindow * _mainWindow)
    { mainWindow = _mainWindow; }

    void buttonClicked (Button *) override
    {
        if (button == &tvbtnSomething) {
            mainWindow->splashSomethingClicked();
        }
    }

private:
    MainWindow * mainWindow;
};

MainWindow 和页面 class 之间存在声明的循环依赖。当编译器看到

mainWindow->splashSomethingClicked();

它需要看到 MainWindow 的声明,但在 mainwindow.h 为此,它需要 SplashPage 的声明:

void alaramReadyBtnClicked()
{
    setContentOwned(new SplashPage(), false);
}

首先,在头文件中使用 include guards 是个好习惯

例如mainwindow.h

// you need these include guards to prevent that the compiler
// sees this file a second time; it's a very good rule of thumb to always do this
// in you header files

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "splashpage.h"

class MainWindow : public DocumentWindow
{
    // ...
};

#endif // !defined(MAINWINDOW_H)

在 C++ 中存在编译器不需要知道完整声明的特定情况:如果您只使用指针或引用而不引用它们。然后,您可以使用 class 名称的前向声明。

但是您不能仅对头文件中的所有内容执行此操作。因为有一个"compilation unit".

的概念

要打破循环依赖,改变你的...Page.h:

#ifndef SPLASHPAGE_H
#define SPLASHPAGE_H

// don't include mainwindow.h here

class MainWindow; // forward declaration


class SplashPage : public Component, public Button::Listener
{
public:
    SplashPage()
        : mainWindow(nullptr)
    {}

    void setMainWindow(MainWindow * _mainWindow)
    {
        mainWindow = _mainWindow; // pointer copy does not need full declaration
    }

    // note: implementation not in this header file anymore
    void buttonClicked (Button *) override;

private:
    // ...
    MainWindow * mainWindow; // does not need full declaration
};

#endif // !defined(SPLASHPAGE_H)

splashpage.cpp:

#include "mainwindow.h"
#include "splashpage.h"

void SplashPage::buttonClicked (Button *) override
{
    if (button == &tvbtnSomething) {
        mainWindow->splashSomethingClicked();
    }
}

如果您使用 JUCE Projucer 工具,您可以轻松地添加成对的这些文件,这些文件应该已经包含了 include guards 内容。