创建一个子类的多个实例,只有一个超类的实例
Posted
技术标签:
【中文标题】创建一个子类的多个实例,只有一个超类的实例【英文标题】:Creating multiple instances of a subclass, with only one instance of a superclass 【发布时间】:2020-04-20 11:33:02 【问题描述】:我正在尝试实现享元模式,但我不太确定继承是如何工作的,所以我不太确定这种模式是如何工作的。
假设我有一个超类,它包含所有“重”信息 - 纹理等,内在信息(永远不会改变的信息)。
class Block
public: //for the sake of the example
Texture tex;
etc.
;
我的灯光类的数据会发生变化:
class Block_light : public Block
public:
int posX, posY, posZ;
int color;
etc.
;
那么,如果我在 main 中创建 n Block_light
,我会同时创建 n Block
还是将它们都绑定到一个实例?
int main()
std::vector<Block_light> blocks(n);
return 0;
;
我创建了 n Block_light
和 1 Block
还是 n Block_light
和 n Block
?如果是后者,我怎样才能让它只使用一个实例?
【问题讨论】:
在您的示例中,Block
和 Block_light
之间没有关系。如果Block_light
继承自Block
,您将创建n
。
是的,一个错字。那么,我该如何让它只创建 1 个区块?
由于不能继承。每个Block_light
对象也是一个 Block
对象。
不要让它继承。继承将使子类足够大以包含超类的对象。在Block_light
中可能应该有一个引用/指针/shared_ptr
到Block
。什么最合适在很大程度上取决于您的用例。 (注意静态变量,由于隐式线程检查,它们会产生运行时开销)
【参考方案1】:
C++ 对象模型确保子类(即派生 类)contains 的每个实例也是其所有超类(即基 类)的自己的实例。
在您的情况下,这意味着每个Block_light
对象都将具有所有Block_light
属性,以及具有所有Block
属性的Block
子对象。换句话说,Block_light
一点也不轻。
如果你想共享一个共同的状态:
您可以 use composition instead of inheritance:Block_light
不会继承自 Block
,而是引用共享的 Block
对象。
如果您仍然需要能够交替使用 Block
和 Block_light
,则可以使两者都继承自一个通用接口(即只有虚函数而没有状态的 cls)IBlock
。
您也可以使用flyweight pattern,其中部分状态(共享与否)被“外部化”为外部状态。这种模式的特殊之处在于,外部状态的引用由调用者提供给类函数,因此享元对象不需要存储指向这种状态的指针。
第一个选项如下所示:
class Block ...;
class Block_light // no inheritance
shared_ptr<Block> b; // but (private) composition
...
;
第二个选项是:
class IBlock // no member variables
public:
virtual Texture get_texture()=0;
virtual ~IBlock();
;
class Block : public IBlock // sharable state
Texture tex;
public:
Texture get_texture() override return tex;
;
class Block_light : public IBlock
shared_ptr<IBlock> b; // or IBlock or Block depending on the needs.
public:
Block_light (shared_ptr<IBlock>i) : b(i)
Texture get_texture() override return b->get_texture();
;
int main() // just a perfectible quick example
auto myb=make_shared<Block>();
Block_light b1(myb);
b1.get_texture();
最后一个会这样使用:
int main() // just a perfectible quick example
Block myb; // shared part;
Block_light bl;
bl.get_texture(myb);
我不会详细介绍享元实现的细节,但你有an example here。但是,在选择此模式之前请三思,因为提供共享上下文可能具有挑战性且容易出错。
【讨论】:
FWIW 第一个链接(“包含”)需要身份验证才能访问。我不再有费米的信用了。 @DaveNewton 感谢您通知我此更改。这篇文章有一些有趣的模式来可视化这些原则。我已更新链接以使用回溯机器指向我回答时的文章。【参考方案2】:您实现的不是轻量级模式。没有 Block_light
这样的东西,而是 Block
将是由 Block1
之类的东西提供的实现的纯接口,看起来就像你的 Block
定义的样子。
例如:
#include <string>
#include <unordered_map>
#include <memory>
class BlockState
public:
int posX, posY, posZ;
int color;
;
struct Texture
Texture(const std::string &);
char data[8196];
;
class Block
public:
Block(std::string) ;
virtual void render(const BlockState &state) const = 0;
;
class Block1 final : public Block
public:
Block1(std::string name) : Block(name), tex(name) ;
void render(const BlockState &state) const override;
private:
const Texture tex;
;
class BlockFactory final
public:
std::shared_ptr<const Block> getBlock(const std::string &key)
auto it = dict.find(key);
if(it != dict.end()) return it->second;
auto it2 = dict.try_emplace(key, std::make_shared<Block1>(key));
return it2.first->second;
private:
std::unordered_map<std::string, std::shared_ptr<const Block>> dict;
;
void render ()
static BlockFactory factory;
BlockState a, b;
factory.getBlock("brick")->render(a);
factory.getBlock("brick")->render(b);
BlockFactory
正在为您管理Block
的实例,由不可见的Block1
实现实现。使用不同的工厂进行测试等,您可以根据需要切换下面的实现以进行测试。
您的“每个实例”状态不是从 Block
继承的,而是按值单独保存,其中 Block
实例由 BlockFactory
拥有,并且仅通过引用/指针传递。
【讨论】:
以上是关于创建一个子类的多个实例,只有一个超类的实例的主要内容,如果未能解决你的问题,请参考以下文章
当子类被实例化时,超类的私有成员是不是也被实例化? [复制]