出现错误“在 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 内容。
我是 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 内容。