【问题标题】:Using abstract virtual function in c++ Interface implemented by other parent class在其他父类实现的C++接口中使用抽象虚函数
【发布时间】:2021-07-13 22:21:17
【问题描述】:

我试图定义一个接口(抽象类),它将“自动”注册任何创建到全局映射的实例,其中键是 uint8_t,值是指向接口类的指针。

将实现此接口的所有类都已具有使用getId() 方法检索唯一ID 的方法。我尝试了以下方法,但是当我在接口的 c'tor 和 d'tor 中使用(当时)抽象方法 getId() 时,它会收到警告,我可以理解。但是当我尝试创建 LightZoneImpl 的实例时出现错误,因为它没有实现 getId()。

我在这里做错了什么?

注意:这是真实事物的简化示例,真实事物中涉及许多其他类等。

class ILightZone; // forward
typedef std::map<uint8_t, ILightZone*> LightZoneMap;
extern LightZoneMap lightZoneMap;
      
/**
 * @brief Interface defining a Lightzone operating node, automatically (de-)registered in the lightZoneMap
 * 
 */
class ILightZone {
public:
  ILightZone() {
    lightZoneMap[getId()] = this; // <== warning: pure virtual function called from c'tor
  }

  virtual ~ILightZone() {
    lightZoneMap.erase(getId()); // <== warning: pure virtual function called from d'tor
  }
  virtual const uint8_t getId() const = 0;
  virtual void setLightOn() = 0;
  virtual void setLightOff() = 0;
  virtual bool isLightOn() = 0;
  virtual void setIntensity(const uint8_t percentage) = 0;
  virtual uint8_t getIntensity() = 0;
};

class BaseNode {
public:
  BaseNode(uint8_t nodeId) : nodeId(nodeId) {};
  virtual ~BaseNode() {};
  virtual const uint8_t getid() const { return nodeId; };
private:
  uint8_t nodeId;
};

class LightZoneImpl : public ILightZone, public BaseNode {
public:
  LightZoneImpl() {};
  virtual ~LightZoneImpl() {};

  using BaseNode::getId;

  void setLightOn() override { /* implementation */};
  void setLightOff() override { /* implementation */};
  bool isLightOn() override { return false; };
  void setIntensity(const uint8_t percentage) override { /* implementation */ };
  uint8_t getIntensity() override { return 0; };
}

LightZoneImpl zone{12}; // <= error: cannot declare variable 'zone' to be of abstract type LightZoneImpl

注意2:修改下面的示例以显示下面建议的解决方案

class ILightZone; // forward
typedef std::map<uint8_t, ILightZone*> LightZoneMap;
extern LightZoneMap lightZoneMap;
      
/**
 * @brief Interface defining a Lightzone operating node, automatically (de-)registered in the lightZoneMap
 * 
 */
class ILightZone {
public:
  ILightZone(uint8_t nodeId) : nodeId(nodeId) {
    lightZoneMap[nodeId] = this; 
  }

  virtual ~ILightZone() {
    lightZoneMap.erase(nodeId); 
  }
  const uint8_t getId() const { return nodeId; };
  virtual void setLightOn() = 0;
  virtual void setLightOff() = 0;
  virtual bool isLightOn() = 0;
  virtual void setIntensity(const uint8_t percentage) = 0;
  virtual uint8_t getIntensity() = 0;
private:
  uint8_t nodeId;
};

class BaseNode {
public:
  BaseNode(uint8_t nodeId) : nodeId(nodeId) {};
  virtual ~BaseNode() {};
  virtual const uint8_t getid() const { return nodeId; };
private:
  uint8_t nodeId;
};

class LightZoneImpl : public ILightZone, public BaseNode {
public:
  LightZoneImpl(uint8_t nodeId) : ILightZone(nodeId), BaseNode(nodeId) {};
  virtual ~LightZoneImpl() {};

  using BaseNode::getId;

  void setLightOn() override { /* implementation */};
  void setLightOff() override { /* implementation */};
  bool isLightOn() override { return false; };
  void setIntensity(const uint8_t percentage) override { /* implementation */ };
  uint8_t getIntensity() override { return 0; };
}

LightZoneImpl zone{12}; // <= error: cannot declare variable 'zone' to be of abstract type LightZoneImpl

【问题讨论】:

  • 您未能声明BaseNode::getId

标签: c++ avr-gcc


【解决方案1】:

在构造完成之前,虚拟调度不会开始使用派生类函数覆盖:在您希望 getId() 使用派生类覆盖的时候,只有抽象的一部分 base 类已构建 - 派生对象不存在以调用其函数。

您可以让派生类或工厂函数提供 id 并在地图上进行操作。

Bascy 要求的详细说明/示例...

您可以将这里涉及的对象视为一个LightZoneImpl 对象,其中嵌入了一个ILightZone 基类对象。要构造LightZoneImpl,必须首先构造基类......而在这种情况下,派生类对象不存在或不具有派生类构造函数设置的不变量(关于状态的保证),所以它是调用虚函数的任何派生类覆盖还为时过早。出于这个原因,C++ 标准规定应继续调用基类虚函数实现,但如果它们因为函数是纯虚函数而不可用,您的程序将终止。

要解决此问题,您可以按照 Mooing Duck 在评论中的建议执行操作,并让派生类指定基类保存的 id。这可能是最好的。您还可以有一个工厂函数来创建光区,让派生类构造函数将其传递给基类以供存储/使用:

std::unique_ptr<LightZoneImpl> lz_factory() {
    static int id_ = 0;
    if (id_ > 255)
        throw std::runtime_error("too many lightzones");
    return std::make_unique<LightZoneImpl>(id_++);
}

然后您希望将 light zone 构造函数设为私有并将工厂设为 friend

【讨论】:

  • 我不确定我是否理解你在这里想说的......你能举个例子来说明如何解决它吗?
  • 我假设最后一句意味着派生类应该将 id 传递给接口的 constructor 应该将其保存到成员中,而不是使用virtual 方法?
  • 我修改了原帖中的代码,将nodeId作为c'tor参数添加到界面中。但这仍然给我一个无法声明抽象类型的错误
  • 在原帖中再次修改了代码,现在可以使用了!谢谢@Tony-Delroy
  • 哇,感谢您恢复对原始帖子的更改!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 2012-07-17
  • 2021-04-03
  • 2013-01-30
  • 1970-01-01
  • 2015-04-02
  • 1970-01-01
相关资源
最近更新 更多