首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当代码移到类中时,Arduino NeoPixel代码的行为异常

当代码移到类中时,Arduino NeoPixel代码的行为异常
EN

Stack Overflow用户
提问于 2021-06-18 06:34:42
回答 1查看 52关注 0票数 1

所以我在尝试划分我为Arduino/NeoPixel应用程序编写的一些测试代码时遇到了一个奇怪的问题。有两种场景:场景A,我在移动之前的代码,以及场景B,我在移动之后的代码。测试代码在场景A中按预期工作(一个红灯穿过我的8x8 led矩阵)。完全相同的代码,当移动到容器类(场景B)时,会导致奇怪的行为(随机颜色的LED斑点出现,但不移动)。简单地将功能从一个地方移到另一个地方似乎不会导致这些症状,所以我有点迷茫。

这里有一些图片。Scenario A Scenario B

我已经为下面两个不同的场景附加了代码。为了清楚起见,我删除了代码的部分,并包含了尚未被引用的部分。

当涉及到Arduino/C++时,我或多或少仍然是一个爱好者,所以也可以随意指出一些次要的东西。

场景A

Program.ino

代码语言:javascript
复制
#include <Arduino.h>
#include "Hardware.h"

Hardware* hardware = new Hardware();

void setup()
{
  hardware->Setup();
}

uint8_t i = 0;

void loop()
{
  auto screen = hardware->GetScreen();
  screen->Clear();
  screen->SetLedHSV(i++ % screen->Count(), 0, 255, 255);
  screen->Show();

  delay(100);
}

Hardware.h

代码语言:javascript
复制
#pragma once
#include "Screen.h"

class Hardware
{
private:
    Screen screen = Screen(8, 8, 14);

public:
    void Setup()
    {
        screen.Setup();
    }

    Screen* GetScreen() { return &screen; }
};

Screen.h

代码语言:javascript
复制
#pragma once
#include <Adafruit_NeoPixel.h>

class Screen
{
private:
    uint8_t width, height;
    uint8_t pin;

    Adafruit_NeoPixel pixels;

public:
    Screen(uint8_t width, uint8_t height, uint8_t pin) :
        width(width), height(height), pin(pin)
    {
        pixels = Adafruit_NeoPixel(width * height, pin, NEO_GRB + NEO_KHZ800);
    }

    void Setup()
    {
        pixels.begin();
        pixels.show();
        pixels.setBrightness(32);
    }

    uint16_t Count() { return width * height; }
    uint8_t GetWidth() { return width; }
    uint8_t GetHeight() { return height; }

    void Show()
    {
        pixels.show();
    }

    void Clear()
    {
        pixels.clear();
    }

    void SetLedHSV(uint16_t i, uint16_t h, uint8_t s, uint8_t v)
    {
        pixels.setPixelColor(i, pixels.ColorHSV(h, s, v));
    }

    void SetLedHSV(uint8_t x, uint8_t y, uint16_t h, uint8_t s, uint8_t v)
    {
        if (x < 0 || x >= width)
            return;
        if (y < 0 || y >= height)
            return;

        auto i = x + y * width;
        pixels.setPixelColor(i, pixels.ColorHSV(h, s, v));
    }
};

场景B

Program.ino

代码语言:javascript
复制
#include <Arduino.h>
#include "Hardware.h"
#include "TestApp.h"

unsigned long timestamp;
Hardware* hardware = new Hardware();
TestApp* app = new TestApp(hardware);

void setup()
{
  hardware->Setup();
}

void loop()
{
  app->Update();
  delay(100);
}

Hardware.h

同上。

Screen.h

同上。

TestApp.h

代码语言:javascript
复制
#pragma once
#include "Hardware.h"

class TestApp
{
    Hardware* hardware = 0;
    uint8_t i = 0;

public:
    TestApp(Hardware* hardware) : hardware(hardware) {}

    void Update()
    {
        auto screen = hardware->GetScreen();
        screen->Clear();
        screen->SetLedHSV(i++ % screen->Count(), 0, 255, 255);
        screen->Show();
    }
};
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-19 13:51:14

问题显然出在Screen构造函数中:

代码语言:javascript
复制
Screen(uint8_t width, uint8_t height, uint8_t pin) :
    width(width), height(height), pin(pin)
{
    pixels = Adafruit_NeoPixel(width * height, pin, NEO_GRB + NEO_KHZ800);
}

现在,它使用默认构造函数创建“空”像素,并在代码块中创建另一个实例(现在已正确初始化),并将其复制为像素。由于没有提供拷贝赋值运算符,因此使用默认的运算符,并且它生成像素存储的浅拷贝。在此之后,第二个实例超出作用域并删除已分配的指针。

现在你有了悬空指针,如果你分配了任何东西,它将被分配到与悬空指针所指向的相同的空间中。

这两个测试用例之间的不同之处在于,您在第二个程序中进行了另一个分配:

代码语言:javascript
复制
Hardware* hardware = new Hardware();  // dangling pointer is created here
TestApp* app = new TestApp(hardware); // <<-- allocated in the same space as that dangling pointer

无论如何,像素类成员应该在构造函数的初始化器列表中初始化(与其余值完全相同):

代码语言:javascript
复制
Screen(uint8_t width, uint8_t height, uint8_t pin) :
    width(width), height(height), pin(pin), pixels(width * height, pin, NEO_GRB + NEO_KHZ800)
{
    // btw: do you really have to store a "pin" value?
}

为什么你要这么多地使用指针?它可以是这样的(或者它也可以作为引用传递到应用程序中):

代码语言:javascript
复制
#include <Arduino.h>
#include "Hardware.h"
#include "TestApp.h"

unsigned long timestamp;
Hardware hardware;
TestApp app { &hardware };

void setup()
{
  hardware.Setup();
}

void loop()
{
  app.Update();
  delay(100);
}

顺便说一句:这是由Adafruit库引起的,它没有遵循“三条规则”/“五条规则”的指导方针。它根本不应该允许复制对象(因为深度复制在小型设备上可能更糟糕)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68027262

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档