创建一个子类的多个实例,只有一个超类的实例
Creating multiple instances of a subclass, with only one instance of a superclass
我正在尝试实现享元模式,但我不太确定继承是如何工作的,所以我不太确定这种模式是如何工作的。
假设我有一个超级class,它包含所有 "heavy" 信息——纹理等,内在信息(永远不会改变的信息)。
class Block{
public: //for the sake of the example
Texture tex;
etc.
};
我的灯 class 的数据会发生变化:
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_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
实现实现。使用不同的工厂进行测试等,您可以根据需要切换下面的实现来进行测试。
您的 "per instance" 状态不是从 Block
继承的,而是按值保持独立的,而 Block
实例由 BlockFactory
拥有且仅通过引用/指针传递。
C++ 对象模型确保每个子实例class(即派生class)contains也是它自己的实例它的所有超级classes(即base classes)。
在您的情况下,这意味着每个 Block_light
对象都将具有所有 Block_light
属性,以及具有所有 Block
的 Block
子对象特性。换句话说,Block_light
一点都不轻
如果你想分享一个共同的状态:
- 您可以 use composition instead of inheritance:
Block_light
不会继承自 Block
,而是引用一个共享的 Block
对象。
- 如果您仍然需要能够互换使用
Block
和 Block_light
,您可以使两者都继承自一个公共接口(即只有虚函数而没有状态的 clss)IBlock
.
- 您也可以使用 flyweight pattern,其中部分状态(共享或非共享)被“外部化”为外部状态。这种模式的特殊性在于,外部状态的引用由调用者提供给classes函数,因此享元对象不需要存储指向这种状态的指针。
第一个选项如下所示:
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。但是,在选择此模式之前请三思,因为提供共享上下文可能具有挑战性且容易出错。
我正在尝试实现享元模式,但我不太确定继承是如何工作的,所以我不太确定这种模式是如何工作的。
假设我有一个超级class,它包含所有 "heavy" 信息——纹理等,内在信息(永远不会改变的信息)。
class Block{
public: //for the sake of the example
Texture tex;
etc.
};
我的灯 class 的数据会发生变化:
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_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
实现实现。使用不同的工厂进行测试等,您可以根据需要切换下面的实现来进行测试。
您的 "per instance" 状态不是从 Block
继承的,而是按值保持独立的,而 Block
实例由 BlockFactory
拥有且仅通过引用/指针传递。
C++ 对象模型确保每个子实例class(即派生class)contains也是它自己的实例它的所有超级classes(即base classes)。
在您的情况下,这意味着每个 Block_light
对象都将具有所有 Block_light
属性,以及具有所有 Block
的 Block
子对象特性。换句话说,Block_light
一点都不轻
如果你想分享一个共同的状态:
- 您可以 use composition instead of inheritance:
Block_light
不会继承自Block
,而是引用一个共享的Block
对象。 - 如果您仍然需要能够互换使用
Block
和Block_light
,您可以使两者都继承自一个公共接口(即只有虚函数而没有状态的 clss)IBlock
. - 您也可以使用 flyweight pattern,其中部分状态(共享或非共享)被“外部化”为外部状态。这种模式的特殊性在于,外部状态的引用由调用者提供给classes函数,因此享元对象不需要存储指向这种状态的指针。
第一个选项如下所示:
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。但是,在选择此模式之前请三思,因为提供共享上下文可能具有挑战性且容易出错。