在由其他父级实现的 C++ 接口中使用抽象虚函数 class
Using abstract virtual function in c++ Interface implemented by other parent class
我正在尝试定义一个接口(抽象 class),它将“自动”将创建的任何实例注册到全局映射,其中键是 uint8_t
,值是指向的指针界面 class.
所有将实现此接口的 class 都已经具有使用 getId()
方法检索唯一 ID 的方法。我尝试了以下方法,但是当我在接口的 c'tor 和 d'tor 中使用 (then) 抽象方法 getId() 时,它会收到警告,我可以理解。但是当我尝试创建 LightZoneImpl 的实例时出现错误,因为它没有 getId() 的实现。
我做错了什么?
注意:这是真实事物的简化示例,真实事物中涉及许多其他 classes 等。
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
虚拟调度在构造完成之前不会开始使用派生-class 函数覆盖:当时您希望 getId()
将使用派生-class 覆盖,只是部分抽象的 base class 已经构造 - 派生对象不存在以调用其函数。
您可以派生 classes 或工厂函数提供 id 并在地图上操作。
Elaboration/example 根据 Bascy 的要求...
你可以认为这里涉及的对象是一个LightZoneImpl
对象,其中嵌入了一个ILightZone
基础class对象。要构建 LightZoneImpl
,必须首先构建基础 class... 而当发生这种情况时,派生的 class 对象不存在或不具有不变量(关于状态的保证) derived-class 构造函数已设置,因此调用虚函数的任何派生 class 覆盖为时过早。出于这个原因,C++ 标准说基本 class 虚函数实现应该继续被调用,但是如果它们不可用,因为函数是纯虚函数,你的程序将终止。
要解决此问题,您可以按照 Mooing Duck 在其评论中的建议进行操作,并让派生的 class 指定基 class 保存的 ID。这可能是最好的。您还可以有一个创建光区的工厂函数,让 derived-class 构造函数将它传递给基础 class for storage/use:
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_++);
}
然后您希望将光区构造函数设为私有并将工厂设为 friend
。
我正在尝试定义一个接口(抽象 class),它将“自动”将创建的任何实例注册到全局映射,其中键是 uint8_t
,值是指向的指针界面 class.
所有将实现此接口的 class 都已经具有使用 getId()
方法检索唯一 ID 的方法。我尝试了以下方法,但是当我在接口的 c'tor 和 d'tor 中使用 (then) 抽象方法 getId() 时,它会收到警告,我可以理解。但是当我尝试创建 LightZoneImpl 的实例时出现错误,因为它没有 getId() 的实现。
我做错了什么?
注意:这是真实事物的简化示例,真实事物中涉及许多其他 classes 等。
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
虚拟调度在构造完成之前不会开始使用派生-class 函数覆盖:当时您希望 getId()
将使用派生-class 覆盖,只是部分抽象的 base class 已经构造 - 派生对象不存在以调用其函数。
您可以派生 classes 或工厂函数提供 id 并在地图上操作。
Elaboration/example 根据 Bascy 的要求...
你可以认为这里涉及的对象是一个LightZoneImpl
对象,其中嵌入了一个ILightZone
基础class对象。要构建 LightZoneImpl
,必须首先构建基础 class... 而当发生这种情况时,派生的 class 对象不存在或不具有不变量(关于状态的保证) derived-class 构造函数已设置,因此调用虚函数的任何派生 class 覆盖为时过早。出于这个原因,C++ 标准说基本 class 虚函数实现应该继续被调用,但是如果它们不可用,因为函数是纯虚函数,你的程序将终止。
要解决此问题,您可以按照 Mooing Duck 在其评论中的建议进行操作,并让派生的 class 指定基 class 保存的 ID。这可能是最好的。您还可以有一个创建光区的工厂函数,让 derived-class 构造函数将它传递给基础 class for storage/use:
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_++);
}
然后您希望将光区构造函数设为私有并将工厂设为 friend
。